def run(self): # Index for simulation time sim_time = self.pump.sim_time() tindex = (sim_time + self.ramp_up) / self.freq if tindex >= (self.min_ts_length - self.ramp_down / self.freq): print 'Driver exited!' print 'Shutting down simulation...' self.scoreboard.close() self.pump.stop() return # Update slot count in scoreboard self.scoreboard.update_slot_count() # For all nodes update their domains and aggregate the load for the node for host in self.model.get_hosts(model.types.NODE): # Reset aggregated server load aggregated_load = 0 # Go over all domains and update their load by their TS for domain in host.domains.values(): load = domain.ts[tindex] # Notify load to the domain self.__notify(sim_time, domain.name, 'psutilcpu', load) # Load aggregation for the node aggregated_load += nodes.to_node_load(load) # Update aggregated cpu load self.scoreboard.add_cpu_load(load) # Add hypervisor load to the aggregated load # For the SSAPv this causes service level violations aggregated_load += BASE_LOAD # Add Migration overheads if host.active_migrations_out: aggregated_load += host.active_migrations_out * MIGRATION_SOURCE if host.active_migrations_in: aggregated_load += host.active_migrations_in * MIGRATION_TARGET self.__notify(sim_time, host.name, 'psutilcpu', aggregated_load) # Update overload counter if aggregated_load > 100: self.scoreboard.add_cpu_violations(1) # Schedule next call for run self.pump.callLater(self.report_rate, self.run)
def migrate_underload(self, node, nodes, source, domain, time_now, sleep_time, empty): # Try all targets for the migration for target in range(nodes.index(node) - 1): target = nodes[target] if len(target.domains) == 0 and empty == False: continue test = True test &= (target.percentile_load(self.PERCENTILE, self.K_VALUE) + nodesv.to_node_load(domain.percentile_load(self.PERCENTILE, self.K_VALUE))) < self.THRESHOLD_OVERLOAD # Overload threshold test &= len(target.domains) < 6 test &= (time_now - target.blocked) > sleep_time test &= (time_now - source.blocked) > sleep_time if test: migration_type = 'Underload (Empty=%s)' % (empty) self.migration_scheduler.add(domain, source, target, description=migration_type) raise StopIteration()
def migrate_imbalance(self, time_now, sleep_time): # based on DSR algorithm # create snapshot of nodes and domains system_nodes = self.model.get_hosts(types.NODE) nodes = {} domains = {} for node in system_nodes: node_domains = {} for domain in node.domains.values(): new_domain = Host() new_domain.name = domain.name new_domain.cpu = nodesv.to_node_load(domain.percentile_load(self.PERCENTILE, self.K_VALUE)) new_domain.source = node.name node_domains[domain.name] = new_domain domains[domain.name] = new_domain new_node = Host() new_node.name = node.name new_node.cpu = node.percentile_load(self.PERCENTILE, self.K_VALUE) new_node.domains = node_domains new_node.normalized_cpu = self.normalized_cpu(new_node) nodes[node.name] = new_node # run load balancing imbalance = self.imbalance(nodes) migration_triggered = False num_migrations = 0 max_migrations = 10 while (imbalance > self.THRESHOLD_IMBALANCE) and (num_migrations < max_migrations): best_migration = None best_improvement = 0 for domain in domains.itervalues(): # for every domain tmp_nodes = nodes # save parent node extra source = tmp_nodes[domain.source] source_node = self.model.get_host(source.name) if len(source.domains) == 1: # don't consider domains, which are alone on one node continue for node in tmp_nodes.itervalues(): # consider every node as new parent for domain # except the same node if (node.name == source_node.name): continue # calculate new cpu target_node = self.model.get_host(node.name) target_domain = self.model.get_host(domain.name) old_domain_cpu = domain.cpu domain.cpu = nodesv.to_node_load(target_domain.percentile_load(self.PERCENTILE, self.K_VALUE)) target_threshold = node.cpu + domain.cpu # don't migrate to empty node nor full node nor if overload threshold is exceeded test = False test |= len(target_node.domains) == 0 test |= len(target_node.domains) >= 6 test |= target_threshold > self.THRESHOLD_OVERLOAD test |= (time_now - target_node.blocked) <= sleep_time test |= (time_now - source_node.blocked) <= sleep_time if test: continue # migrate domain to node (in snapshot) and check new imbalance of all nodes node.domains[domain.name] = domain del source.domains[domain.name] old_normalized_cpu_node = node.normalized_cpu old_normalized_cpu_source = source.normalized_cpu node.normalized_cpu = self.normalized_cpu(node) source.normalized_cpu = self.normalized_cpu(source) new_imbalance = self.imbalance(tmp_nodes) # check if imbalance is improved improvement = imbalance - new_imbalance if improvement > self.MIN_IMPROVEMENT_IMBALANCE and improvement > best_improvement: # safe nodes and domain with MODEL information as best migration best_migration = Migration() best_migration.domain = target_domain best_migration.source = source_node best_migration.target = target_node # safe nodes and domain with SNAPSHOT information as best migration best_source = source best_target = node best_domain = domain best_improvement = improvement # go back to previous state in snapshot domain.cpu = old_domain_cpu source.domains[domain.name] = domain source.normalized_cpu = old_normalized_cpu_source del node.domains[domain.name] node.normalized_cpu = old_normalized_cpu_node if best_migration is None: break # update imbalance with best improvement imbalance -= best_improvement # update migration in snapshot best_domain.source = best_target.name best_target.domains[best_domain.name] = best_domain best_target.normalized_cpu = self.normalized_cpu(best_target) del best_source.domains[best_domain.name] best_source.normalized_cpu = self.normalized_cpu(best_source) num_migrations += 1 # add migration that improves imbalance the most self.migration_scheduler.add(best_migration.domain, best_migration.source, best_migration.target, description='Imbalance') migration_triggered = True return migration_triggered
def migrate_swap(self, time_now, sleep_time): ############################################ ## HOTSPOT DETECTOR ######################## ############################################ for node in self.model.get_hosts(types.NODE): # Check past readings readings = node.get_readings() # m out of the k last measurements are used to detect overloads k = self.K_VALUE overload = 0 underload = 0 for reading in readings[-k:]: if reading > self.THRESHOLD_OVERLOAD: overload += 1 if reading < self.THRESHOLD_UNDERLOAD: underload += 1 m = self.M_VALUE overload = (overload >= m) underload = (underload >= m) if overload: print 'Overload in %s - %s' % (node.name, readings[-k:]) # Update overload node.overloaded = overload node.underloaded = underload ############################################ ## MIGRATION MANAGER ####################### ############################################ # Calculate volumes of each node nodes = [] domains = [] for node in self.model.get_hosts(): volume = 1.0 / max(0.001, float(100.0 - node.percentile_load(self.PERCENTILE, k)) / 100.0) node.volume = volume node.volume_size = volume / 8.0 # 8 GByte if node.type == types.NODE: nodes.append(node) elif node.type == types.DOMAIN: domains.append(node) # Sort nodes to their volume in DECREASING order # Multiplication with a big value to shift post comma digits to the front (integer) nodes.sort(lambda a, b: int((b.volume - a.volume) * 100000)) ############################################ ## MIGRATION TRIGGER ####################### ############################################ migration_triggered = False for node in nodes: node.dump() # Overload situation try: if node.overloaded: # Source node to migrate from source = node # Sort domains by their VSR value in decreasing order node_domains = [] node_domains.extend(node.domains.values()) node_domains.sort(lambda a, b: int(b.volume_size - a.volume_size)) # Try to migrate all domains by decreasing VSR value for domain in node_domains: # Try all targets for swapping for target_node in reversed(range(nodes.index(node) + 1, len(nodes))): target_node = nodes[target_node] if len(target_node.domains) == 0: continue # Sort domains of target by their VSR value in ascending order target_domains = [] target_domains.extend(target_node.domains.values()) target_domains.sort(lambda a, b: int((a.volume_size - b.volume_size))) # Try to find one or more low VSR VMs for swapping for target in range(0, len(target_domains)): targets = [] # Get one or more VMs for i in range(0, target+1): targets.append(target_domains[i]) # Calculate new loads new_target_node_load = target_node.percentile_load(self.PERCENTILE, k) + nodesv.to_node_load(domain.percentile_load(self.PERCENTILE, k)) new_source_node_load = node.percentile_load(self.PERCENTILE, k) - nodesv.to_node_load(domain.percentile_load(self.PERCENTILE, k)) for target_domain in targets: new_target_node_load -= nodesv.to_node_load(target_domain.percentile_load(self.PERCENTILE, self.K_VALUE)) new_source_node_load += nodesv.to_node_load(target_domain.percentile_load(self.PERCENTILE, self.K_VALUE)) #Test if swap violates rules test = True test &= new_target_node_load < self.THRESHOLD_OVERLOAD test &= new_source_node_load < self.THRESHOLD_OVERLOAD test &= len(node.domains) < 6 test &= (time_now - target_node.blocked) > sleep_time test &= (time_now - source.blocked) > sleep_time if test: self.migration_scheduler.add(domain, source, target_node, description='Swap Part 1') for target_domain in targets: self.migration_scheduler.add(target_domain, target_node, source, description='Swap Part 2') raise StopIteration() except StopIteration: migration_triggered = True pass return migration_triggered