void CpuAttributes::show_totals( int dflag ) { ::dprintf( dflag | D_NOHEADER, "slot type %d: " , c_type); if( c_num_cpus == AUTO_CPU ) { ::dprintf( dflag | D_NOHEADER, "Cpus: auto"); } else { ::dprintf( dflag | D_NOHEADER, "Cpus: %d", c_num_cpus); } if( c_phys_mem == AUTO_MEM ) { ::dprintf( dflag | D_NOHEADER, ", Memory: auto" ); } else { ::dprintf( dflag | D_NOHEADER, ", Memory: %d",c_phys_mem ); } if( IS_AUTO_SHARE(c_virt_mem_fraction) ) { ::dprintf( dflag | D_NOHEADER, ", Swap: auto" ); } else { ::dprintf( dflag | D_NOHEADER, ", Swap: %.2f%%", 100*c_virt_mem_fraction ); } if( IS_AUTO_SHARE(c_disk_fraction) ) { ::dprintf( dflag | D_NOHEADER, ", Disk: auto" ); } else { ::dprintf( dflag | D_NOHEADER, ", Disk: %.2f%%", 100*c_disk_fraction ); } for (slotres_map_t::iterator j(c_slotres_map.begin()); j != c_slotres_map.end(); ++j) { if (IS_AUTO_SHARE(j->second)) { ::dprintf(dflag | D_NOHEADER, ", %s: auto", j->first.c_str()); } else { ::dprintf(dflag | D_NOHEADER, ", %s: %d", j->first.c_str(), int(j->second)); } } ::dprintf(dflag | D_NOHEADER, "\n"); }
void CpuAttributes::compute( amask_t how_much ) { double val; if( IS_UPDATE(how_much) && IS_SHARED(how_much) ) { // Shared attributes that we only get a fraction of val = map->virt_mem(); if (!IS_AUTO_SHARE(c_virt_mem_fraction)) { val *= c_virt_mem_fraction; } c_virt_mem = (unsigned long)floor( val ); } if( IS_TIMEOUT(how_much) && !IS_SHARED(how_much) ) { // Dynamic, non-shared attributes we need to actually compute c_condor_load = rip->compute_condor_load(); c_total_disk = sysapi_disk_space(rip->executeDir()); if (IS_UPDATE(how_much)) { dprintf(D_FULLDEBUG, "Total execute space: %lu\n", c_total_disk); } val = c_total_disk * c_disk_fraction; c_disk = (int)floor( val ); if (0 == (int)c_slot_disk) { // only use the 1st compute ignore subsequent. c_slot_disk = c_disk; } } }
bool AvailAttributes::computeAutoShares( CpuAttributes* cap ) { if( cap->c_phys_mem == AUTO_MEM ) { ASSERT( a_phys_mem_auto_count > 0 ); int new_value = a_phys_mem / a_phys_mem_auto_count; if( new_value < 1 ) { return false; } cap->c_slot_mem = cap->c_phys_mem = new_value; } if( IS_AUTO_SHARE(cap->c_virt_mem_fraction) ) { ASSERT( a_virt_mem_auto_count > 0 ); float new_value = a_virt_mem_fraction / a_virt_mem_auto_count; if( new_value <= 0 ) { return false; } cap->c_virt_mem_fraction = new_value; } if( IS_AUTO_SHARE(cap->c_disk_fraction) ) { AvailDiskPartition &partition = GetAvailDiskPartition( cap->c_execute_partition_id ); ASSERT( partition.m_auto_count > 0 ); float new_value = partition.m_disk_fraction / partition.m_auto_count; if( new_value <= 0 ) { return false; } cap->c_disk_fraction = new_value; } for (slotres_map_t::iterator j(a_slotres_map.begin()); j != a_slotres_map.end(); ++j) { if (IS_AUTO_SHARE(cap->c_slotres_map[j->first])) { // replace auto share vals with final values: ASSERT(a_autocnt_map[j->first] > 0); int new_value = int(j->second / a_autocnt_map[j->first]); if (new_value <= 0) return false; cap->c_slotres_map[j->first] = new_value; } // save off slot resource totals once we have final value: cap->c_slottot_map[j->first] = cap->c_slotres_map[j->first]; } return true; }
int compute_local_resource(const float share, const string& rname, const CpuAttributes::slotres_map_t& machres) { CpuAttributes::slotres_map_t::const_iterator f(machres.find(rname)); if (f == machres.end()) { EXCEPT("Resource name %s was not defined in local machine resource table\n", rname.c_str()); } if (IS_AUTO_SHARE(share)) return int(share); if (share > 0) return int(f->second * share); return int(-share); }
CpuAttributes& CpuAttributes::operator-=( CpuAttributes& rhs ) { c_num_cpus -= rhs.c_num_cpus; c_phys_mem -= rhs.c_phys_mem; if (!IS_AUTO_SHARE(rhs.c_virt_mem_fraction) && !IS_AUTO_SHARE(c_virt_mem_fraction)) { c_virt_mem_fraction -= rhs.c_virt_mem_fraction; } if (!IS_AUTO_SHARE(rhs.c_disk_fraction) && !IS_AUTO_SHARE(c_disk_fraction)) { c_disk_fraction -= rhs.c_disk_fraction; } for (slotres_map_t::iterator j(c_slotres_map.begin()); j != c_slotres_map.end(); ++j) { j->second -= rhs.c_slotres_map[j->first]; } compute( A_TIMEOUT | A_UPDATE ); // Re-compute return *this; }
/* Generally speaking, we want to round down for fractional amounts of a CPU. However, we never want to advertise less than 1. Plus, if the share in question is negative, it means it's an absolute value, not a fraction. */ int compute_cpus( int num_cpus, float share ) { int cpus; if( IS_AUTO_SHARE(share) ) { // Currently, "auto" for cpus just means 1 cpu per slot. return 1; } if( share > 0 ) { cpus = (int)floor( share * num_cpus ); } else { cpus = (int)floor( -share ); } return cpus; }
/* Generally speaking, we want to round down for fractional amounts of physical memory. However, we never want to advertise less than 1. Plus, if the share in question is negative, it means it's an absolute value, not a fraction. */ int compute_phys_mem( MachAttributes *m_attr, float share ) { int phys_mem; if( IS_AUTO_SHARE(share) ) { // This will be replaced later with an even share of whatever // memory is left over. return AUTO_MEM; } if( share > 0 ) { phys_mem = (int)floor( share * m_attr->phys_mem() ); } else { phys_mem = (int)floor( -share ); } return phys_mem; }
bool AvailAttributes::decrement( CpuAttributes* cap ) { int new_cpus, new_phys_mem; float new_virt_mem, new_disk, floor = -0.000001f; new_cpus = a_num_cpus - cap->c_num_cpus; new_phys_mem = a_phys_mem; if( cap->c_phys_mem != AUTO_MEM ) { new_phys_mem -= cap->c_phys_mem; } new_virt_mem = a_virt_mem_fraction; if( !IS_AUTO_SHARE(cap->c_virt_mem_fraction) ) { new_virt_mem -= cap->c_virt_mem_fraction; } AvailDiskPartition &partition = GetAvailDiskPartition( cap->executePartitionID() ); new_disk = partition.m_disk_fraction; if( !IS_AUTO_SHARE(cap->c_disk_fraction) ) { new_disk -= cap->c_disk_fraction; } bool resfloor = false; slotres_map_t new_res(a_slotres_map); for (slotres_map_t::iterator j(new_res.begin()); j != new_res.end(); ++j) { if (!IS_AUTO_SHARE(cap->c_slotres_map[j->first])) { j->second -= cap->c_slotres_map[j->first]; } if (j->second < floor) { resfloor = true; } } if (resfloor || new_cpus < floor || new_phys_mem < floor || new_virt_mem < floor || new_disk < floor) { return false; } a_num_cpus = new_cpus; a_phys_mem = new_phys_mem; if( cap->c_phys_mem == AUTO_MEM ) { a_phys_mem_auto_count += 1; } a_virt_mem_fraction = new_virt_mem; if( IS_AUTO_SHARE(cap->c_virt_mem_fraction) ) { a_virt_mem_auto_count += 1; } partition.m_disk_fraction = new_disk; if( IS_AUTO_SHARE(cap->c_disk_fraction) ) { partition.m_auto_count += 1; } for (slotres_map_t::iterator j(a_slotres_map.begin()); j != a_slotres_map.end(); ++j) { j->second = new_res[j->first]; if (IS_AUTO_SHARE(cap->c_slotres_map[j->first])) { a_autocnt_map[j->first] += 1; } } return true; }
CpuAttributes* buildSlot( MachAttributes *m_attr, int slot_id, StringList* list, int type, bool except ) { typedef CpuAttributes::slotres_map_t slotres_map_t; int cpus = UNSET_SHARE; int ram = UNSET_SHARE; float disk = UNSET_SHARE; float swap = UNSET_SHARE; float share; slotres_map_t slotres; float default_share = AUTO_SHARE; MyString execute_dir, partition_id; GetConfigExecuteDir( slot_id, &execute_dir, &partition_id ); if ( list == NULL) { // give everything the default share and return cpus = compute_cpus( m_attr->num_cpus(), default_share ); ram = compute_phys_mem( m_attr, default_share ); swap = default_share; disk = default_share; for (slotres_map_t::const_iterator j(m_attr->machres().begin()); j != m_attr->machres().end(); ++j) { slotres[j->first] = default_share; } return new CpuAttributes( m_attr, type, cpus, ram, swap, disk, slotres, execute_dir, partition_id ); } // For this parsing code, deal with the following example // string list: // "c=1, r=25%, d=1/4, s=25%" // There may be a bare number (no equals sign) that specifies // the default share for any items not explicitly defined. Example: // "c=1, 25%" for (slotres_map_t::const_iterator j(m_attr->machres().begin()); j != m_attr->machres().end(); ++j) { slotres[j->first] = AUTO_RES; } list->rewind(); while (char* attrp = list->next()) { string attr_expr = attrp; string::size_type eqpos = attr_expr.find('='); if (string::npos == eqpos) { // There's no = in this description, it must be one // percentage or fraction for all attributes. // For example "1/4" or "25%". So, we can just parse // it as a percentage and use that for everything. default_share = parse_value(attr_expr.c_str(), type, except); if( default_share <= 0 && !IS_AUTO_SHARE(default_share) ) { dprintf( D_ALWAYS, "ERROR: Bad description of slot type %d: ", type ); dprintf( D_ALWAYS | D_NOHEADER, "\"%s\" is invalid.\n", attr_expr.c_str() ); dprintf( D_ALWAYS | D_NOHEADER, "\tYou must specify a percentage (like \"25%%\"), " ); dprintf( D_ALWAYS | D_NOHEADER, "a fraction (like \"1/4\"),\n" ); dprintf( D_ALWAYS | D_NOHEADER, "\tor list all attributes (like \"c=1, r=25%%, s=25%%, d=25%%\").\n" ); dprintf( D_ALWAYS | D_NOHEADER, "\tSee the manual for details.\n" ); if( except ) { DC_Exit( 4 ); } else { return NULL; } } continue; } // If we're still here, this is part of a string that // lists out seperate attributes and the share for each one. // Get the value for this attribute. It'll either be a // percentage, or it'll be a distinct value (in which // case, parse_value() will return negative. string val = attr_expr.substr(1+eqpos); if (val.empty()) { dprintf(D_ALWAYS, "Can't parse attribute \"%s\" in description of slot type %d\n", attr_expr.c_str(), type); if( except ) { DC_Exit( 4 ); } else { return NULL; } } share = parse_value(val.c_str(), type, except); // Figure out what attribute we're dealing with. string attr = attr_expr.substr(0, eqpos); slotres_map_t::const_iterator f(m_attr->machres().find(attr)); if (f != m_attr->machres().end()) { slotres[f->first] = compute_local_resource(share, attr, m_attr->machres()); continue; } switch( tolower(attr[0]) ) { case 'c': cpus = compute_cpus( m_attr->num_cpus(), share ); break; case 'r': case 'm': ram = compute_phys_mem( m_attr, share ); break; case 's': case 'v': if( share >= 0 || IS_AUTO_SHARE(share) ) { swap = share; } else { dprintf( D_ALWAYS, "You must specify a percent or fraction for swap in slot type %d\n", type ); if( except ) { DC_Exit( 4 ); } else { return NULL; } } break; case 'd': if( share >= 0 || IS_AUTO_SHARE(share) ) { disk = share; } else { dprintf( D_ALWAYS, "You must specify a percent or fraction for disk in slot type %d\n", type ); if( except ) { DC_Exit( 4 ); } else { return NULL; } } break; default: dprintf( D_ALWAYS, "Unknown attribute \"%s\" in slot type %d\n", attr.c_str(), type ); if( except ) { DC_Exit( 4 ); } else { return NULL; } break; } } // We're all done parsing the string. Any attribute not // listed will get the default share. if (IS_UNSET_SHARE(cpus)) { cpus = compute_cpus( m_attr->num_cpus(), default_share ); } if (IS_UNSET_SHARE(ram)) { ram = compute_phys_mem( m_attr, default_share ); } if (IS_UNSET_SHARE(swap)) { swap = default_share; } if (IS_UNSET_SHARE(disk)) { disk = default_share; } for (slotres_map_t::iterator j(slotres.begin()); j != slotres.end(); ++j) { if (int(j->second) == AUTO_RES) { j->second = compute_local_resource(default_share, j->first, m_attr->machres()); } } // Now create the object. return new CpuAttributes( m_attr, type, cpus, ram, swap, disk, slotres, execute_dir, partition_id ); }
CpuAttributes* buildSlot( MachAttributes *m_attr, int slot_id, StringList* list, int type, bool except ) { typedef CpuAttributes::slotres_map_t slotres_map_t; double cpus = UNSET_SHARE; int ram = UNSET_SHARE; // this is in MB so an int is enough double disk_fraction = UNSET_SHARE; double swap_fraction = UNSET_SHARE; slotres_map_t slotres; special_share_t default_share_special = SPECIAL_SHARE_AUTO; double default_share = AUTO_SHARE; bool allow_fractional_cpu = false; MyString execute_dir, partition_id; GetConfigExecuteDir( slot_id, &execute_dir, &partition_id ); if ( list == NULL) { // give everything the default share and return cpus = compute_local_resource(m_attr->num_cpus(), default_share, 1.0); ram = compute_local_resource(m_attr->phys_mem(), default_share, 1); for (slotres_map_t::const_iterator j(m_attr->machres().begin()); j != m_attr->machres().end(); ++j) { slotres[j->first] = default_share; } return new CpuAttributes( m_attr, type, cpus, ram, AUTO_SHARE, AUTO_SHARE, slotres, execute_dir, partition_id ); } // For this parsing code, deal with the following example // string list: // "c=1, r=25%, d=1/4, s=25%" // There may be a bare number (no equals sign) that specifies // the default share for any items not explicitly defined. Example: // "c=1, 25%" for (slotres_map_t::const_iterator j(m_attr->machres().begin()); j != m_attr->machres().end(); ++j) { slotres[j->first] = UNSET_SHARE; } list->rewind(); while (char* attrp = list->next()) { string attr_expr = attrp; string::size_type eqpos = attr_expr.find('='); if (string::npos == eqpos) { // There's no = in this description, it must be one // percentage or fraction for all attributes. // For example "1/4" or "25%". So, we can just parse // it as a percentage and use that for everything. default_share = parse_share_value(attr_expr.c_str(), type, except, default_share_special); if (default_share_special == SPECIAL_SHARE_NONE && (default_share < -1 || default_share > 0)) { dprintf( D_ALWAYS, "ERROR: Bad description of slot type %d: " "\"%s\" is invalid.\n" "\tYou must specify a percentage (like \"25%%\"), " "a fraction (like \"1/4\"),\n" "\tor list all attributes (like \"c=1, r=25%%, s=25%%, d=25%%\").\n" "\tSee the manual for details.\n", type, attr_expr.c_str()); if( except ) { DC_Exit( 4 ); } else { return NULL; } } if (default_share_special == SPECIAL_SHARE_MINIMAL) allow_fractional_cpu = true; continue; } // If we're still here, this is part of a string that // lists out seperate attributes and the share for each one. // Get the value for this attribute. It'll either be a // percentage, or it'll be a distinct value (in which // case, parse_share_value() will return negative. string val = attr_expr.substr(1+eqpos); if (val.empty()) { dprintf(D_ALWAYS, "Can't parse attribute \"%s\" in description of slot type %d\n", attr_expr.c_str(), type); if( except ) { DC_Exit( 4 ); } else { return NULL; } } special_share_t share_special = SPECIAL_SHARE_NONE; double share = parse_share_value(val.c_str(), type, except, share_special); // Figure out what attribute we're dealing with. string attr = attr_expr.substr(0, eqpos); trim(attr); slotres_map_t::const_iterator f(m_attr->machres().find(attr)); if (f != m_attr->machres().end()) { slotres[f->first] = compute_local_resource(f->second, share); continue; } // before 8.1.4, only the first character of standard resource types // was looked at (case insensitively). This prevented us from detecting // typos in resource type names that start with c,r,m,s,v or d. // This code now compares the whole resource type name // But to preserve (most) of the pre 8.1.4 we will tolerate // any case-insensitive substring match of cpus, ram, memory, swap, virtualmemory, or disk int cattr = int(attr.length()); if (MATCH == strncasecmp(attr.c_str(), "cpus", cattr)) { double lower_bound = (allow_fractional_cpu) ? 0.0 : 1.0; cpus = compute_local_resource(m_attr->num_cpus(), share, lower_bound); } else if ( MATCH == strncasecmp(attr.c_str(), "ram", cattr) || MATCH == strncasecmp(attr.c_str(), "memory", cattr)) { ram = compute_local_resource(m_attr->phys_mem(), share, 1); } else if ( MATCH == strncasecmp(attr.c_str(), "swap", cattr) || MATCH == strncasecmp(attr.c_str(), "virtualmemory", cattr)) { // for now, we can only tolerate swap expressed a 'auto' or a proportion // because we don't know how much disk is available until later. if (share <= 0 || IS_AUTO_SHARE(share)) { swap_fraction = compute_local_resource(1.0, share); } else { dprintf( D_ALWAYS, "You must specify a percent or fraction for swap in slot type %d\n", type ); if( except ) { DC_Exit( 4 ); } else { return NULL; } } } else if ( MATCH == strncasecmp(attr.c_str(), "disk", cattr)) { // for now, we can only tolerate swap expressed a 'auto' or a proportion // because we don't know how much disk is available until later. if (share <= 0 || IS_AUTO_SHARE(share)) { disk_fraction = compute_local_resource(1.0, share); } else { dprintf( D_ALWAYS, "You must specify a percent or fraction for disk in slot type %d\n", type ); if( except ) { DC_Exit( 4 ); } else { return NULL; } } } else { // at this point in the code a non-zero amount of the resource is requested only if share > 0. // requesting non-zero amounts of *unknown* resources is a fatal error // but requesting 0 or a share of the remainder isn't. this makes it possible // to have a common config between machines that can be used to express things // like - "if this machine has GPUs, I don't want this slot to get any".. if (share > 0) { dprintf( D_ALWAYS, "Unknown attribute \"%s\" in slot type %d\n", attr.c_str(), type); if( except ) { DC_Exit( 4 ); } else { return NULL; } } else { dprintf( D_ALWAYS | D_FULLDEBUG, "Unknown attribute \"%s\" in slot type %d, no resources will be allocated\n", attr.c_str(), type); } } } int std_resource_lower_bound = (allow_fractional_cpu) ? 0 : 1; // We're all done parsing the string. Any attribute not // listed will get the default share. if (IS_UNSET_SHARE(cpus)) { cpus = compute_local_resource(m_attr->num_cpus(), default_share, (double)std_resource_lower_bound); } if (IS_UNSET_SHARE(ram)) { ram = compute_local_resource(m_attr->phys_mem(), default_share, std_resource_lower_bound); } if (IS_UNSET_SHARE(swap_fraction)) { swap_fraction = compute_local_resource(1.0, default_share); } if (IS_UNSET_SHARE(disk_fraction)) { disk_fraction = compute_local_resource(1.0, default_share); } for (slotres_map_t::iterator j(slotres.begin()); j != slotres.end(); ++j) { if (IS_UNSET_SHARE(j->second)) { slotres_map_t::const_iterator f(m_attr->machres().find(j->first)); // it shouldn't be possible to get here with keys in slotres that aren't in machres // but if it should happen it's a benign error, just allocate 0 of the unknown resource. double avail = (f != m_attr->machres().end()) ? f->second : 0; j->second = compute_local_resource(avail, default_share); } } // Now create the object. return new CpuAttributes( m_attr, type, cpus, ram, swap_fraction, disk_fraction, slotres, execute_dir, partition_id ); }
static t compute_local_resource(t num_res, double share, t min_res = 0) { if (IS_AUTO_SHARE(share)) return AUTO_RES; t res = (share < 0) ? t(-share * num_res) : t(share); return MAX(res, min_res); }