def test_create_volume_dfn(self): rsc_dfn = self.execute_with_single_resp( ['resource-definition', 'create', 'rscvlm']) self.assert_api_succuess(rsc_dfn) vlm_dfn = self.execute_with_single_resp( ['volume-definition', 'create', 'rscvlm', '128MiB']) self.assert_api_succuess(vlm_dfn) self.assert_volume_def( 'rscvlm', 0, None, SizeCalc.convert_round_up(128, SizeCalc.UNIT_MiB, SizeCalc.UNIT_KiB)) vlm_dfn = self.execute_with_single_resp( ['volume-definition', 'create', 'rscvlm', '0']) self.assertTrue(vlm_dfn.is_error()) self.assertEqual( self.signed_mask(MASK_VLM_DFN | MASK_CRT | FAIL_INVLD_VLM_SIZE), vlm_dfn.ret_code) vlm_dfn = self.execute_with_single_resp([ 'volume-definition', 'create', 'rscvlm', '--vlmnr', '3', '256Mib' ]) self.assert_api_succuess(vlm_dfn) self.assertEqual(MASK_VLM_DFN | MASK_CRT | CREATED, vlm_dfn.ret_code) self.assert_volume_def( 'rscvlm', 3, None, SizeCalc.convert_round_up(256, SizeCalc.UNIT_MiB, SizeCalc.UNIT_KiB)) with self.assertRaises(SystemExit): self.execute_with_single_resp( ['volume-definition', 'create', 'rscvlm', '1Gi'])
def parse_opts(cls, new_args, object_name): modify = {} deletes = [] for arg in new_args: is_unset = arg.startswith(cls.unsetprefix) value = new_args[arg] prop_name = arg[len(cls.unsetprefix) + 1:] if is_unset else arg if prop_name.startswith( "drbd-") and prop_name[5:] in cls.CLASH_OPTIONS: prop_name = prop_name[5:] option = cls.drbd_options[object_name][prop_name] key = option['key'] if is_unset: deletes.append(key) else: if DrbdOptions._is_byte_unit(option): unit = SizeCalc.UNIT_KiB if option.get( 'unit_prefix') == 'k' else SizeCalc.UNIT_B value = SizeCalc.auto_convert(value, unit) if option['min'] <= value <= option['max']: value = str(value) else: raise ArgumentError( prop_name + " value {v}{u} is out of range [{mi}-{ma}]".format( v=value, u=SizeCalc.unit_to_str(unit), mi=str(option['min']) + option.get('unit_prefix', ''), ma=str(option['max']) + option.get('unit_prefix', ''))) modify[key] = str(value) return modify, deletes
def test_size_calc_convert(self): self.assertEqual(10485760, SizeCalc.auto_convert("10M", SizeCalc.UNIT_B)) self.assertEqual(10485760, SizeCalc.auto_convert("10MiB", SizeCalc.UNIT_B)) self.assertEqual(10000000, SizeCalc.auto_convert("10MB", SizeCalc.UNIT_B)) self.assertEqual(3221225472, SizeCalc.auto_convert("3Gib", SizeCalc.UNIT_B)) self.assertEqual(3145728, SizeCalc.auto_convert("3Gib", SizeCalc.UNIT_KiB))
def parse_opts(cls, new_args, object_name): modify = {} deletes = [] for arg in new_args: value = new_args[arg] is_unset = arg.startswith(cls.unsetprefix) prop_name = arg[len(cls.unsetprefix) + 1:] if is_unset else arg option = cls.drbd_options[object_name][prop_name] key = option['key'] if is_unset: deletes.append(key) else: if 'bytes' in option and option['unit'] == 'bytes': value = SizeCalc.auto_convert(value, SizeCalc.UNIT_B) if option['min'] < value < option['max']: value = str(value) else: raise argparse.ArgumentTypeError( prop_name + " value {v} is out of range [{mi}-{ma}]".format( v=value, mi=option['min'], ma=option['max'])) modify[key] = str(value) return modify, deletes
def parse_size_str(cls, size_str, default_unit="GiB"): if size_str is None: return None m = re.match(r'(\d+)(\D*)', size_str) size = 0 try: size = int(m.group(1)) except AttributeError: sys.stderr.write('Size is not a valid number\n') sys.exit(ExitCode.ARGPARSE_ERROR) unit_str = m.group(2) if unit_str == "": unit_str = default_unit try: _, unit = SizeCalc.UNITS_MAP[unit_str.lower()] except KeyError: sys.stderr.write('"%s" is not a valid unit!\n' % unit_str) sys.stderr.write('Valid units: %s\n' % SizeCalc.UNITS_LIST_STR) sys.exit(ExitCode.ARGPARSE_ERROR) _, unit = SizeCalc.UNITS_MAP[unit_str.lower()] if unit != SizeCalc.UNIT_KiB: size = SizeCalc.convert_round_up(size, unit, SizeCalc.UNIT_KiB) return size
def show(cls, args, lstmsg): tbl = linstor_client.Table(utf8=not args.no_utf8, colors=not args.no_color, pastable=args.pastable) vlm_dfn_hdrs = list(cls._vlm_dfn_headers) if args.external_name: vlm_dfn_hdrs.insert(1, linstor_client.TableHeader("External")) for hdr in vlm_dfn_hdrs: tbl.add_header(hdr) tbl.set_groupby(args.groupby if args.groupby else [tbl.header_name(0)]) for rsc_dfn in lstmsg.resource_definitions: for vlmdfn in rsc_dfn.volume_definitions: state = tbl.color_cell("ok", Color.DARKGREEN) if FLAG_DELETE in vlmdfn.flags: state = tbl.color_cell("DELETING", Color.RED) elif FLAG_RESIZE in vlmdfn.flags: state = tbl.color_cell("resizing", Color.DARKPINK) drbd_data = vlmdfn.drbd_data tbl.add_row([ rsc_dfn.name, vlmdfn.number, drbd_data.minor if drbd_data else "", SizeCalc.approximate_size_string(vlmdfn.size), "+" if FLAG_GROSS_SIZE in vlmdfn.flags else "", state ]) tbl.show()
def show(cls, args, lstmsg): tbl = linstor_client.Table(utf8=not args.no_utf8, colors=not args.no_color, pastable=args.pastable) tbl.add_column("ResourceName") tbl.add_column("SnapshotName") tbl.add_column("NodeNames") tbl.add_column("Volumes") tbl.add_column("State", color=Output.color(Color.DARKGREEN, args.no_color)) for snapshot_dfn in lstmsg.snapshots: if FLAG_DELETE in snapshot_dfn.flags: state_cell = tbl.color_cell("DELETING", Color.RED) elif FLAG_FAILED_DEPLOYMENT in snapshot_dfn.flags: state_cell = tbl.color_cell("Failed", Color.RED) elif FLAG_FAILED_DISCONNECT in snapshot_dfn.flags: state_cell = tbl.color_cell("Satellite disconnected", Color.RED) elif FLAG_SUCCESSFUL in snapshot_dfn.flags: state_cell = tbl.color_cell("Successful", Color.DARKGREEN) else: state_cell = tbl.color_cell("Incomplete", Color.DARKBLUE) tbl.add_row([ snapshot_dfn.resource_name, snapshot_dfn.name, ", ".join([node_name for node_name in snapshot_dfn.nodes]), ", ".join([ str(snapshot_vlm_dfn.number) + ": " + SizeCalc.approximate_size_string(snapshot_vlm_dfn.size) for snapshot_vlm_dfn in snapshot_dfn.snapshot_volume_definitions ]), state_cell ]) tbl.show()
def _show_query_max_volume(self, args, lstmsg): """ DEPRECATED will be removed :param args: :param lstmsg: :return: """ print( Output.color_str("DEPRECATED:", Color.YELLOW, args.no_color) + " use `linstor controller query-max-volume-size`") tbl = linstor_client.Table(utf8=not args.no_utf8, colors=not args.no_color, pastable=args.pastable) tbl.add_column("StoragePool") tbl.add_column("MaxVolumeSize", just_txt='>') tbl.add_column("Provisioning") tbl.add_column("Nodes") def limited_string(obj_list): limit = 40 s = "" list_length = len(obj_list) for i in range(0, len(obj_list)): obj = obj_list[i] s += obj + (", " if i != list_length - 1 else "") if len(s) > limit: s = s[:limit - 3] + "..." return s storage_pool_dfns = self.get_linstorapi().storage_pool_dfn_list( )[0].storage_pool_definitions for candidate in lstmsg.candidates: max_vlm_size = SizeCalc.approximate_size_string( candidate.max_volume_size) storage_pool_props = [ x for x in storage_pool_dfns if x.name == candidate.storage_pool ][0].properties max_oversubscription_ratio_props = \ [x for x in storage_pool_props if x.key == KEY_STOR_POOL_DFN_MAX_OVERSUBSCRIPTION_RATIO] max_oversubscription_ratio_prop = max_oversubscription_ratio_props[0].value \ if max_oversubscription_ratio_props \ else lstmsg.default_max_oversubscription_ratio max_oversubscription_ratio = float(max_oversubscription_ratio_prop) tbl.add_row([ candidate.storage_pool, max_vlm_size, "Thin, oversubscription ratio " + str(max_oversubscription_ratio) if candidate.all_thin else "Thick", limited_string(candidate.node_names) ]) tbl.show()
def show(self, args, lstmsg): tbl = linstor_client.Table(utf8=not args.no_utf8, colors=not args.no_color, pastable=args.pastable) for hdr in self._stor_pool_headers: tbl.add_header(hdr) storage_pool_resp = lstmsg # type: StoragePoolListResponse tbl.set_groupby(args.groupby if args. groupby else [self._stor_pool_headers[0].name]) errors = [] for storpool in storage_pool_resp.storage_pools: driver_device = linstor.StoragePoolDriver.storage_props_to_driver_pool( storpool.provider_kind, storpool.properties) free_capacity = "" total_capacity = "" if not storpool.is_diskless() and storpool.free_space is not None: free_capacity = SizeCalc.approximate_size_string( storpool.free_space.free_capacity) total_capacity = SizeCalc.approximate_size_string( storpool.free_space.total_capacity) for error in storpool.reports: if error not in errors: errors.append(error) state_str, state_color = self.get_replies_state(storpool.reports) tbl.add_row([ storpool.name, storpool.node_name, storpool.provider_kind, driver_device, free_capacity, total_capacity, storpool.supports_snapshots(), tbl.color_cell(state_str, state_color), storpool.free_space_mgr_name if ':' not in storpool.free_space_mgr_name else '' ]) tbl.show() for err in errors: Output.handle_ret(err, warn_as_error=args.warn_as_error, no_color=args.no_color)
def show(cls, args, lstmsg): tbl = linstor_client.Table(utf8=not args.no_utf8, colors=not args.no_color, pastable=args.pastable) tbl.add_column("ResourceName") tbl.add_column("SnapshotName") tbl.add_column("NodeNames") tbl.add_column("Volumes") tbl.add_column("CreatedOn") tbl.add_column("State", color=Output.color(Color.DARKGREEN, args.no_color)) for snapshot_dfn in lstmsg.snapshots: if FLAG_DELETE in snapshot_dfn.flags: state_cell = tbl.color_cell("DELETING", Color.RED) elif FLAG_FAILED_DEPLOYMENT in snapshot_dfn.flags: state_cell = tbl.color_cell("Failed", Color.RED) elif FLAG_FAILED_DISCONNECT in snapshot_dfn.flags: state_cell = tbl.color_cell("Satellite disconnected", Color.RED) elif FLAG_SUCCESSFUL in snapshot_dfn.flags: in_backup_restore = False in_backup_create = False if FLAG_BACKUP in snapshot_dfn.flags and FLAG_SHIPPING in snapshot_dfn.flags: for snap in snapshot_dfn.snapshots: in_backup_create |= FLAG_BACKUP_SOURCE in snap.flags in_backup_restore |= FLAG_BACKUP_TARGET in snap.flags if in_backup_create: state_cell = tbl.color_cell("Shipping", Color.YELLOW) elif in_backup_restore: state_cell = tbl.color_cell("Restoring", Color.YELLOW) else: state_cell = tbl.color_cell("Successful", Color.DARKGREEN) else: state_cell = tbl.color_cell("Incomplete", Color.DARKBLUE) snapshot_date = "" if snapshot_dfn.snapshots and snapshot_dfn.snapshots[ 0].create_datetime: snapshot_date = str( snapshot_dfn.snapshots[0].create_datetime)[:19] tbl.add_row([ snapshot_dfn.resource_name, snapshot_dfn.name, ", ".join([node_name for node_name in snapshot_dfn.nodes]), ", ".join([ str(snapshot_vlm_dfn.number) + ": " + SizeCalc.approximate_size_string(snapshot_vlm_dfn.size) for snapshot_vlm_dfn in snapshot_dfn.snapshot_volume_definitions ]), snapshot_date, state_cell ]) tbl.show()
def make_volume_node(cls, vlm): """ :param responses.Volume vlm: :return: """ volume_node = TreeNode('volume' + str(vlm.number), '', Color.DARKGREEN) volume_node.set_description('minor number: ' + str(vlm.drbd_data.drbd_volume_definition.minor) if vlm.drbd_data else '') volume_node.add_description( ', size: ' + str(SizeCalc.approximate_size_string(vlm.allocated_size)) ) return volume_node
def resize_disk(resource, target_vm, disk_id, new_size): """ :param Resource resource: :param vm.Vm target_vm: :param str disk_id: :param int new_size: new size in mega bytes :return: """ util.log_info("Resizing resource {r} new size: {s}MiB".format( r=resource.name, s=new_size)) resource.volumes[0].size = SizeCalc.convert(new_size, SizeCalc.UNIT_MiB, SizeCalc.UNIT_B) resize_if_qcow2(resource, target_vm, disk_id, new_size)
def _show_query_max_volume(self, args, lstmsg): tbl = linstor_client.Table(utf8=not args.no_utf8, colors=not args.no_color, pastable=args.pastable) tbl.add_column("StoragePool") tbl.add_column("MaxVolumeSize", just_txt='>') tbl.add_column("Provisioning") tbl.add_column("Nodes") def limited_string(obj_list): limit = 40 s = "" list_length = len(obj_list) for i in range(0, len(obj_list)): obj = obj_list[i] s += obj + (", " if i != list_length - 1 else "") if len(s) > limit: s = s[:limit - 3] + "..." return s storage_pool_dfns = self.get_linstorapi().storage_pool_dfn_list( )[0].storage_pool_definitions for candidate in lstmsg.candidates: max_vlm_size = SizeCalc.approximate_size_string( candidate.max_volume_size) storage_pool_props = [ x for x in storage_pool_dfns if x.name == candidate.storage_pool ][0].properties max_oversubscription_ratio = float( storage_pool_props.get( KEY_STOR_POOL_DFN_MAX_OVERSUBSCRIPTION_RATIO, lstmsg.default_max_oversubscription_ratio)) tbl.add_row([ candidate.storage_pool, max_vlm_size, "Thin, oversubscription ratio " + str(max_oversubscription_ratio) if candidate.all_thin else "Thick", limited_string(candidate.node_names) ]) tbl.show()
def show_volumes(cls, args, lstmsg): """ :param args: :param responses.ResourceResponse lstmsg: resource response data to display :return: None """ tbl = Table(utf8=not args.no_utf8, colors=not args.no_color, pastable=args.pastable) tbl.add_column("Node") tbl.add_column("Resource") tbl.add_column("StoragePool") tbl.add_column("VolNr", just_txt='>') tbl.add_column("MinorNr", just_txt='>') tbl.add_column("DeviceName") tbl.add_column("Allocated", just_txt='>') tbl.add_column("InUse", color=Output.color(Color.DARKGREEN, args.no_color)) tbl.add_column("State", color=Output.color(Color.DARKGREEN, args.no_color), just_txt='>') rsc_state_lkup = { x.node_name + x.name: x for x in lstmsg.resource_states } reports = [] for rsc in lstmsg.resources: if not args.all and apiconsts.FLAG_TIE_BREAKER in rsc.flags: continue # skip tie breaker resources if apiconsts.FLAG_RSC_INACTIVE in rsc.flags: continue # do not show non existing volumes for inactive resources rsc_state = rsc_state_lkup.get(rsc.node_name + rsc.name) rsc_usage = "" if rsc_state and rsc_state.in_use is not None: if rsc_state.in_use: rsc_usage = tbl.color_cell("InUse", Color.GREEN) else: rsc_usage = "Unused" for vlm in rsc.volumes: vlm_state = cls.get_volume_state( rsc_state.volume_states, vlm.number) if rsc_state else None state_txt, color = cls.volume_state_cell( vlm_state, rsc.flags, vlm.flags) has_errors = any([x.is_error() for x in vlm.reports]) conn_failed = (rsc.layer_data.drbd_resource and any(not v.connected for k, v in rsc.layer_data. drbd_resource.connections.items())) if conn_failed: color = Color.RED state = tbl.color_cell(state_txt, color) if color else state_txt if has_errors: state = tbl.color_cell("Error", Color.RED) for x in vlm.reports: reports.append(x) vlm_drbd_data = vlm.drbd_data tbl.add_row([ rsc.node_name, rsc.name, vlm.storage_pool_name, str(vlm.number), str(vlm_drbd_data.drbd_volume_definition.minor) if vlm_drbd_data else "", vlm.device_path, SizeCalc.approximate_size_string(vlm.allocated_size) if vlm.allocated_size else "", rsc_usage, state ]) tbl.show() for x in reports: Output.handle_ret(x, args.no_color, warn_as_error=args.warn_as_error)
def assertSizeUnit(self, val, exp_size, exp_unit): size, unit = SizeCalc.parse_unit(val) self.assertEqual(exp_size, size) self.assertEqual(exp_unit, unit)
def show_backups_info(cls, args, lstmsg): rsc_tbl = Table(utf8=not args.no_utf8, colors=not args.no_color, pastable=args.pastable) rsc_tbl.add_column("Resource") rsc_tbl.add_column("Snapshot") rsc_tbl.add_column("Full Backup") rsc_tbl.add_column("Latest Backup") rsc_tbl.add_column("Backup Count") rsc_tbl.add_column("Download Size") rsc_tbl.add_column("Allocated Size") # table will only have a single row row = [ lstmsg.rsc, lstmsg.snap, lstmsg.full, lstmsg.latest, lstmsg.count ] row += [ SizeCalc.approximate_size_string(lstmsg.dl_size), SizeCalc.approximate_size_string(lstmsg.alloc_size) ] rsc_tbl.add_row(row) rsc_tbl.show() stor_pool_tbl = Table(utf8=not args.no_utf8, colors=not args.no_color, pastable=args.pastable) stor_pool_tbl.add_column("Origin StorPool (Type)") if args.target_node: stor_pool_tbl.add_column("Target Pool") stor_pool_tbl.add_column("Remaining Free Space") stor_pool_tbl.add_column("Volume to Download") stor_pool_tbl.add_column("Type") stor_pool_tbl.add_column("Download Size") stor_pool_tbl.add_column("Allocated Size") stor_pool_tbl.add_column("Usable Size") for stor_pool in lstmsg.storpools: row = [stor_pool.name + " (" + stor_pool.provider_kind + ")"] if args.target_node: row += [ stor_pool.target_name, ] if stor_pool.remaining_space < 0: row += [ stor_pool_tbl.color_cell( "-" + SizeCalc.approximate_size_string( -stor_pool.remaining_space), Color.RED) ] else: row += [ SizeCalc.approximate_size_string( stor_pool.remaining_space) ] vlm_to_dl_cell = [] type_cell = [] dl_size_cell = [] alloc_size_cell = [] usable_size_cell = [] for volume in stor_pool.volumes: vlm_to_dl_cell += [volume.name if volume.name else "-"] type_cell += [volume.layer_type] dl_size_cell += [ SizeCalc.approximate_size_string(volume.dl_size) if volume.dl_size else "-" ] alloc_size_cell += [ SizeCalc.approximate_size_string(volume.alloc_size) if volume.alloc_size else "-" ] usable_size_cell += [ SizeCalc.approximate_size_string(volume.usable_size) if volume.usable_size else "-" ] row += [ "\n".join(vlm_to_dl_cell), "\n".join(type_cell), "\n".join(dl_size_cell), "\n".join(alloc_size_cell), "\n".join(usable_size_cell) ] stor_pool_tbl.add_row(row) stor_pool_tbl.show()