def _validate_put(self, bundle, request): errors = defaultdict(list) if 'conf_params' in bundle.data and bundle.data[ 'conf_params'] is not None: try: fs = ManagedFilesystem.objects.get(pk=bundle.data['id']) except ManagedFilesystem.DoesNotExist: errors['id'] = "Filesystem with id %s not found" % bundle.data[ 'id'] except KeyError: errors['id'] = "Field is mandatory" else: if fs.immutable_state: if not conf_param.compare(bundle.data['conf_params'], conf_param.get_conf_params(fs)): errors['conf_params'].append( "Cannot modify conf_params on immutable_state objects" ) else: conf_param_errors = conf_param.validate_conf_params( ManagedFilesystem, bundle.data['conf_params']) if conf_param_errors: errors['conf_params'] = conf_param_errors return errors
def validate_target(klass, target): target_errors = defaultdict(list) volume = Volume.objects.get(id=target['volume_id']) if 'inode_count' in target and 'bytes_per_inode' in target: target_errors['inode_count'].append( "inode_count and bytes_per_inode are mutually exclusive") if 'conf_params' in target: conf_param_errors = conf_param.validate_conf_params( klass, target['conf_params']) if conf_param_errors: # FIXME: not really representing target-specific validations cleanly, # will sort out while fixing HYD-1077. target_errors['conf_params'] = conf_param_errors for setting in ['inode_count', 'inode_size', 'bytes_per_inode']: if setting in target: if target[setting] is not None and not isinstance( target[setting], int): target_errors[setting].append("Must be an integer") # If they specify and inode size and a bytes_per_inode, check the inode fits # within the ratio try: inode_size = target['inode_size'] bytes_per_inode = target['bytes_per_inode'] if inode_size >= bytes_per_inode: target_errors['inode_size'].append( "inode_size must be less than bytes_per_inode") except KeyError: pass # If they specify an inode count, check it will fit on the device try: inode_count = target['inode_count'] except KeyError: # If no inode_count is specified, no need to check it against inode_size pass else: try: inode_size = target['inode_size'] except KeyError: inode_size = { ManagedMgs: 128, ManagedMdt: 512, ManagedOst: 256 }[klass] if inode_size is not None and inode_count is not None: if inode_count * inode_size > volume.size: target_errors['inode_count'].append( "%d %d-byte inodes too large for %s-byte device" % (inode_count, inode_size, volume.size)) return target_errors
def _validate_post(self, bundle, request): errors = defaultdict(list) targets = defaultdict(list) # Check 'mgt', 'mdts', 'osts' are present and compose # a record of targets which will be formatted try: # Check that client hasn't specified an existing MGT # *and* a volume to format. if 'id' in bundle.data['mgt'] and 'volume_id' in bundle.data['mgt']: errors['mgt'].append("id and volume_id are mutually exclusive") mgt = bundle.data['mgt'] if 'volume_id' in mgt: targets['mgt'].append(mgt) except KeyError: errors['mgt'].append("This field is mandatory") try: targets['mdts'].extend(bundle.data['mdts']) except KeyError: errors['mdts'].append("This field is mandatory") try: targets['osts'].extend(bundle.data['osts']) except KeyError: errors['osts'].append("This field is mandatory") if 'conf_params' not in bundle.data: errors['conf_params'].append("This field is mandatory") if 'name' not in bundle.data: errors['name'].append("This field is mandatory") # Return if some of the things we're going to validate in detail are absent if len(errors): return errors # As all fields are present we can be more specific about the errors. errors['mgt'] = defaultdict(list) errors['mdts'] = defaultdict(list) errors['osts'] = defaultdict(list) # Validate filesystem name if len(bundle.data['name']) > 8: errors['name'].append("Name '%s' too long (max 8 characters)" % bundle.data['name']) if len(bundle.data['name']) < 1: errors['name'].append("Name '%s' too short (min 1 character)" % bundle.data['name']) if bundle.data['name'].find(" ") != -1: errors['name'].append("Name may not contain spaces") # Check volume IDs are present and correct used_volume_ids = set() def check_volume(field, volume_id): # Check we haven't tried to use the same volume twice if volume_id in used_volume_ids: return "Volume ID %s specified for multiple targets!" % volume_id try: # Check the volume exists volume = Volume.objects.get(id=volume_id) try: # Check the volume isn't in use target = ManagedTarget.objects.get(volume=volume) return "Volume with ID %s is already in use by target %s" % ( volume_id, target) except ManagedTarget.DoesNotExist: pass except Volume.DoesNotExist: return "Volume with ID %s not found" % volume_id used_volume_ids.add(volume_id) try: mgt_volume_id = bundle.data['mgt']['volume_id'] error = check_volume('mgt', mgt_volume_id) if error: errors['mgt']['volume_id'].append(error) except KeyError: mgt_volume_id = None try: mgt = ManagedMgs.objects.get(id=bundle.data['mgt']['id']) if mgt.immutable_state: errors['mgt']['id'].append("MGT is unmanaged") try: ManagedFilesystem.objects.get(name=bundle.data['name'], mgs=mgt) errors['mgt']['name'].append( "A file system with name '%s' already exists for this MGT" % bundle.data['name']) except ManagedFilesystem.DoesNotExist: pass except KeyError: errors['mgt']['id'].append( "One of id or volume_id must be set") except ManagedMgs.DoesNotExist: errors['mgt']['id'].append("MGT with ID %s not found" % (bundle.data['mgt']['id'])) for mdt in bundle.data['mdts']: try: mdt_volume_id = mdt['volume_id'] check_volume('mdts', mdt_volume_id) except KeyError: errors['mdts']['volume_id'].append( "volume_id attribute is mandatory for mdt " % mdt['id']) for ost in bundle.data['osts']: try: volume_id = ost['volume_id'] check_volume('osts', volume_id) except KeyError: errors['osts']['volume_id'].append( "volume_id attribute is mandatory for ost " % ost['id']) # If formatting an MGS, check its not on a host already used as an MGS # If this is an MGS, there may not be another MGS on # this host if mgt_volume_id: mgt_volume = Volume.objects.get(id=mgt_volume_id) hosts = [ vn.host for vn in VolumeNode.objects.filter(volume=mgt_volume, use=True) ] conflicting_mgs_count = ManagedTarget.objects.filter( ~Q(managedmgs=None), managedtargetmount__host__in=hosts).count() if conflicting_mgs_count > 0: errors['mgt']['volume_id'].append( "Volume %s cannot be used for MGS (only one MGS is allowed per server)" % mgt_volume.label) def validate_target(klass, target): target_errors = defaultdict(list) volume = Volume.objects.get(id=target['volume_id']) if 'inode_count' in target and 'bytes_per_inode' in target: target_errors['inode_count'].append( "inode_count and bytes_per_inode are mutually exclusive") if 'conf_params' in target: conf_param_errors = conf_param.validate_conf_params( klass, target['conf_params']) if conf_param_errors: # FIXME: not really representing target-specific validations cleanly, # will sort out while fixing HYD-1077. target_errors['conf_params'] = conf_param_errors for setting in ['inode_count', 'inode_size', 'bytes_per_inode']: if setting in target: if target[setting] is not None and not isinstance( target[setting], int): target_errors[setting].append("Must be an integer") # If they specify and inode size and a bytes_per_inode, check the inode fits # within the ratio try: inode_size = target['inode_size'] bytes_per_inode = target['bytes_per_inode'] if inode_size >= bytes_per_inode: target_errors['inode_size'].append( "inode_size must be less than bytes_per_inode") except KeyError: pass # If they specify an inode count, check it will fit on the device try: inode_count = target['inode_count'] except KeyError: # If no inode_count is specified, no need to check it against inode_size pass else: try: inode_size = target['inode_size'] except KeyError: inode_size = { ManagedMgs: 128, ManagedMdt: 512, ManagedOst: 256 }[klass] if inode_size is not None and inode_count is not None: if inode_count * inode_size > volume.size: target_errors['inode_count'].append( "%d %d-byte inodes too large for %s-byte device" % (inode_count, inode_size, volume.size)) return target_errors # Validate generic target settings for attr, targets in targets.items(): for target in targets: klass = ManagedTarget.managed_target_of_type( attr[0:3] ) # We get osts, mdts, mgs so just take the first 3 letters. target_errors = validate_target(klass, target) if target_errors: errors[attr].update(target_errors) conf_param_errors = conf_param.validate_conf_params( ManagedFilesystem, bundle.data['conf_params']) if conf_param_errors: errors['conf_params'] = conf_param_errors def recursive_count(o): """Count the number of non-empty dicts/lists or other objects""" if isinstance(o, dict): c = 0 for v in o.values(): c += recursive_count(v) return c elif isinstance(o, list): c = 0 for v in o: c += recursive_count(v) return c else: return 1 if not recursive_count(errors): errors = {} return errors