def test_ensure_safety(self): """ Validates whether the ensure_safety call works as expected """ def _generate_mds_service_load_repr(_mds_service): """ Generates a load representing thing for a given mds_service """ masters, slaves = 0, 0 for _junction in _mds_service.vdisks: if _junction.is_master: masters += 1 else: slaves += 1 capacity = _mds_service.capacity if capacity == -1: capacity = 'infinite' _load, _ = MDSServiceController.get_mds_load(_mds_service) if _load == float('inf'): _load = 'infinite' else: _load = round(_load, 2) return [_mds_service.service.storagerouter.ip, _mds_service.service.ports[0], masters, slaves, capacity, _load] def _check_reality(_configs, _loads, _vdisks, _mds_services, test=True, display=False): """ Validates 'reality' with an expected config/load """ reality_configs = [] for _vdisk_id in _vdisks: reality_configs.append(_vdisks[_vdisk_id].info['metadata_backend_config']) if display is True: for c in reality_configs: print c if test is True: self.assertListEqual(reality_configs, _configs) reality_loads = [] for mds_id in _mds_services: reality_loads.append(_generate_mds_service_load_repr(_mds_services[mds_id])) if display is True: for l in reality_loads: print l if test is True: self.assertListEqual(reality_loads, _loads) PersistentFactory.get_client().set('ovs.storagedriver.mds.safety', 3) PersistentFactory.get_client().set('ovs.storagedriver.mds.maxload', 75) PersistentFactory.get_client().set('ovs.storagedriver.mds.tlogs', 100) vpools, storagerouters, storagedrivers, _, mds_services, service_type = self._build_service_structure( {'vpools': [1], 'storagerouters': [1, 2, 3, 4], 'storagedrivers': [(1, 1, 1), (2, 1, 2), (3, 1, 3), (4, 1, 4)], # (<id>, <vpool_id>, <sr_id>) 'mds_services': [(1, 1), (2, 2), (3, 3), (4, 4)]} # (<id>, <sd_id>) ) vdisks = {} start_id = 1 for mds_service in mds_services.itervalues(): vdisks.update(self._create_vdisks_for_mds_service(2, start_id, mds_service=mds_service)) start_id += 2 # Validate the start configuration which is simple, each disk has only its default local master configs = [[{'ip': '10.0.0.1', 'port': 1}], [{'ip': '10.0.0.1', 'port': 1}], [{'ip': '10.0.0.2', 'port': 2}], [{'ip': '10.0.0.2', 'port': 2}], [{'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.4', 'port': 4}]] loads = [['10.0.0.1', 1, 2, 0, 10, 20.0], ['10.0.0.2', 2, 2, 0, 10, 20.0], ['10.0.0.3', 3, 2, 0, 10, 20.0], ['10.0.0.4', 4, 2, 0, 10, 20.0]] _check_reality(configs, loads, vdisks, mds_services) # Validate first run. Each disk should now have sufficient nodes, since there are plenty of MDS services available configs = [[{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.2', 'port': 2}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.2', 'port': 2}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.1', 'port': 1}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.2', 'port': 2}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 2, 4, 10, 60.0], ['10.0.0.3', 3, 2, 4, 10, 60.0], ['10.0.0.4', 4, 2, 3, 10, 50.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validate whether this extra (unnecessary) run doesn't change anything, preventing reconfiguring over and # over again for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validating whether an overloaded node is correctly rebalanced mds_services[2].capacity = 2 mds_services[2].save() configs = [[{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.1', 'port': 1}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.1', 'port': 1}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 2, 0, 2, 100.0], ['10.0.0.3', 3, 2, 5, 10, 70.0], ['10.0.0.4', 4, 2, 5, 10, 70.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validate whether the overloaded services are still handled. In this case, causing a re-order of the slaves as # ordered in the model configs = [[{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.1', 'port': 1}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 2, 0, 2, 100.0], ['10.0.0.3', 3, 2, 5, 10, 70.0], ['10.0.0.4', 4, 2, 5, 10, 70.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Again, validating whether a subsequent run doesn't give unexpected changes for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # A MDS service will be added (next to the overloaded service), this should cause the expected to be rebalanced s_id = '{0}-5'.format(storagerouters[2].name) service = Service() service.name = s_id service.storagerouter = storagerouters[2] service.ports = [5] service.type = service_type service.save() mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = vpools[1] mds_service.save() mds_services[5] = mds_service configs = [[{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.2', 'port': 5}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.2', 'port': 5}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.1', 'port': 1}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.2', 'port': 5}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 2, 0, 2, 100.0], ['10.0.0.3', 3, 2, 5, 10, 70.0], ['10.0.0.4', 4, 2, 5, 10, 70.0], ['10.0.0.2', 5, 0, 3, 10, 30.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # If the tlogs are not caught up, nothing should be changed for vdisk_id in [3, 4]: StorageDriverClient.catch_up[vdisks[vdisk_id].volume_id] = 1000 for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # The next run, after tlogs are caught up, a master switch should be executed for vdisk_id in [3, 4]: StorageDriverClient.catch_up[vdisks[vdisk_id].volume_id] = 50 configs = [[{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 5}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.1', 'port': 1}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.2', 'port': 5}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 1, 0, 2, 50.0], ['10.0.0.3', 3, 2, 5, 10, 70.0], ['10.0.0.4', 4, 2, 5, 10, 70.0], ['10.0.0.2', 5, 1, 1, 10, 20.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validate whether a volume migration makes the master follow StorageDriverClient.vrouter_id[vdisks[1].volume_id] = storagedrivers[3].storagedriver_id configs = [[{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.2', 'port': 5}], [{'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 5}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.3', 'port': 3}], [{'ip': '10.0.0.2', 'port': 2}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}, {'ip': '10.0.0.4', 'port': 4}], [{'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.1', 'port': 1}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.2', 'port': 5}], [{'ip': '10.0.0.4', 'port': 4}, {'ip': '10.0.0.3', 'port': 3}, {'ip': '10.0.0.1', 'port': 1}]] loads = [['10.0.0.1', 1, 1, 6, 10, 70.0], ['10.0.0.2', 2, 1, 0, 2, 50.0], ['10.0.0.3', 3, 3, 4, 10, 70.0], ['10.0.0.4', 4, 2, 4, 10, 60.0], ['10.0.0.2', 5, 1, 2, 10, 30.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validates if a second run doesn't change anything for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services)
def test_clone(self): """ Test the clone functionality - Create a vDisk with name 'clone1' - Clone the vDisk and make some assertions - Attempt to clone again using same name and same devicename - Attempt to clone on Storage Router which is not linked to the vPool on which the original vDisk is hosted - Attempt to clone on Storage Driver without MDS service - Attempt to clone from snapshot which is not yet completely synced to backend - Attempt to delete the snapshot from which a clone was made - Clone the vDisk on another Storage Router - Clone another vDisk with name 'clone1' linked to another vPool """ structure = DalHelper.build_dal_structure({ 'vpools': [1, 2], 'storagerouters': [1, 2, 3], 'storagedrivers': [(1, 1, 1), (2, 2, 1)], # (<id>, <vpool_id>, <storagerouter_id>) 'mds_services': [(1, 1), (2, 2)] } # (<id>, <storagedriver_id>) ) vpools = structure['vpools'] mds_services = structure['mds_services'] service_type = structure['service_types']['MetadataServer'] storagedrivers = structure['storagedrivers'] storagerouters = structure['storagerouters'] self._roll_out_dtl_services(vpool=vpools[1], storagerouters=storagerouters) self._roll_out_dtl_services(vpool=vpools[2], storagerouters=storagerouters) # Basic clone scenario vdisk1 = VDisk( VDiskController.create_new( volume_name='vdisk_1', volume_size=1024**3, storagedriver_guid=storagedrivers[1].guid)) clone1_info = VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone1') vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 2, msg='Expected to find 2 vDisks') clones = VDiskList.get_by_parentsnapshot(vdisk1.snapshot_ids[0]) self.assertTrue(expr=len(clones) == 1, msg='Expected to find 1 vDisk with parent snapshot') self.assertTrue(expr=len(vdisk1.child_vdisks) == 1, msg='Expected to find 1 child vDisk') for expected_key in ['vdisk_guid', 'name', 'backingdevice']: self.assertTrue( expr=expected_key in clone1_info, msg='Expected to find key "{0}" in clone_info'.format( expected_key)) self.assertTrue(expr=clones[0].guid == clone1_info['vdisk_guid'], msg='Guids do not match') self.assertTrue(expr=clones[0].name == clone1_info['name'], msg='Names do not match') self.assertTrue( expr=clones[0].devicename == clone1_info['backingdevice'], msg='Device names do not match') # Attempt to clone again with same name with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone1') vdisks = VDiskList.get_vdisks() self.assertTrue( expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 1') # Attempt to clone again with a name which will have identical devicename with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone1%') vdisks = VDiskList.get_vdisks() self.assertTrue( expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 2') # Attempt to clone on Storage Router on which vPool is not extended with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2', storagerouter_guid=storagerouters[2].guid) vdisks = VDiskList.get_vdisks() self.assertTrue( expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 3') # Attempt to clone on non-existing Storage Driver storagedrivers[1].storagedriver_id = 'non-existing' storagedrivers[1].save() with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2') vdisks = VDiskList.get_vdisks() self.assertTrue( expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 4') storagedrivers[1].storagedriver_id = '1' storagedrivers[1].save() # Attempt to clone on Storage Driver without MDS service mds_services[1].service.storagerouter = storagerouters[3] mds_services[1].service.save() with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2') vdisks = VDiskList.get_vdisks() self.assertTrue( expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 5') mds_services[1].service.storagerouter = storagerouters[1] mds_services[1].service.save() # Attempt to clone by providing snapshot_id not synced to backend self.assertTrue(expr=len(vdisk1.snapshots) == 1, msg='Expected to find only 1 snapshot before cloning') self.assertTrue( expr=len(vdisk1.snapshot_ids) == 1, msg='Expected to find only 1 snapshot ID before cloning') metadata = { 'label': 'label1', 'timestamp': int(time.time()), 'is_sticky': False, 'in_backend': False, 'is_automatic': True, 'is_consistent': True } snapshot_id = VDiskController.create_snapshot(vdisk_guid=vdisk1.guid, metadata=metadata) self.assertTrue(expr=len(vdisk1.snapshots) == 2, msg='Expected to find 2 snapshots') self.assertTrue(expr=len(vdisk1.snapshot_ids) == 2, msg='Expected to find 2 snapshot IDs') with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2', snapshot_id=snapshot_id) vdisks = VDiskList.get_vdisks() self.assertTrue( expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 6') # Update backend synced flag and retry vdisk1.storagedriver_client._set_snapshot_in_backend( vdisk1.volume_id, snapshot_id, True) vdisk1.invalidate_dynamics(['snapshots', 'snapshot_ids']) VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2', snapshot_id=snapshot_id) vdisks = VDiskList.get_vdisks() vdisk1.invalidate_dynamics() self.assertTrue(expr=len(vdisks) == 3, msg='Expected to find 3 vDisks') self.assertTrue(expr=len(vdisk1.child_vdisks) == 2, msg='Expected to find 2 child vDisks') self.assertTrue( expr=len(vdisk1.snapshots) == 2, msg= 'Expected to find 2 snapshots after cloning from a specified snapshot' ) self.assertTrue( expr=len(vdisk1.snapshot_ids) == 2, msg= 'Expected to find 2 snapshot IDs after cloning from a specified snapshot' ) # Attempt to delete the snapshot that has clones with self.assertRaises(RuntimeError): VDiskController.delete_snapshot(vdisk_guid=vdisk1.guid, snapshot_id=snapshot_id) # Clone on specific Storage Router storagedriver = StorageDriver() storagedriver.vpool = vpools[1] storagedriver.storagerouter = storagerouters[2] storagedriver.name = '3' storagedriver.mountpoint = '/' storagedriver.cluster_ip = storagerouters[2].ip storagedriver.storage_ip = '127.0.0.1' storagedriver.storagedriver_id = '3' storagedriver.ports = { 'management': 1, 'xmlrpc': 2, 'dtl': 3, 'edge': 4 } storagedriver.save() s_id = '{0}-1'.format(storagedriver.storagerouter.name) service = Service() service.name = s_id service.storagerouter = storagedriver.storagerouter service.ports = [3] service.type = service_type service.save() mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = storagedriver.vpool mds_service.save() clone3 = VDisk( VDiskController.clone( vdisk_guid=vdisk1.guid, name='clone3', storagerouter_guid=storagerouters[2].guid)['vdisk_guid']) self.assertTrue( expr=clone3.storagerouter_guid == storagerouters[2].guid, msg='Incorrect Storage Router on which the clone is attached') # Clone vDisk with existing name on another vPool vdisk2 = VDisk( VDiskController.create_new( volume_name='vdisk_1', volume_size=1024**3, storagedriver_guid=storagedrivers[2].guid)) clone_vdisk2 = VDisk( VDiskController.clone(vdisk_guid=vdisk2.guid, name='clone1')['vdisk_guid']) self.assertTrue( expr=clone_vdisk2.vpool == vpools[2], msg='Cloned vDisk with name "clone1" was created on incorrect vPool' ) self.assertTrue(expr=len([ vdisk for vdisk in VDiskList.get_vdisks() if vdisk.name == 'clone1' ]) == 2, msg='Expected to find 2 vDisks with name "clone1"') # Attempt to clone without specifying snapshot and snapshot fails to sync to backend StorageRouterClient.synced = False vdisk2 = VDisk( VDiskController.create_new( volume_name='vdisk_2', volume_size=1024**3, storagedriver_guid=storagedrivers[1].guid)) with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk2.guid, name='clone4') vdisk2.invalidate_dynamics() self.assertTrue(expr=len(vdisk2.snapshots) == 0, msg='Expected to find 0 snapshots after clone failure') self.assertTrue( expr=len(vdisk2.snapshot_ids) == 0, msg='Expected to find 0 snapshot IDs after clone failure') self.assertTrue(expr=len(vdisk2.child_vdisks) == 0, msg='Expected to find 0 children after clone failure') StorageRouterClient.synced = True
def build_dal_structure(structure, previous_structure=None): """ Builds a model structure Example: structure = DalHelper.build_service_structure( {'vpools': [1], 'domains': [], 'storagerouters': [1], 'storagedrivers': [(1, 1, 1)], # (<id>, <vpool_id>, <storagerouter_id>) 'mds_services': [(1, 1)], # (<id>, <storagedriver_id>) 'storagerouter_domains': []} # (<id>, <storagerouter_id>, <domain_id>) ) """ Configuration.set(key=Configuration.EDITION_KEY, value=PackageFactory.EDITION_ENTERPRISE) if previous_structure is None: previous_structure = {} vdisks = previous_structure.get('vdisks', {}) vpools = previous_structure.get('vpools', {}) domains = previous_structure.get('domains', {}) services = previous_structure.get('services', {}) mds_services = previous_structure.get('mds_services', {}) storagerouters = previous_structure.get('storagerouters', {}) storagedrivers = previous_structure.get('storagedrivers', {}) storagerouter_domains = previous_structure.get('storagerouter_domains', {}) service_types = {} for service_type_name in ServiceType.SERVICE_TYPES.values(): service_type = ServiceTypeList.get_by_name(service_type_name) if service_type is None: service_type = ServiceType() service_type.name = service_type_name service_type.save() service_types[service_type_name] = service_type srclients = {} for domain_id in structure.get('domains', []): if domain_id not in domains: domain = Domain() domain.name = 'domain_{0}'.format(domain_id) domain.save() domains[domain_id] = domain for vpool_id in structure.get('vpools', []): if vpool_id not in vpools: vpool = VPool() vpool.name = str(vpool_id) vpool.status = 'RUNNING' vpool.metadata = {'backend': {}, 'caching_info': {}} vpool.metadata_store_bits = 5 vpool.save() vpools[vpool_id] = vpool else: vpool = vpools[vpool_id] srclients[vpool_id] = StorageRouterClient(vpool.guid, None) Configuration.set( '/ovs/vpools/{0}/mds_config|mds_tlogs'.format(vpool.guid), 100) Configuration.set( '/ovs/vpools/{0}/mds_config|mds_safety'.format(vpool.guid), 2) Configuration.set( '/ovs/vpools/{0}/mds_config|mds_maxload'.format(vpool.guid), 75) Configuration.set( '/ovs/vpools/{0}/proxies/scrub/generic_scrub'.format( vpool.guid), json.dumps({}, indent=4), raw=True) for sr_id in structure.get('storagerouters', []): if sr_id not in storagerouters: storagerouter = StorageRouter() storagerouter.name = str(sr_id) storagerouter.ip = '10.0.0.{0}'.format(sr_id) storagerouter.rdma_capable = False storagerouter.node_type = 'MASTER' storagerouter.machine_id = str(sr_id) storagerouter.save() storagerouters[sr_id] = storagerouter disk = Disk() disk.storagerouter = storagerouter disk.state = 'OK' disk.name = '/dev/uda' disk.size = 1 * 1024**4 disk.is_ssd = True disk.aliases = ['/dev/uda'] disk.save() partition = DiskPartition() partition.offset = 0 partition.size = disk.size partition.aliases = ['/dev/uda-1'] partition.state = 'OK' partition.mountpoint = '/tmp/unittest/sr_{0}/disk_1/partition_1'.format( sr_id) partition.disk = disk partition.roles = [ DiskPartition.ROLES.DB, DiskPartition.ROLES.SCRUB ] partition.save() else: storagerouter = storagerouters[sr_id] # noinspection PyProtectedMember System._machine_id[storagerouter.ip] = str(sr_id) mds_start = 10000 + 100 * (sr_id - 1) mds_end = 10000 + 100 * sr_id - 1 arakoon_start = 20000 + 100 * (sr_id - 1) storagedriver_start = 30000 + 100 * (sr_id - 1) storagedriver_end = 30000 + 100 * sr_id - 1 Configuration.initialize_host( host_id=sr_id, port_info={ 'mds': [mds_start, mds_end], 'arakoon': arakoon_start, 'storagedriver': [storagedriver_start, storagedriver_end] }) for sd_id, vpool_id, sr_id in structure.get('storagedrivers', ()): if sd_id not in storagedrivers: storagedriver = StorageDriver() storagedriver.vpool = vpools[vpool_id] storagedriver.storagerouter = storagerouters[sr_id] storagedriver.name = str(sd_id) storagedriver.mountpoint = '/' storagedriver.cluster_ip = storagerouters[sr_id].ip storagedriver.storage_ip = '10.0.1.{0}'.format(sr_id) storagedriver.storagedriver_id = str(sd_id) storagedriver.ports = { 'management': 1, 'xmlrpc': 2, 'dtl': 3, 'edge': 4 } storagedriver.save() storagedrivers[sd_id] = storagedriver DalHelper.set_vpool_storage_driver_configuration( vpool=vpools[vpool_id], storagedriver=storagedriver) for mds_id, sd_id in structure.get('mds_services', ()): if mds_id not in mds_services: sd = storagedrivers[sd_id] s_id = '{0}-{1}'.format(sd.storagerouter.name, mds_id) service = Service() service.name = s_id service.storagerouter = sd.storagerouter service.ports = [mds_id] service.type = service_types['MetadataServer'] service.save() services[s_id] = service mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = sd.vpool mds_service.save() mds_services[mds_id] = mds_service StorageDriverController.add_storagedriverpartition( sd, { 'size': None, 'role': DiskPartition.ROLES.DB, 'sub_role': StorageDriverPartition.SUBROLE.MDS, 'partition': sd.storagerouter.disks[0].partitions[0], 'mds_service': mds_service }) for vdisk_id, storage_driver_id, vpool_id, mds_id in structure.get( 'vdisks', ()): if vdisk_id not in vdisks: vpool = vpools[vpool_id] devicename = 'vdisk_{0}'.format(vdisk_id) mds_backend_config = DalHelper.generate_mds_metadata_backend_config( [] if mds_id is None else [mds_services[mds_id]]) volume_id = srclients[vpool_id].create_volume( devicename, mds_backend_config, 0, str(storage_driver_id)) vdisk = VDisk() vdisk.name = str(vdisk_id) vdisk.devicename = devicename vdisk.volume_id = volume_id vdisk.vpool = vpool vdisk.size = 0 vdisk.save() vdisk.reload_client('storagedriver') vdisks[vdisk_id] = vdisk for srd_id, sr_id, domain_id, backup in structure.get( 'storagerouter_domains', ()): if srd_id not in storagerouter_domains: sr_domain = StorageRouterDomain() sr_domain.backup = backup sr_domain.domain = domains[domain_id] sr_domain.storagerouter = storagerouters[sr_id] sr_domain.save() storagerouter_domains[srd_id] = sr_domain return { 'vdisks': vdisks, 'vpools': vpools, 'domains': domains, 'services': services, 'mds_services': mds_services, 'service_types': service_types, 'storagerouters': storagerouters, 'storagedrivers': storagedrivers, 'storagerouter_domains': storagerouter_domains }
def _build_service_structure(self, structure): """ Builds an MDS service structure """ vpools = {} storagerouters = {} storagedrivers = {} services = {} mds_services = {} service_type = ServiceType() service_type.name = 'MetadataServer' service_type.save() backend_type = BackendType() backend_type.name = 'BackendType' backend_type.code = 'BT' backend_type.save() pmachine = PMachine() pmachine.name = 'PMachine' pmachine.username = '******' pmachine.ip = '127.0.0.1' pmachine.hvtype = 'VMWARE' pmachine.save() for vpool_id in structure['vpools']: vpool = VPool() vpool.name = str(vpool_id) vpool.backend_type = backend_type vpool.save() vpools[vpool_id] = vpool for sr_id in structure['storagerouters']: storagerouter = StorageRouter() storagerouter.name = str(sr_id) storagerouter.ip = '10.0.0.{0}'.format(sr_id) storagerouter.pmachine = pmachine storagerouter.save() storagerouters[sr_id] = storagerouter for sd_info in structure['storagedrivers']: sd_id, vpool_id, sr_id = sd_info storagedriver = StorageDriver() storagedriver.vpool = vpools[vpool_id] storagedriver.storagerouter = storagerouters[sr_id] storagedriver.name = str(sd_id) storagedriver.mountpoint = '/' storagedriver.cluster_ip = storagerouters[sr_id].ip storagedriver.storage_ip = '127.0.0.1' storagedriver.storagedriver_id = str(sd_id) storagedriver.ports = [1, 2, 3] storagedriver.save() storagedrivers[sd_id] = storagedriver for mds_info in structure['mds_services']: mds_id, sd_id = mds_info sd = storagedrivers[sd_id] s_id = '{0}-{1}'.format(sd.storagerouter.name, mds_id) service = Service() service.name = s_id service.storagerouter = sd.storagerouter service.ports = [mds_id] service.type = service_type service.save() services[s_id] = service mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = sd.vpool mds_service.save() mds_services[mds_id] = mds_service return vpools, storagerouters, storagedrivers, services, mds_services, service_type
def test_ensure_safety(self): """ Validates whether the ensure_safety call works as expected """ def _generate_mds_service_load_repr(_mds_service): """ Generates a load representing thing for a given mds_service """ masters, slaves = 0, 0 for _junction in _mds_service.vdisks: if _junction.is_master: masters += 1 else: slaves += 1 capacity = _mds_service.capacity if capacity == -1: capacity = 'infinite' _load, _ = MDSServiceController.get_mds_load(_mds_service) if _load == float('inf'): _load = 'infinite' else: _load = round(_load, 2) return [ _mds_service.service.storagerouter.ip, _mds_service.service.ports[0], masters, slaves, capacity, _load ] def _check_reality(_configs, _loads, _vdisks, _mds_services, test=True, display=False): """ Validates 'reality' with an expected config/load """ reality_configs = [] for _vdisk_id in _vdisks: reality_configs.append( _vdisks[_vdisk_id].info['metadata_backend_config']) if display is True: for c in reality_configs: print c if test is True: self.assertListEqual(reality_configs, _configs) reality_loads = [] for mds_id in _mds_services: reality_loads.append( _generate_mds_service_load_repr(_mds_services[mds_id])) if display is True: for l in reality_loads: print l if test is True: self.assertListEqual(reality_loads, _loads) PersistentFactory.get_client().set('ovs.storagedriver.mds.safety', 3) PersistentFactory.get_client().set('ovs.storagedriver.mds.maxload', 75) PersistentFactory.get_client().set('ovs.storagedriver.mds.tlogs', 100) vpools, storagerouters, storagedrivers, _, mds_services, service_type = self._build_service_structure( { 'vpools': [1], 'storagerouters': [1, 2, 3, 4], 'storagedrivers': [(1, 1, 1), (2, 1, 2), (3, 1, 3), (4, 1, 4)], # (<id>, <vpool_id>, <sr_id>) 'mds_services': [(1, 1), (2, 2), (3, 3), (4, 4)] } # (<id>, <sd_id>) ) vdisks = {} start_id = 1 for mds_service in mds_services.itervalues(): vdisks.update( self._create_vdisks_for_mds_service(2, start_id, mds_service=mds_service)) start_id += 2 # Validate the start configuration which is simple, each disk has only its default local master configs = [[{ 'ip': '10.0.0.1', 'port': 1 }], [{ 'ip': '10.0.0.1', 'port': 1 }], [{ 'ip': '10.0.0.2', 'port': 2 }], [{ 'ip': '10.0.0.2', 'port': 2 }], [{ 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.4', 'port': 4 }]] loads = [['10.0.0.1', 1, 2, 0, 10, 20.0], ['10.0.0.2', 2, 2, 0, 10, 20.0], ['10.0.0.3', 3, 2, 0, 10, 20.0], ['10.0.0.4', 4, 2, 0, 10, 20.0]] _check_reality(configs, loads, vdisks, mds_services) # Validate first run. Each disk should now have sufficient nodes, since there are plenty of MDS services available configs = [[{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.2', 'port': 2 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.2', 'port': 2 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.1', 'port': 1 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.2', 'port': 2 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 2, 4, 10, 60.0], ['10.0.0.3', 3, 2, 4, 10, 60.0], ['10.0.0.4', 4, 2, 3, 10, 50.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validate whether this extra (unnessecairy) run doesn't change anything, preventing reconfiguring over and # over again for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validating whether an overloaded node will cause correct rebalancing mds_services[2].capacity = 2 mds_services[2].save() configs = [[{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.1', 'port': 1 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.1', 'port': 1 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 2, 0, 2, 100.0], ['10.0.0.3', 3, 2, 5, 10, 70.0], ['10.0.0.4', 4, 2, 5, 10, 70.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validate whether the overloaded services are still handled. In this case, causing a reoder of the slaves as # ordered in the model configs = [[{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.1', 'port': 1 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 2, 0, 2, 100.0], ['10.0.0.3', 3, 2, 5, 10, 70.0], ['10.0.0.4', 4, 2, 5, 10, 70.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Again, validating whether a subsequent run doesn't give unexpected changes for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # A MDS service will be added (next to the overloaded service), this should cause the expected rebalancing s_id = '{0}-5'.format(storagerouters[2].name) service = Service() service.name = s_id service.storagerouter = storagerouters[2] service.ports = [5] service.type = service_type service.save() mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = vpools[1] mds_service.save() mds_services[5] = mds_service configs = [[{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.2', 'port': 5 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.2', 'port': 5 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.1', 'port': 1 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.2', 'port': 5 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 2, 0, 2, 100.0], ['10.0.0.3', 3, 2, 5, 10, 70.0], ['10.0.0.4', 4, 2, 5, 10, 70.0], ['10.0.0.2', 5, 0, 3, 10, 30.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # If the tlogs are not catched up, nothing should be changed for vdisk_id in [3, 4]: StorageDriverClient.catch_up[vdisks[vdisk_id].volume_id] = 1000 for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # The next run, after tlogs are catched up, a master switch should be executed for vdisk_id in [3, 4]: StorageDriverClient.catch_up[vdisks[vdisk_id].volume_id] = 50 configs = [[{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 5 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.1', 'port': 1 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.2', 'port': 5 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }]] loads = [['10.0.0.1', 1, 2, 5, 10, 70.0], ['10.0.0.2', 2, 1, 0, 2, 50.0], ['10.0.0.3', 3, 2, 5, 10, 70.0], ['10.0.0.4', 4, 2, 5, 10, 70.0], ['10.0.0.2', 5, 1, 1, 10, 20.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validate whether a volume migration makes the master follow StorageDriverClient.vrouter_id[ vdisks[1].volume_id] = storagedrivers[3].storagedriver_id configs = [[{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.2', 'port': 5 }], [{ 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 5 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.3', 'port': 3 }], [{ 'ip': '10.0.0.2', 'port': 2 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }, { 'ip': '10.0.0.4', 'port': 4 }], [{ 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.1', 'port': 1 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.2', 'port': 5 }], [{ 'ip': '10.0.0.4', 'port': 4 }, { 'ip': '10.0.0.3', 'port': 3 }, { 'ip': '10.0.0.1', 'port': 1 }]] loads = [['10.0.0.1', 1, 1, 6, 10, 70.0], ['10.0.0.2', 2, 1, 0, 2, 50.0], ['10.0.0.3', 3, 3, 4, 10, 70.0], ['10.0.0.4', 4, 2, 4, 10, 60.0], ['10.0.0.2', 5, 1, 2, 10, 30.0]] for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services) # Validates if a second run doesn't change anything for vdisk_id in sorted(vdisks.keys()): MDSServiceController.ensure_safety(vdisks[vdisk_id]) _check_reality(configs, loads, vdisks, mds_services)
def test_clone_snapshot(self): """ Validates that a snapshot that has clones will not be deleted while other snapshots will be deleted """ # Setup # There are 2 disks, second one cloned from a snapshot of the first vpool = VPool() vpool.name = 'vpool' vpool.status = 'RUNNING' vpool.save() storage_router = StorageRouter() storage_router.name = 'storage_router' storage_router.ip = '127.0.0.1' storage_router.machine_id = System.get_my_machine_id() storage_router.rdma_capable = False storage_router.save() disk = Disk() disk.name = 'physical_disk_1' disk.aliases = ['/dev/non-existent'] disk.size = 500 * 1024 ** 3 disk.state = 'OK' disk.is_ssd = True disk.storagerouter = storage_router disk.save() disk_partition = DiskPartition() disk_partition.disk = disk disk_partition.aliases = ['/dev/disk/non-existent'] disk_partition.size = 400 * 1024 ** 3 disk_partition.state = 'OK' disk_partition.offset = 1024 disk_partition.roles = [DiskPartition.ROLES.SCRUB] disk_partition.mountpoint = '/var/tmp' disk_partition.save() storage_driver = StorageDriver() storage_driver.vpool = vpool storage_driver.storagerouter = storage_router storage_driver.name = 'storage_driver_1' storage_driver.mountpoint = '/' storage_driver.cluster_ip = storage_router.ip storage_driver.storage_ip = '127.0.0.1' storage_driver.storagedriver_id = 'storage_driver_1' storage_driver.ports = {'management': 1, 'xmlrpc': 2, 'dtl': 3, 'edge': 4} storage_driver.save() service_type = ServiceType() service_type.name = 'MetadataServer' service_type.save() service = Service() service.name = 'service_1' service.storagerouter = storage_driver.storagerouter service.ports = [1] service.type = service_type service.save() mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = storage_driver.vpool mds_service.save() vdisk_1_1 = VDisk() vdisk_1_1.name = 'vdisk_1_1' vdisk_1_1.volume_id = 'vdisk_1_1' vdisk_1_1.vpool = vpool vdisk_1_1.devicename = 'dummy' vdisk_1_1.size = 0 vdisk_1_1.save() vdisk_1_1.reload_client('storagedriver') [dynamic for dynamic in vdisk_1_1._dynamics if dynamic.name == 'snapshots'][0].timeout = 0 travis = 'TRAVIS' in os.environ and os.environ['TRAVIS'] == 'true' if travis is True: print 'Running in Travis, reducing output.' base = datetime.datetime.now().date() day = datetime.timedelta(1) base_timestamp = self._make_timestamp(base, day) minute = 60 hour = minute * 60 for h in [6, 12, 18]: timestamp = base_timestamp + (hour * h) VDiskController.create_snapshot(vdisk_guid=vdisk_1_1.guid, metadata={'label': 'snapshot_{0}:30'.format(str(h)), 'is_consistent': True, 'timestamp': str(timestamp), 'machineguid': None}) base_snapshot_guid = vdisk_1_1.snapshots[0]['guid'] # Oldest clone_vdisk = VDisk() clone_vdisk.name = 'clone_vdisk' clone_vdisk.volume_id = 'clone_vdisk' clone_vdisk.vpool = vpool clone_vdisk.devicename = 'dummy' clone_vdisk.parentsnapshot = base_snapshot_guid clone_vdisk.size = 0 clone_vdisk.save() clone_vdisk.reload_client('storagedriver') for h in [6, 12, 18]: timestamp = base_timestamp + (hour * h) VDiskController.create_snapshot(vdisk_guid=clone_vdisk.guid, metadata={'label': 'snapshot_{0}:30'.format(str(h)), 'is_consistent': True, 'timestamp': str(timestamp), 'machineguid': None}) base_timestamp = self._make_timestamp(base, day * 2) ScheduledTaskController.delete_snapshots(timestamp=base_timestamp + (minute * 30)) self.assertIn(base_snapshot_guid, [snap['guid'] for snap in vdisk_1_1.snapshots], 'Snapshot was deleted while there are still clones of it')
def build_service_structure(structure, previous_structure=None): """ Builds an MDS service structure Example: structure = Helper.build_service_structure( {'vpools': [1], 'domains': [], 'storagerouters': [1], 'storagedrivers': [(1, 1, 1)], # (<id>, <vpool_id>, <storagerouter_id>) 'mds_services': [(1, 1)], # (<id>, <storagedriver_id>) 'storagerouter_domains': []} # (<id>, <storagerouter_id>, <domain_id>) ) """ if previous_structure is None: previous_structure = {} vdisks = previous_structure.get('vdisks', {}) vpools = previous_structure.get('vpools', {}) domains = previous_structure.get('domains', {}) services = previous_structure.get('services', {}) mds_services = previous_structure.get('mds_services', {}) storagerouters = previous_structure.get('storagerouters', {}) storagedrivers = previous_structure.get('storagedrivers', {}) storagerouter_domains = previous_structure.get('storagerouter_domains', {}) service_type = ServiceTypeList.get_by_name('MetadataServer') if service_type is None: service_type = ServiceType() service_type.name = 'MetadataServer' service_type.save() srclients = {} for domain_id in structure.get('domains', []): if domain_id not in domains: domain = Domain() domain.name = 'domain_{0}'.format(domain_id) domain.save() domains[domain_id] = domain for vpool_id in structure.get('vpools', []): if vpool_id not in vpools: vpool = VPool() vpool.name = str(vpool_id) vpool.status = 'RUNNING' vpool.save() vpools[vpool_id] = vpool else: vpool = vpools[vpool_id] srclients[vpool_id] = StorageRouterClient(vpool.guid, None) for sr_id in structure.get('storagerouters', []): if sr_id not in storagerouters: storagerouter = StorageRouter() storagerouter.name = str(sr_id) storagerouter.ip = '10.0.0.{0}'.format(sr_id) storagerouter.rdma_capable = False storagerouter.node_type = 'MASTER' storagerouter.machine_id = str(sr_id) storagerouter.save() storagerouters[sr_id] = storagerouter disk = Disk() disk.storagerouter = storagerouter disk.state = 'OK' disk.name = '/dev/uda' disk.size = 1 * 1024**4 disk.is_ssd = True disk.aliases = ['/dev/uda'] disk.save() partition = DiskPartition() partition.offset = 0 partition.size = disk.size partition.aliases = ['/dev/uda-1'] partition.state = 'OK' partition.mountpoint = '/tmp/unittest/sr_{0}/disk_1/partition_1'.format( sr_id) partition.disk = disk partition.roles = [ DiskPartition.ROLES.DB, DiskPartition.ROLES.SCRUB ] partition.save() for sd_id, vpool_id, sr_id in structure.get('storagedrivers', ()): if sd_id not in storagedrivers: storagedriver = StorageDriver() storagedriver.vpool = vpools[vpool_id] storagedriver.storagerouter = storagerouters[sr_id] storagedriver.name = str(sd_id) storagedriver.mountpoint = '/' storagedriver.cluster_ip = storagerouters[sr_id].ip storagedriver.storage_ip = '10.0.1.{0}'.format(sr_id) storagedriver.storagedriver_id = str(sd_id) storagedriver.ports = { 'management': 1, 'xmlrpc': 2, 'dtl': 3, 'edge': 4 } storagedriver.save() storagedrivers[sd_id] = storagedriver Helper._set_vpool_storage_driver_configuration( vpool=vpools[vpool_id], storagedriver=storagedriver) for mds_id, sd_id in structure.get('mds_services', ()): if mds_id not in mds_services: sd = storagedrivers[sd_id] s_id = '{0}-{1}'.format(sd.storagerouter.name, mds_id) service = Service() service.name = s_id service.storagerouter = sd.storagerouter service.ports = [mds_id] service.type = service_type service.save() services[s_id] = service mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = sd.vpool mds_service.save() mds_services[mds_id] = mds_service StorageDriverController.add_storagedriverpartition( sd, { 'size': None, 'role': DiskPartition.ROLES.DB, 'sub_role': StorageDriverPartition.SUBROLE.MDS, 'partition': sd.storagerouter.disks[0].partitions[0], 'mds_service': mds_service }) for vdisk_id, storage_driver_id, vpool_id, mds_id in structure.get( 'vdisks', ()): if vdisk_id not in vdisks: vpool = vpools[vpool_id] devicename = 'vdisk_{0}'.format(vdisk_id) mds_backend_config = Helper._generate_mdsmetadatabackendconfig( [] if mds_id is None else [mds_services[mds_id]]) volume_id = srclients[vpool_id].create_volume( devicename, mds_backend_config, 0, str(storage_driver_id)) vdisk = VDisk() vdisk.name = str(vdisk_id) vdisk.devicename = devicename vdisk.volume_id = volume_id vdisk.vpool = vpool vdisk.size = 0 vdisk.save() vdisk.reload_client('storagedriver') vdisks[vdisk_id] = vdisk for srd_id, sr_id, domain_id, backup in structure.get( 'storagerouter_domains', ()): if srd_id not in storagerouter_domains: sr_domain = StorageRouterDomain() sr_domain.backup = backup sr_domain.domain = domains[domain_id] sr_domain.storagerouter = storagerouters[sr_id] sr_domain.save() storagerouter_domains[srd_id] = sr_domain return { 'vdisks': vdisks, 'vpools': vpools, 'domains': domains, 'services': services, 'service_type': service_type, 'mds_services': mds_services, 'storagerouters': storagerouters, 'storagedrivers': storagedrivers, 'storagerouter_domains': storagerouter_domains }
def _build_service_structure(self, structure): """ Builds an MDS service structure """ vpools = {} storagerouters = {} storagedrivers = {} services = {} mds_services = {} service_type = ServiceType() service_type.name = 'MetadataServer' service_type.save() for vpool_id in structure['vpools']: vpool = VPool() vpool.name = str(vpool_id) vpool.backend_type = BackendType() vpool.save() vpools[vpool_id] = vpool for sr_id in structure['storagerouters']: storagerouter = StorageRouter() storagerouter.name = str(sr_id) storagerouter.ip = '10.0.0.{0}'.format(sr_id) storagerouter.pmachine = PMachine() storagerouter.save() storagerouters[sr_id] = storagerouter for sd_info in structure['storagedrivers']: sd_id, vpool_id, sr_id = sd_info storagedriver = StorageDriver() storagedriver.vpool = vpools[vpool_id] storagedriver.storagerouter = storagerouters[sr_id] storagedriver.name = str(sd_id) storagedriver.mountpoint_temp = '/' storagedriver.mountpoint_foc = '/' storagedriver.mountpoint_readcaches = ['/'] storagedriver.mountpoint_writecaches = ['/'] storagedriver.mountpoint_temp = '/' storagedriver.mountpoint_md = '/' storagedriver.mountpoint_bfs = '/' storagedriver.mountpoint_fragmentcache = '/' storagedriver.mountpoint = '/' storagedriver.cluster_ip = storagerouters[sr_id].ip storagedriver.storage_ip = '127.0.0.1' storagedriver.storagedriver_id = str(sd_id) storagedriver.ports = [1, 2, 3] storagedriver.save() storagedrivers[sd_id] = storagedriver for mds_info in structure['mds_services']: mds_id, sd_id = mds_info sd = storagedrivers[sd_id] s_id = '{0}-{1}'.format(sd.storagerouter.name, mds_id) service = Service() service.name = s_id service.storagerouter = sd.storagerouter service.ports = [mds_id] service.type = service_type service.save() services[s_id] = service mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = sd.vpool mds_service.save() mds_services[mds_id] = mds_service return vpools, storagerouters, storagedrivers, services, mds_services, service_type
def _prepare(self): # Setup failure_domain = FailureDomain() failure_domain.name = 'Test' failure_domain.save() backend_type = BackendType() backend_type.name = 'BackendType' backend_type.code = 'BT' backend_type.save() vpool = VPool() vpool.name = 'vpool' vpool.backend_type = backend_type vpool.save() pmachine = PMachine() pmachine.name = 'PMachine' pmachine.username = '******' pmachine.ip = '127.0.0.1' pmachine.hvtype = 'KVM' pmachine.save() vmachine_1 = VMachine() vmachine_1.name = 'vmachine_1' vmachine_1.devicename = 'dummy' vmachine_1.pmachine = pmachine vmachine_1.is_vtemplate = True vmachine_1.save() vdisk_1_1 = VDisk() vdisk_1_1.name = 'vdisk_1_1' vdisk_1_1.volume_id = 'vdisk_1_1' vdisk_1_1.vmachine = vmachine_1 vdisk_1_1.vpool = vpool vdisk_1_1.devicename = 'dummy' vdisk_1_1.size = 0 vdisk_1_1.save() vdisk_1_1.reload_client() storage_router = StorageRouter() storage_router.name = 'storage_router' storage_router.ip = '127.0.0.1' storage_router.pmachine = pmachine storage_router.machine_id = System.get_my_machine_id() storage_router.rdma_capable = False storage_router.primary_failure_domain = failure_domain storage_router.save() storagedriver = StorageDriver() storagedriver.vpool = vpool storagedriver.storagerouter = storage_router storagedriver.name = '1' storagedriver.mountpoint = '/' storagedriver.cluster_ip = storage_router.ip storagedriver.storage_ip = '127.0.0.1' storagedriver.storagedriver_id = '1' storagedriver.ports = [1, 2, 3] storagedriver.save() service_type = ServiceType() service_type.name = 'MetadataServer' service_type.save() s_id = '{0}-{1}'.format(storagedriver.storagerouter.name, '1') service = Service() service.name = s_id service.storagerouter = storagedriver.storagerouter service.ports = [1] service.type = service_type service.save() mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = storagedriver.vpool mds_service.save() def ensure_safety(vdisk): pass class Dtl_Checkup(): @staticmethod def delay(vpool_guid=None, vdisk_guid=None, storagerouters_to_exclude=None): pass MDSServiceController.ensure_safety = staticmethod(ensure_safety) VDiskController.dtl_checkup = Dtl_Checkup return vdisk_1_1, pmachine
def build_service_structure(structure, previous_structure=None): """ Builds an MDS service structure Example: structure = Helper.build_service_structure( {'vpools': [1], 'domains': [], 'storagerouters': [1], 'storagedrivers': [(1, 1, 1)], # (<id>, <vpool_id>, <storagerouter_id>) 'mds_services': [(1, 1)], # (<id>, <storagedriver_id>) 'storagerouter_domains': []} # (<id>, <storagerouter_id>, <domain_id>) ) """ if previous_structure is None: previous_structure = {} vdisks = previous_structure.get("vdisks", {}) vpools = previous_structure.get("vpools", {}) domains = previous_structure.get("domains", {}) services = previous_structure.get("services", {}) mds_services = previous_structure.get("mds_services", {}) storagerouters = previous_structure.get("storagerouters", {}) storagedrivers = previous_structure.get("storagedrivers", {}) storagerouter_domains = previous_structure.get("storagerouter_domains", {}) service_type = ServiceTypeList.get_by_name("MetadataServer") if service_type is None: service_type = ServiceType() service_type.name = "MetadataServer" service_type.save() srclients = {} for domain_id in structure.get("domains", []): if domain_id not in domains: domain = Domain() domain.name = "domain_{0}".format(domain_id) domain.save() domains[domain_id] = domain for vpool_id in structure.get("vpools", []): if vpool_id not in vpools: vpool = VPool() vpool.name = str(vpool_id) vpool.status = "RUNNING" vpool.save() vpools[vpool_id] = vpool else: vpool = vpools[vpool_id] srclients[vpool_id] = StorageRouterClient(vpool.guid, None) for sr_id in structure.get("storagerouters", []): if sr_id not in storagerouters: storagerouter = StorageRouter() storagerouter.name = str(sr_id) storagerouter.ip = "10.0.0.{0}".format(sr_id) storagerouter.rdma_capable = False storagerouter.node_type = "MASTER" storagerouter.machine_id = str(sr_id) storagerouter.save() storagerouters[sr_id] = storagerouter disk = Disk() disk.storagerouter = storagerouter disk.state = "OK" disk.name = "/dev/uda" disk.size = 1 * 1024 ** 4 disk.is_ssd = True disk.aliases = ["/dev/uda"] disk.save() partition = DiskPartition() partition.offset = 0 partition.size = disk.size partition.aliases = ["/dev/uda-1"] partition.state = "OK" partition.mountpoint = "/tmp/unittest/sr_{0}/disk_1/partition_1".format(sr_id) partition.disk = disk partition.roles = [DiskPartition.ROLES.DB, DiskPartition.ROLES.SCRUB] partition.save() for sd_id, vpool_id, sr_id in structure.get("storagedrivers", ()): if sd_id not in storagedrivers: storagedriver = StorageDriver() storagedriver.vpool = vpools[vpool_id] storagedriver.storagerouter = storagerouters[sr_id] storagedriver.name = str(sd_id) storagedriver.mountpoint = "/" storagedriver.cluster_ip = storagerouters[sr_id].ip storagedriver.storage_ip = "10.0.1.{0}".format(sr_id) storagedriver.storagedriver_id = str(sd_id) storagedriver.ports = {"management": 1, "xmlrpc": 2, "dtl": 3, "edge": 4} storagedriver.save() storagedrivers[sd_id] = storagedriver Helper._set_vpool_storage_driver_configuration(vpool=vpools[vpool_id], storagedriver=storagedriver) for mds_id, sd_id in structure.get("mds_services", ()): if mds_id not in mds_services: sd = storagedrivers[sd_id] s_id = "{0}-{1}".format(sd.storagerouter.name, mds_id) service = Service() service.name = s_id service.storagerouter = sd.storagerouter service.ports = [mds_id] service.type = service_type service.save() services[s_id] = service mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = sd.vpool mds_service.save() mds_services[mds_id] = mds_service StorageDriverController.add_storagedriverpartition( sd, { "size": None, "role": DiskPartition.ROLES.DB, "sub_role": StorageDriverPartition.SUBROLE.MDS, "partition": sd.storagerouter.disks[0].partitions[0], "mds_service": mds_service, }, ) for vdisk_id, storage_driver_id, vpool_id, mds_id in structure.get("vdisks", ()): if vdisk_id not in vdisks: vpool = vpools[vpool_id] devicename = "vdisk_{0}".format(vdisk_id) mds_backend_config = Helper._generate_mdsmetadatabackendconfig( [] if mds_id is None else [mds_services[mds_id]] ) volume_id = srclients[vpool_id].create_volume(devicename, mds_backend_config, 0, str(storage_driver_id)) vdisk = VDisk() vdisk.name = str(vdisk_id) vdisk.devicename = devicename vdisk.volume_id = volume_id vdisk.vpool = vpool vdisk.size = 0 vdisk.save() vdisk.reload_client("storagedriver") vdisks[vdisk_id] = vdisk for srd_id, sr_id, domain_id, backup in structure.get("storagerouter_domains", ()): if srd_id not in storagerouter_domains: sr_domain = StorageRouterDomain() sr_domain.backup = backup sr_domain.domain = domains[domain_id] sr_domain.storagerouter = storagerouters[sr_id] sr_domain.save() storagerouter_domains[srd_id] = sr_domain return { "vdisks": vdisks, "vpools": vpools, "domains": domains, "services": services, "service_type": service_type, "mds_services": mds_services, "storagerouters": storagerouters, "storagedrivers": storagedrivers, "storagerouter_domains": storagerouter_domains, }
def test_clone_snapshot(self): """ Validates that a snapshot that has clones will not be deleted while other snapshots will be deleted """ # Setup # There are 2 disks, second one cloned from a snapshot of the first vpool = VPool() vpool.name = 'vpool' vpool.status = 'RUNNING' vpool.save() storage_router = StorageRouter() storage_router.name = 'storage_router' storage_router.ip = '127.0.0.1' storage_router.machine_id = System.get_my_machine_id() storage_router.rdma_capable = False storage_router.save() disk = Disk() disk.name = 'physical_disk_1' disk.aliases = ['/dev/non-existent'] disk.size = 500 * 1024**3 disk.state = 'OK' disk.is_ssd = True disk.storagerouter = storage_router disk.save() disk_partition = DiskPartition() disk_partition.disk = disk disk_partition.aliases = ['/dev/disk/non-existent'] disk_partition.size = 400 * 1024**3 disk_partition.state = 'OK' disk_partition.offset = 1024 disk_partition.roles = [DiskPartition.ROLES.SCRUB] disk_partition.mountpoint = '/var/tmp' disk_partition.save() storage_driver = StorageDriver() storage_driver.vpool = vpool storage_driver.storagerouter = storage_router storage_driver.name = 'storage_driver_1' storage_driver.mountpoint = '/' storage_driver.cluster_ip = storage_router.ip storage_driver.storage_ip = '127.0.0.1' storage_driver.storagedriver_id = 'storage_driver_1' storage_driver.ports = { 'management': 1, 'xmlrpc': 2, 'dtl': 3, 'edge': 4 } storage_driver.save() service_type = ServiceType() service_type.name = 'MetadataServer' service_type.save() service = Service() service.name = 'service_1' service.storagerouter = storage_driver.storagerouter service.ports = [1] service.type = service_type service.save() mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = storage_driver.vpool mds_service.save() vdisk_1_1 = VDisk() vdisk_1_1.name = 'vdisk_1_1' vdisk_1_1.volume_id = 'vdisk_1_1' vdisk_1_1.vpool = vpool vdisk_1_1.devicename = 'dummy' vdisk_1_1.size = 0 vdisk_1_1.save() vdisk_1_1.reload_client('storagedriver') [ dynamic for dynamic in vdisk_1_1._dynamics if dynamic.name == 'snapshots' ][0].timeout = 0 travis = 'TRAVIS' in os.environ and os.environ['TRAVIS'] == 'true' if travis is True: print 'Running in Travis, reducing output.' base = datetime.datetime.now().date() day = datetime.timedelta(1) base_timestamp = self._make_timestamp(base, day) minute = 60 hour = minute * 60 for h in [6, 12, 18]: timestamp = base_timestamp + (hour * h) VDiskController.create_snapshot(vdisk_guid=vdisk_1_1.guid, metadata={ 'label': 'snapshot_{0}:30'.format( str(h)), 'is_consistent': True, 'timestamp': str(timestamp), 'machineguid': None }) base_snapshot_guid = vdisk_1_1.snapshots[0]['guid'] # Oldest clone_vdisk = VDisk() clone_vdisk.name = 'clone_vdisk' clone_vdisk.volume_id = 'clone_vdisk' clone_vdisk.vpool = vpool clone_vdisk.devicename = 'dummy' clone_vdisk.parentsnapshot = base_snapshot_guid clone_vdisk.size = 0 clone_vdisk.save() clone_vdisk.reload_client('storagedriver') for h in [6, 12, 18]: timestamp = base_timestamp + (hour * h) VDiskController.create_snapshot(vdisk_guid=clone_vdisk.guid, metadata={ 'label': 'snapshot_{0}:30'.format( str(h)), 'is_consistent': True, 'timestamp': str(timestamp), 'machineguid': None }) base_timestamp = self._make_timestamp(base, day * 2) ScheduledTaskController.delete_snapshots(timestamp=base_timestamp + (minute * 30)) self.assertIn( base_snapshot_guid, [snap['guid'] for snap in vdisk_1_1.snapshots], 'Snapshot was deleted while there are still clones of it')
def test_clone(self): """ Test the clone functionality - Create a vDisk with name 'clone1' - Clone the vDisk and make some assertions - Attempt to clone again using same name and same devicename - Attempt to clone on Storage Router which is not linked to the vPool on which the original vDisk is hosted - Attempt to clone on Storage Driver without MDS service - Attempt to clone from snapshot which is not yet completely synced to backend - Attempt to delete the snapshot from which a clone was made - Clone the vDisk on another Storage Router - Clone another vDisk with name 'clone1' linked to another vPool """ structure = Helper.build_service_structure( {'vpools': [1, 2], 'storagerouters': [1, 2, 3], 'storagedrivers': [(1, 1, 1), (2, 2, 1)], # (<id>, <vpool_id>, <storagerouter_id>) 'mds_services': [(1, 1), (2, 2)]} # (<id>, <storagedriver_id>) ) vpools = structure['vpools'] mds_services = structure['mds_services'] service_type = structure['service_type'] storagedrivers = structure['storagedrivers'] storagerouters = structure['storagerouters'] self._roll_out_dtl_services(vpool=vpools[1], storagerouters=storagerouters) self._roll_out_dtl_services(vpool=vpools[2], storagerouters=storagerouters) # Basic clone scenario vdisk1 = VDisk(VDiskController.create_new(volume_name='vdisk_1', volume_size=1024 ** 3, storagedriver_guid=storagedrivers[1].guid)) clone1_info = VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone1') vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 2, msg='Expected to find 2 vDisks') clones = VDiskList.get_by_parentsnapshot(vdisk1.snapshots[0]['guid']) self.assertTrue(expr=len(clones) == 1, msg='Expected to find 1 vDisk with parent snapshot') self.assertTrue(expr=len(vdisk1.child_vdisks) == 1, msg='Expected to find 1 child vDisk') for expected_key in ['vdisk_guid', 'name', 'backingdevice']: self.assertTrue(expr=expected_key in clone1_info, msg='Expected to find key "{0}" in clone_info'.format(expected_key)) self.assertTrue(expr=clones[0].guid == clone1_info['vdisk_guid'], msg='Guids do not match') self.assertTrue(expr=clones[0].name == clone1_info['name'], msg='Names do not match') self.assertTrue(expr=clones[0].devicename == clone1_info['backingdevice'], msg='Device names do not match') # Attempt to clone again with same name with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone1') vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 1') # Attempt to clone again with a name which will have identical devicename with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone1%') vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 2') # Attempt to clone on Storage Router on which vPool is not extended with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2', storagerouter_guid=storagerouters[2].guid) vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 3') # Attempt to clone on non-existing Storage Driver storagedrivers[1].storagedriver_id = 'non-existing' storagedrivers[1].save() with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2') vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 4') storagedrivers[1].storagedriver_id = '1' storagedrivers[1].save() # Attempt to clone on Storage Driver without MDS service mds_services[1].service.storagerouter = storagerouters[3] mds_services[1].service.save() with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2') vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 5') mds_services[1].service.storagerouter = storagerouters[1] mds_services[1].service.save() # Attempt to clone by providing snapshot_id not synced to backend self.assertTrue(expr=len(vdisk1.snapshots) == 1, msg='Expected to find only 1 snapshot before cloning') metadata = {'label': 'label1', 'timestamp': int(time.time()), 'is_sticky': False, 'in_backend': False, 'is_automatic': True, 'is_consistent': True} snapshot_id = VDiskController.create_snapshot(vdisk_guid=vdisk1.guid, metadata=metadata) self.assertTrue(expr=len(vdisk1.snapshots) == 2, msg='Expected to find 2 snapshots') with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2', snapshot_id=snapshot_id) vdisks = VDiskList.get_vdisks() self.assertTrue(expr=len(vdisks) == 2, msg='Expected to find 2 vDisks after failed clone attempt 6') # Update backend synced flag and retry vdisk1.storagedriver_client._set_snapshot_in_backend(vdisk1.volume_id, snapshot_id, True) vdisk1.invalidate_dynamics('snapshots') VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone2', snapshot_id=snapshot_id) vdisks = VDiskList.get_vdisks() vdisk1.invalidate_dynamics() self.assertTrue(expr=len(vdisks) == 3, msg='Expected to find 3 vDisks') self.assertTrue(expr=len(vdisk1.child_vdisks) == 2, msg='Expected to find 2 child vDisks') self.assertTrue(expr=len(vdisk1.snapshots) == 2, msg='Expected to find 2 snapshots after cloning from a specified snapshot') # Attempt to delete the snapshot that has clones with self.assertRaises(RuntimeError): VDiskController.delete_snapshot(vdisk_guid=vdisk1.guid, snapshot_id=snapshot_id) # Clone on specific Storage Router storagedriver = StorageDriver() storagedriver.vpool = vpools[1] storagedriver.storagerouter = storagerouters[2] storagedriver.name = '3' storagedriver.mountpoint = '/' storagedriver.cluster_ip = storagerouters[2].ip storagedriver.storage_ip = '127.0.0.1' storagedriver.storagedriver_id = '3' storagedriver.ports = {'management': 1, 'xmlrpc': 2, 'dtl': 3, 'edge': 4} storagedriver.save() s_id = '{0}-1'.format(storagedriver.storagerouter.name) service = Service() service.name = s_id service.storagerouter = storagedriver.storagerouter service.ports = [3] service.type = service_type service.save() mds_service = MDSService() mds_service.service = service mds_service.number = 0 mds_service.capacity = 10 mds_service.vpool = storagedriver.vpool mds_service.save() clone3 = VDisk(VDiskController.clone(vdisk_guid=vdisk1.guid, name='clone3', storagerouter_guid=storagerouters[2].guid)['vdisk_guid']) self.assertTrue(expr=clone3.storagerouter_guid == storagerouters[2].guid, msg='Incorrect Storage Router on which the clone is attached') # Clone vDisk with existing name on another vPool vdisk2 = VDisk(VDiskController.create_new(volume_name='vdisk_1', volume_size=1024 ** 3, storagedriver_guid=storagedrivers[2].guid)) clone_vdisk2 = VDisk(VDiskController.clone(vdisk_guid=vdisk2.guid, name='clone1')['vdisk_guid']) self.assertTrue(expr=clone_vdisk2.vpool == vpools[2], msg='Cloned vDisk with name "clone1" was created on incorrect vPool') self.assertTrue(expr=len([vdisk for vdisk in VDiskList.get_vdisks() if vdisk.name == 'clone1']) == 2, msg='Expected to find 2 vDisks with name "clone1"') # Attempt to clone without specifying snapshot and snapshot fails to sync to backend StorageRouterClient.synced = False vdisk2 = VDisk(VDiskController.create_new(volume_name='vdisk_2', volume_size=1024 ** 3, storagedriver_guid=storagedrivers[1].guid)) with self.assertRaises(RuntimeError): VDiskController.clone(vdisk_guid=vdisk2.guid, name='clone4') vdisk2.invalidate_dynamics() self.assertTrue(expr=len(vdisk2.snapshots) == 0, msg='Expected to find 0 snapshots after clone failure') self.assertTrue(expr=len(vdisk2.child_vdisks) == 0, msg='Expected to find 0 children after clone failure') StorageRouterClient.synced = True