def _get_rt(self, pid): scheduler = schedutils.get_scheduler(pid) sched_str = schedutils.schedstr(scheduler) priority = schedutils.get_priority(pid) log.debug("Read scheduler policy '%s' and priority '%d' of PID '%d'" % (sched_str, priority, pid)) return (scheduler, priority)
def get_kthread_sched_tunings(proc = None): if not proc: proc = procfs.pidstats() kthreads = {} for pid in proc.keys(): name = proc[pid]["stat"]["comm"] # Trying to set the priority of the migration threads will # fail, at least on 3.6.0-rc1 and doesn't make sense anyway # and this function is only used to save those priorities # to reset them using tools like rtctl, skip those to # avoid sched_setscheduler/chrt to fail if iskthread(pid) and not name.startswith("migration/"): rtprio = int(proc[pid]["stat"]["rt_priority"]) try: policy = schedutils.get_scheduler(pid) affinity = schedutils.get_affinity(pid) except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError if e[0] == 3: continue raise e percpu = iskthread(pid) and \ proc.is_bound_to_cpu(pid) kthreads[name] = sched_tunings(name, pid, policy, rtprio, affinity, percpu) return kthreads
def set_irq_columns(self, iter, irq, irq_info, nics): new_value = [None] * self.nr_columns users = tuna.get_irq_users(self.irqs, irq, nics) if self.has_threaded_irqs: irq_re = tuna.threaded_irq_re(irq) pids = self.ps.find_by_regex(irq_re) if pids: pid = pids[0] prio = int(self.ps[pid]["stat"]["rt_priority"]) sched = schedutils.schedstr(schedutils.get_scheduler(pid))[6:] else: sched = "" pid = -1 prio = -1 new_value[self.COL_PID] = pid new_value[self.COL_POL] = sched new_value[self.COL_PRI] = prio new_value[self.COL_NUM] = irq new_value[self.COL_AFF] = tuna.get_irq_affinity_text(self.irqs, irq) new_value[self.COL_EVENTS] = reduce(lambda a, b: a + b, irq_info["cpu"]) new_value[self.COL_USERS] = ",".join(users) gui.set_store_columns(self.list_store, iter, new_value)
def ps_show_thread(self, pid, affect_children, ps, has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups): try: affinity = self.format_affinity(schedutils.get_affinity(pid)) except ( SystemError, OSError ) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError if e[0] == 3: return raise e sched = schedutils.schedstr(schedutils.get_scheduler(pid))[6:] rtprio = int(ps[pid]["stat"]["rt_priority"]) cgout = ps[pid]["cgroups"] cmd = ps[pid]["stat"]["comm"] users = "" if tuna.is_irq_thread(cmd): try: if not self.irqs: self.irqs = procfs.interrupts() if cmd[:4] == "IRQ-": users = self.irqs[tuna.irq_thread_number(cmd)]["users"] for u in users: if u in self.get_nics(): users[users.index( u)] = "%s(%s)" % (u, ethtool.get_module(u)) users = ",".join(users) else: u = cmd[cmd.find('-') + 1:] if u in self.get_nics(): users = ethtool.get_module(u) except: users = "Not found in /proc/interrupts!" ctxt_switch_info = "" if has_ctxt_switch_info: voluntary_ctxt_switches = int( ps[pid]["status"]["voluntary_ctxt_switches"]) nonvoluntary_ctxt_switches = int( ps[pid]["status"]["nonvoluntary_ctxt_switches"]) ctxt_switch_info = " %9d %12s" % (voluntary_ctxt_switches, nonvoluntary_ctxt_switches) if affect_children: print " %-5d " % pid, else: print " %-5d" % pid, print "%6s %5d %8s%s %15s %s" % (sched, rtprio, affinity, ctxt_switch_info, cmd, users), if cgroups: print " %9s" % cgout, print "" if sock_inodes: self.ps_show_sockets(pid, ps, sock_inodes, sock_inode_re, affect_children and 3 or 4) if affect_children and ps[pid].has_key("threads"): for tid in ps[pid]["threads"].keys(): self.ps_show_thread(tid, False, ps[pid]["threads"], has_ctxt_switch_info, sock_inodes, sock_inode_re)
def show_settings(pid): policy = schedutils.get_scheduler(pid) spolicy = schedutils.schedstr(policy) rtprio = schedutils.get_priority(pid) reset_on_fork = "" if policy & schedutils.SCHED_RESET_ON_FORK: reset_on_fork = "|SCHED_RESET_ON_FORK" print '''pid %d's current scheduling policy: %s%s pid %d's current scheduling priority: %d''' % (pid, spolicy, reset_on_fork, pid, rtprio)
def show(ps, cpuinfo, irqs): ps_list = [] for pid in ps.keys(): if schedutils.get_scheduler(pid) == 0: continue ps_list.append(pid) ps_list.sort() nics = ethtool.get_active_devices() for pid in ps_list: thread_affinity_list = schedutils.get_affinity(pid) if len(thread_affinity_list) <= 4: thread_affinity = ",".join(str(a) for a in thread_affinity_list) else: thread_affinity = ",".join( str(hex(a)) for a in procfs.hexbitmask( schedutils.get_affinity(pid), cpuinfo.nr_cpus)) sched = schedutils.schedstr(schedutils.get_scheduler(pid))[6:] rtprio = int(ps[pid]["stat"]["rt_priority"]) cmd = ps[pid]["stat"]["comm"] users = "" if cmd[:4] == "IRQ-": try: users = irqs[cmd[4:]]["users"] for u in users: if u in nics: users[users.index( u)] = "%s(%s)" % (u, ethtool.get_module(u)) users = ",".join(users) except: users = "Not found in /proc/interrupts!" try: voluntary_ctxt_switches = int( ps[pid]["status"]["voluntary_ctxt_switches"]) nonvoluntary_ctxt_switches = int( ps[pid]["status"]["nonvoluntary_ctxt_switches"]) except: voluntary_ctxt_switches = -1 nonvoluntary_ctxt_switches = -1 print "%5d %6s %5d %8s %9d %12s %15s %s" % ( pid, sched, rtprio, thread_affinity, voluntary_ctxt_switches, nonvoluntary_ctxt_switches, cmd, users)
def ps_show_thread(pid, affect_children, ps, has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups): global irqs try: affinity = format_affinity(schedutils.get_affinity(pid)) except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError if e[0] == 3: return raise e sched = schedutils.schedstr(schedutils.get_scheduler(pid))[6:] rtprio = int(ps[pid]["stat"]["rt_priority"]) cgout = ps[pid]["cgroups"] cmd = ps[pid]["stat"]["comm"] users = "" if tuna.is_irq_thread(cmd): try: if not irqs: irqs = procfs.interrupts() if cmd[:4] == "IRQ-": users = irqs[tuna.irq_thread_number(cmd)]["users"] for u in users: if u in get_nics(): users[users.index(u)] = "%s(%s)" % (u, ethtool.get_module(u)) users = ",".join(users) else: u = cmd[cmd.find('-') + 1:] if u in get_nics(): users = ethtool.get_module(u) except: users = "Not found in /proc/interrupts!" ctxt_switch_info = "" if has_ctxt_switch_info: voluntary_ctxt_switches = int(ps[pid]["status"]["voluntary_ctxt_switches"]) nonvoluntary_ctxt_switches = int(ps[pid]["status"]["nonvoluntary_ctxt_switches"]) ctxt_switch_info = " %9d %12s" % (voluntary_ctxt_switches, nonvoluntary_ctxt_switches) if affect_children: print " %-5d " % pid, else: print " %-5d" % pid, print "%6s %5d %8s%s %15s %s" % (sched, rtprio, affinity, ctxt_switch_info, cmd, users), if cgroups: print " %9s" % cgout, print "" if sock_inodes: ps_show_sockets(pid, ps, sock_inodes, sock_inode_re, affect_children and 3 or 4) if affect_children and ps[pid].has_key("threads"): for tid in ps[pid]["threads"].keys(): ps_show_thread(tid, False, ps[pid]["threads"], has_ctxt_switch_info, sock_inodes, sock_inode_re, cgroups)
def show(ps, cpuinfo, irqs): ps_list = [] for pid in ps.keys(): if schedutils.get_scheduler(pid) == 0: continue ps_list.append(pid) ps_list.sort() nics = ethtool.get_active_devices() for pid in ps_list: thread_affinity_list = schedutils.get_affinity(pid) if len(thread_affinity_list) <= 4: thread_affinity = ",".join(str(a) for a in thread_affinity_list) else: thread_affinity = ",".join(str(hex(a)) for a in procfs.hexbitmask(schedutils.get_affinity(pid), cpuinfo.nr_cpus)) sched = schedutils.schedstr(schedutils.get_scheduler(pid))[6:] rtprio = int(ps[pid]["stat"]["rt_priority"]) cmd = ps[pid]["stat"]["comm"] users = "" if cmd[:4] == "IRQ-": try: users = irqs[cmd[4:]]["users"] for u in users: if u in nics: users[users.index(u)] = "%s(%s)" % (u, ethtool.get_module(u)) users = ",".join(users) except: users = "Not found in /proc/interrupts!" try: voluntary_ctxt_switches = int(ps[pid]["status"]["voluntary_ctxt_switches"]) nonvoluntary_ctxt_switches = int(ps[pid]["status"]["nonvoluntary_ctxt_switches"]) except: voluntary_ctxt_switches = -1 nonvoluntary_ctxt_switches = -1 print "%5d %6s %5d %8s %9d %12s %15s %s" % (pid, sched, rtprio, thread_affinity, voluntary_ctxt_switches, nonvoluntary_ctxt_switches, cmd, users)
def __init__(self, ps, pid, pid_info, nr_cpus, gladefile): self.ps = ps self.pid = pid self.pid_info = pid_info self.nr_cpus = nr_cpus self.window = gtk.glade.XML(gladefile, "set_process_attributes", "tuna") self.dialog = self.window.get_widget("set_process_attributes") pixbuf = self.dialog.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_SMALL_TOOLBAR) self.dialog.set_icon(pixbuf) event_handlers = { "on_cmdline_regex_changed": self.on_cmdline_regex_changed, "on_affinity_text_changed": self.on_affinity_text_changed, "on_sched_policy_combo_changed": self.on_sched_policy_combo_changed, "on_command_regex_clicked": self.on_command_regex_clicked, "on_all_these_threads_clicked": self.on_all_these_threads_clicked, "on_just_this_thread_clicked": self.on_just_this_thread_clicked } self.window.signal_autoconnect(event_handlers) self.sched_pri = self.window.get_widget("sched_pri_spin") self.sched_policy = self.window.get_widget("sched_policy_combo") self.regex_edit = self.window.get_widget("cmdline_regex") self.affinity = self.window.get_widget("affinity_text") self.just_this_thread = self.window.get_widget("just_this_thread") self.all_these_threads = self.window.get_widget("all_these_threads") processes = self.window.get_widget("matching_process_list") self.sched_pri.set_value(int(pid_info["stat"]["rt_priority"])) cmdline_regex = procfs.process_cmdline(pid_info) self.affinity_text = tuna.list_to_cpustring( schedutils.get_affinity(pid)) self.affinity.set_text(self.affinity_text) self.create_matching_process_model(processes) self.create_policy_model(self.sched_policy) self.sched_policy.set_active(schedutils.get_scheduler(pid)) self.regex_edit.set_text(cmdline_regex) self.just_this_thread.set_active(True) self.regex_edit.set_sensitive(False) if not ps.has_key(pid) or not ps[pid].has_key("threads"): self.all_these_threads.hide() self.on_just_this_thread_clicked(None)
def set_thread_columns(self, iter, tid, thread_info): new_value = [ None ] * self.nr_columns new_value[self.COL_PRI] = int(thread_info["stat"]["rt_priority"]) new_value[self.COL_POL] = schedutils.schedstr(schedutils.get_scheduler(tid))[6:] thread_affinity_list = schedutils.get_affinity(tid) new_value[self.COL_PID] = tid new_value[self.COL_AFF] = tuna.list_to_cpustring(thread_affinity_list) try: new_value[self.COL_VOLCTXT] = int(thread_info["status"]["voluntary_ctxt_switches"]) new_value[self.COL_NONVOLCTXT] = int(thread_info["status"]["nonvoluntary_ctxt_switches"]) new_value[self.COL_CGROUP] = thread_info["cgroups"] except: pass new_value[self.COL_CMDLINE] = procfs.process_cmdline(thread_info) gui.set_store_columns(self.tree_store, iter, new_value)
def __init__(self, irqs, ps, irq, gladefile): self.irqs = irqs self.ps = ps self.irq = irq self.window = gtk.glade.XML(gladefile, "set_irq_attributes", "tuna") self.dialog = self.window.get_widget("set_irq_attributes") pixbuf = self.dialog.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_SMALL_TOOLBAR) self.dialog.set_icon(pixbuf) event_handlers = { "on_irq_affinity_text_changed": self.on_irq_affinity_text_changed, "on_sched_policy_combo_changed": self.on_sched_policy_combo_changed } self.window.signal_autoconnect(event_handlers) self.sched_pri = self.window.get_widget("irq_pri_spinbutton") self.sched_policy = self.window.get_widget("irq_policy_combobox") self.affinity = self.window.get_widget("irq_affinity_text") text = self.window.get_widget("irq_text") users = tuna.get_irq_users(irqs, irq) self.affinity_text = tuna.get_irq_affinity_text(irqs, irq) irq_re = tuna.threaded_irq_re(irq) pids = self.ps.find_by_regex(irq_re) if pids: pid = pids[0] prio = int(ps[pid]["stat"]["rt_priority"]) self.create_policy_model(self.sched_policy) self.sched_policy.set_active(schedutils.get_scheduler(pid)) self.sched_pri.set_value(prio) text.set_markup("IRQ <b>%u</b> (PID <b>%u</b>), pri <b>%u</b>, aff <b>%s</b>, <tt><b>%s</b></tt>" % \ ( irq, pid, prio, self.affinity_text, ",".join(users))) else: self.sched_pri.set_sensitive(False) self.sched_policy.set_sensitive(False) text.set_markup("IRQ <b>%u</b>, aff <b>%s</b>, <tt><b>%s</b></tt>" % \ ( irq, self.affinity_text, ",".join(users))) self.affinity.set_text(self.affinity_text)
def __init__(self, irqs, ps, irq, gladefile): self.irqs = irqs self.ps = ps self.irq = irq self.window = gtk.glade.XML(gladefile, "set_irq_attributes", "tuna") self.dialog = self.window.get_widget("set_irq_attributes") pixbuf = self.dialog.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_SMALL_TOOLBAR) self.dialog.set_icon(pixbuf) event_handlers = { "on_irq_affinity_text_changed" : self.on_irq_affinity_text_changed, "on_sched_policy_combo_changed": self.on_sched_policy_combo_changed } self.window.signal_autoconnect(event_handlers) self.sched_pri = self.window.get_widget("irq_pri_spinbutton") self.sched_policy = self.window.get_widget("irq_policy_combobox") self.affinity = self.window.get_widget("irq_affinity_text") text = self.window.get_widget("irq_text") users = tuna.get_irq_users(irqs, irq) self.affinity_text = tuna.get_irq_affinity_text(irqs, irq) irq_re = tuna.threaded_irq_re(irq) pids = self.ps.find_by_regex(irq_re) if pids: pid = pids[0] prio = int(ps[pid]["stat"]["rt_priority"]) self.create_policy_model(self.sched_policy) self.sched_policy.set_active(schedutils.get_scheduler(pid)) self.sched_pri.set_value(prio) text.set_markup("IRQ <b>%u</b> (PID <b>%u</b>), pri <b>%u</b>, aff <b>%s</b>, <tt><b>%s</b></tt>" % \ ( irq, pid, prio, self.affinity_text, ",".join(users))) else: self.sched_pri.set_sensitive(False) self.sched_policy.set_sensitive(False) text.set_markup("IRQ <b>%u</b>, aff <b>%s</b>, <tt><b>%s</b></tt>" % \ ( irq, self.affinity_text, ",".join(users))) self.affinity.set_text(self.affinity_text)
def __init__(self, ps, pid, pid_info, nr_cpus, gladefile): self.ps = ps self.pid = pid self.pid_info = pid_info self.nr_cpus = nr_cpus self.window = gtk.glade.XML(gladefile, "set_process_attributes", "tuna") self.dialog = self.window.get_widget("set_process_attributes") pixbuf = self.dialog.render_icon(gtk.STOCK_PREFERENCES, gtk.ICON_SIZE_SMALL_TOOLBAR) self.dialog.set_icon(pixbuf) event_handlers = { "on_cmdline_regex_changed" : self.on_cmdline_regex_changed, "on_affinity_text_changed" : self.on_affinity_text_changed, "on_sched_policy_combo_changed" : self.on_sched_policy_combo_changed, "on_command_regex_clicked" : self.on_command_regex_clicked, "on_all_these_threads_clicked" : self.on_all_these_threads_clicked, "on_just_this_thread_clicked" : self.on_just_this_thread_clicked } self.window.signal_autoconnect(event_handlers) self.sched_pri = self.window.get_widget("sched_pri_spin") self.sched_policy = self.window.get_widget("sched_policy_combo") self.regex_edit = self.window.get_widget("cmdline_regex") self.affinity = self.window.get_widget("affinity_text") self.just_this_thread = self.window.get_widget("just_this_thread") self.all_these_threads = self.window.get_widget("all_these_threads") processes = self.window.get_widget("matching_process_list") self.sched_pri.set_value(int(pid_info["stat"]["rt_priority"])) cmdline_regex = procfs.process_cmdline(pid_info) self.affinity_text = tuna.list_to_cpustring(schedutils.get_affinity(pid)) self.affinity.set_text(self.affinity_text) self.create_matching_process_model(processes) self.create_policy_model(self.sched_policy) self.sched_policy.set_active(schedutils.get_scheduler(pid)) self.regex_edit.set_text(cmdline_regex) self.just_this_thread.set_active(True) self.regex_edit.set_sensitive(False) if not ps.has_key(pid) or not ps[pid].has_key("threads"): self.all_these_threads.hide() self.on_just_this_thread_clicked(None)
def set_thread_columns(self, iter, tid, thread_info): new_value = [None] * self.nr_columns new_value[self.COL_PRI] = int(thread_info["stat"]["rt_priority"]) new_value[self.COL_POL] = schedutils.schedstr( schedutils.get_scheduler(tid))[6:] thread_affinity_list = schedutils.get_affinity(tid) new_value[self.COL_PID] = tid new_value[self.COL_AFF] = tuna.list_to_cpustring(thread_affinity_list) try: new_value[self.COL_VOLCTXT] = int( thread_info["status"]["voluntary_ctxt_switches"]) new_value[self.COL_NONVOLCTXT] = int( thread_info["status"]["nonvoluntary_ctxt_switches"]) new_value[self.COL_CGROUP] = thread_info["cgroups"] except: pass new_value[self.COL_CMDLINE] = procfs.process_cmdline(thread_info) gui.set_store_columns(self.tree_store, iter, new_value)
def set_irq_columns(self, iter, irq, irq_info, nics): new_value = [ None ] * self.nr_columns users = tuna.get_irq_users(self.irqs, irq, nics) if self.has_threaded_irqs: irq_re = tuna.threaded_irq_re(irq) pids = self.ps.find_by_regex(irq_re) if pids: pid = pids[0] prio = int(self.ps[pid]["stat"]["rt_priority"]) sched = schedutils.schedstr(schedutils.get_scheduler(pid))[6:] else: sched = "" pid = -1 prio = -1 new_value[self.COL_PID] = pid new_value[self.COL_POL] = sched new_value[self.COL_PRI] = prio new_value[self.COL_NUM] = irq new_value[self.COL_AFF] = tuna.get_irq_affinity_text(self.irqs, irq) new_value[self.COL_EVENTS] = reduce(lambda a, b: a + b, irq_info["cpu"]) new_value[self.COL_USERS] = ",".join(users) gui.set_store_columns(self.list_store, iter, new_value)
def get_scheduler(self, pid): return schedutils.get_scheduler(pid)
def thread_set_priority(tid, policy, rtprio): if not policy and policy != 0: policy = schedutils.get_scheduler(tid) schedutils.set_scheduler(tid, policy, rtprio)
def thread_set_attributes(pid_info, new_policy, new_prio, new_affinity, nr_cpus): pid = pid_info.pid changed = False curr_policy = schedutils.get_scheduler(pid) curr_prio = int(pid_info["stat"]["rt_priority"]) if new_policy == schedutils.SCHED_OTHER: new_prio = 0 if curr_policy != new_policy or curr_prio != new_prio: try: schedutils.set_scheduler(pid, new_policy, new_prio) except: dialog = gtk.MessageDialog( None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, _("Invalid parameters!")) dialog.run() dialog.destroy() return False curr_policy = schedutils.get_scheduler(pid) if curr_policy != new_policy: print _("couldn't change pid %(pid)d from %(cpol)s(%(cpri)d) to %(npol)s(%(npri)d)!") % \ { 'pid': pid, 'cpol': schedutils.schedstr(curr_policy), 'cpri': curr_prio, 'npol': schedutils.schedstr(new_policy), 'npri': new_prio} else: changed = True try: curr_affinity = schedutils.get_affinity(pid) except ( SystemError, OSError ) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError if e[0] == 3: return False raise e try: new_affinity = [int(a) for a in new_affinity.split(",")] except: try: new_affinity = tuna.cpustring_to_list(new_affinity) except: new_affinity = procfs.bitmasklist(new_affinity, nr_cpus) new_affinity.sort() if curr_affinity != new_affinity: try: schedutils.set_affinity(pid, new_affinity) except: return invalid_affinity() try: curr_affinity = schedutils.get_affinity(pid) except ( SystemError, OSError ) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError if e[0] == 3: return False raise e if curr_affinity != new_affinity: print _("couldn't change pid %(pid)d from %(caff)s to %(naff)s!") % \ { 'pid':pid, 'caff':curr_affinity, 'naff':new_affinity } else: changed = True return changed
def thread_set_attributes(pid_info, new_policy, new_prio, new_affinity, nr_cpus): pid = pid_info.pid changed = False curr_policy = schedutils.get_scheduler(pid) curr_prio = int(pid_info["stat"]["rt_priority"]) if new_policy == schedutils.SCHED_OTHER: new_prio = 0 if curr_policy != new_policy or curr_prio != new_prio: try: schedutils.set_scheduler(pid, new_policy, new_prio) except: dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT, gtk.MESSAGE_WARNING, gtk.BUTTONS_OK, _("Invalid parameters!")) dialog.run() dialog.destroy() return False curr_policy = schedutils.get_scheduler(pid) if curr_policy != new_policy: print _("couldn't change pid %(pid)d from %(cpol)s(%(cpri)d) to %(npol)s(%(npri)d)!") % \ { 'pid': pid, 'cpol': schedutils.schedstr(curr_policy), 'cpri': curr_prio, 'npol': schedutils.schedstr(new_policy), 'npri': new_prio} else: changed = True try: curr_affinity = schedutils.get_affinity(pid) except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError if e[0] == 3: return False raise e try: new_affinity = [ int(a) for a in new_affinity.split(",") ] except: try: new_affinity = tuna.cpustring_to_list(new_affinity) except: new_affinity = procfs.bitmasklist(new_affinity, nr_cpus) new_affinity.sort() if curr_affinity != new_affinity: try: schedutils.set_affinity(pid, new_affinity) except: return invalid_affinity() try: curr_affinity = schedutils.get_affinity(pid) except (SystemError, OSError) as e: # (3, 'No such process') old python-schedutils incorrectly raised SystemError if e[0] == 3: return False raise e if curr_affinity != new_affinity: print _("couldn't change pid %(pid)d from %(caff)s to %(naff)s!") % \ { 'pid':pid, 'caff':curr_affinity, 'naff':new_affinity } else: changed = True return changed