def loadbalance(self): """ Run the loadbalancer on the cluster and migrate vm accordingly. See cxm.loadbalancer module for details about algorithm. """ if not core.cfg['QUIET']: print "Recording metrics..." current_state = {} vm_metrics = {} node_metrics = {} for node in self.get_nodes(): node.metrics.init_cache( ) # Early call to increase timeslice used to compute rates vms = node.get_vms() # Get current cluster state current_state[node.get_hostname()] = [vm.name for vm in vms] # Get node's metrics node_metrics[node.get_hostname()] = { 'ram': node.metrics.get_available_ram() } # Get VM's metrics cpu = node.metrics.get_vms_cpu_usage() io = node.metrics.get_vms_disk_io_rate() for vm in vms: vm_metrics[vm.name] = {} vm_metrics[vm.name]['ram'] = vm.get_ram() vm_metrics[vm.name]['cpu'] = cpu[vm.name] vm_metrics[ vm.name]['io'] = io[vm.name]['Read'] + io[vm.name]['Write'] # Initialize loadbalancer lb = loadbalancer.LoadBalancer(current_state) lb.set_metrics(vm_metrics, node_metrics) solution = lb.get_solution() if not solution: print "No better solution found with a minimal gain of %s%%." % core.cfg[ 'LB_MIN_GAIN'] else: # Ask the user for a confirmation if not core.cfg['QUIET']: print "Here is the proposed migration plan:" for path in solution.get_path(): print " -> Migrate", path['vm'], "from", path[ 'src'], "to", path['dst'] if (raw_input("Proceed ? [y/N]:").upper() != "Y"): print "Aborded by user." return # Do migrations to put the cluster in the selected state for path in solution.get_path(): if not core.cfg['QUIET']: print "Migrating", path['vm'], "from", path[ 'src'], "to", path['dst'], "..." self.migrate(path['vm'], path['src'], path['dst'])
def loadbalance(self): """ Run the loadbalancer on the cluster and migrate vm accordingly. See cxm.loadbalancer module for details about algorithm. """ if not core.cfg["QUIET"]: print "Recording metrics..." current_state = {} vm_metrics = {} node_metrics = {} for node in self.get_nodes(): node.metrics.init_cache() # Early call to increase timeslice used to compute rates vms = node.get_vms() # Get current cluster state current_state[node.get_hostname()] = [vm.name for vm in vms] # Get node's metrics node_metrics[node.get_hostname()] = {"ram": node.metrics.get_available_ram()} # Get VM's metrics cpu = node.metrics.get_vms_cpu_usage() io = node.metrics.get_vms_disk_io_rate() for vm in vms: vm_metrics[vm.name] = {} vm_metrics[vm.name]["ram"] = vm.get_ram() vm_metrics[vm.name]["cpu"] = cpu[vm.name] vm_metrics[vm.name]["io"] = io[vm.name]["Read"] + io[vm.name]["Write"] # Initialize loadbalancer lb = loadbalancer.LoadBalancer(current_state) lb.set_metrics(vm_metrics, node_metrics) solution = lb.get_solution() if not solution: print "No better solution found with a minimal gain of %s%%." % core.cfg["LB_MIN_GAIN"] else: # Ask the user for a confirmation if not core.cfg["QUIET"]: print "Here is the proposed migration plan:" for path in solution.get_path(): print " -> Migrate", path["vm"], "from", path["src"], "to", path["dst"] if raw_input("Proceed ? [y/N]:").upper() != "Y": print "Aborded by user." return # Do migrations to put the cluster in the selected state for path in solution.get_path(): if not core.cfg["QUIET"]: print "Migrating", path["vm"], "from", path["src"], "to", path["dst"], "..." self.migrate(path["vm"], path["src"], path["dst"])
def cxm_list(cluster, options): """List started VM on all nodes.""" if options.node: nodes=[cluster.get_node(options.node)] else: nodes=cluster.get_nodes() for node in nodes: print "\nOn", node.get_hostname(), ":" if not core.cfg['QUIET']: print "-----" + "-" * len(node.get_hostname()) print '\n %-40s %4s %5s %6s' % ("Name","Mem", "VCPUs","State") for vm in sorted(node.get_vms(),key=lambda x: x.name): print ' %-40s %4d %5d %6s' % (vm.name, vm.ram, vm.vcpu, vm.state)
def getList(node): if core.cfg['QUIET']: msg = "" for vm in sorted(node.get_vms(),key=lambda x: x.name): msg += vm.name + "\n" else: # Max 80 columns wide msg = "\nOn %s :\n" % (node.get_hostname()) msg += "-----" + "-" * len(node.get_hostname()) + '\n' msg += '\n %-60s %4s %5s %6s\n' % ("Name","Mem", "VCPUs","State") for vm in sorted(node.get_vms(),key=lambda x: x.name): msg += ' %-60s %4d %5d %6s\n' % (vm.name, vm.ram, vm.vcpu, vm.state) return (node.get_hostname(), msg)
def cxm_create(cluster, vm, options): """Start the specified vm. If options.node is not given, vm is started where an autostart link is found. """ # Check if vm is't already started somewhere nodes=cluster.search_vm_started(vm) if(len(nodes)>0): print "** Nothing to do :" print "** " + vm + " is running on "+", ".join([n.get_hostname() for n in nodes]) sys.exit(2) if options.node: node=cluster.get_node(options.node) else: nodes=cluster.search_vm_autostart(vm) if(len(nodes)>1): print "** Warning: duplicates autostart links found on :" print "** -> " + ", ".join([n.get_hostname() for n in nodes]) print "** Don't know where to start the VM (correct the links or use --force-node)." sys.exit(2) try: node=nodes[0] except IndexError: node=cluster.get_local_node() print "** Warning: no autostart link found. Starting VM here." if not core.cfg['QUIET'] : print "Starting",vm,"on",node.get_hostname(),"..." cluster.start_vm(node,vm,options.console)
def check_bridges(self): """Perform a check on briges' configurations. Return a corresponding exit code (0=sucess, 0!=error) """ if not core.cfg["QUIET"]: print "Checking bridges configurations..." safe = True # Get a dict with bridges of each nodes nodes_bridges = dict() for node in self.get_nodes(): nodes_bridges[node.get_hostname()] = node.get_bridges() if core.cfg["DEBUG"]: print "DEBUG nodes_bridges =", nodes_bridges # Compare bridges lists for each nodes missing = dict() for node in nodes_bridges.keys(): for bridges in nodes_bridges.values(): missing.setdefault(node, []).extend(list(Set(bridges) - Set(nodes_bridges[node]))) # Show missing bridges without duplicates for node in missing.keys(): if missing[node]: print " ** WARNING : Missing bridges on %s : %s" % (node, ", ".join(list(Set(missing[node])))) safe = False return safe
def check_bridges(self): """Perform a check on briges' configurations. Return a corresponding exit code (0=sucess, 0!=error) """ if not core.cfg['QUIET']: print "Checking bridges configurations..." safe = True # Get a dict with bridges of each nodes nodes_bridges = dict() for node in self.get_nodes(): nodes_bridges[node.get_hostname()] = node.get_bridges() if core.cfg['DEBUG']: print "DEBUG nodes_bridges =", nodes_bridges # Compare bridges lists for each nodes missing = dict() for node in nodes_bridges.keys(): for bridges in nodes_bridges.values(): missing.setdefault(node, []).extend( list(Set(bridges) - Set(nodes_bridges[node]))) # Show missing bridges without duplicates for node in missing.keys(): if missing[node]: print " ** WARNING : Missing bridges on %s : %s" % ( node, ", ".join(list(Set(missing[node])))) safe = False return safe
def cxm_infos(cluster): """Print the status of the cluster.""" print '%-40s %3s %3s %8s %4s' % ("Node name","VM", "IRQ","Free-RAM","Load") for node in cluster.get_nodes(): metrics=node.get_metrics() print '%-40s %3d %3d %8d %3d%%' % (node.get_hostname(),node.get_vm_started(), metrics.get_used_irq(),metrics.get_free_ram(),metrics.get_load())
def cxm_eject(cluster, options): """Eject local node from cluster.""" if options.node: node=cluster.get_node(options.node) else: node=cluster.get_local_node() if not core.cfg['QUIET'] : print "Ejecting all running VM from",node.get_hostname(),"..." cluster.emergency_eject(node)
def cxm_init(cluster, options): """Initialize the cluster at startup.""" if options.node: node=cluster.get_node(options.node) else: node=cluster.get_local_node() if not core.cfg['QUIET'] : print "Initialize cluster on",node.get_hostname(),"..." node.deactivate_all_lv()
def cxm_migrate(cluster, vm, dest, options): """Live migrate the vm to the specified dest. If options.node is not given, search for the vm over the cluster. """ node=select_node_by_vm(cluster, vm, options) src_hostname=node.get_hostname() if not core.cfg['QUIET'] : print "Migrating",vm,"from",src_hostname,"to",dest,"..." cluster.migrate(vm, src_hostname, dest)
def cxm_shutdown(cluster, vm, options): """Properly shutdown the specified VM. If options.node is not given, search for the vm over the cluster. """ node=select_node_by_vm(cluster, vm, options) if not core.cfg['QUIET'] : print "Shutting down",vm,"on",node.get_hostname(),"..." node.shutdown(vm, True)
def cxm_console(cluster, vm, options): """Attach local console to the given VM.""" node=select_node_by_vm(cluster, vm, options) if node.is_local_node(): node.get_vm(vm).attach_console() else: print "** ERROR: Cannot attach console on a remote host !" print "** You should try on", node.get_hostname()
def cxm_reboot(cluster, options, vm): """Reboot the specified VM. If options.node is not given, search for the vm over the cluster. """ vm=os.path.basename(vm) node=select_node_by_vm(cluster, vm, options) if not core.cfg['QUIET'] : print "Rebooting",vm,"on",node.get_hostname(),"..." node.reboot(vm)
def start_vm(self, node, vmname, console): """Start the specified VM on the given node. If there is not enough ram on the given node, the VM will be started on the node with the highest free ram and the autostart link will be updated accordingly. node - (Node) Selected host vmname - (String) VM hostname console - (boolean) Attach console to the domain """ # Resources checks needed_ram = vm.VM(vmname).get_ram() free_ram = node.metrics.get_free_ram() if needed_ram > free_ram: # Not enough ram, switching to another node old_node = node # Get the node with the highest free ram (first fit increasing algorithm) pool = self.get_nodes() pool.sort(key=lambda x: x.metrics.get_free_ram(), reverse=True) node = pool[0] # Last resources checks free_ram = node.metrics.get_free_ram() if needed_ram > free_ram: raise ClusterNodeError( node.get_hostname(), ClusterNodeError.NOT_ENOUGH_RAM, "need " + str(needed_ram) + "M, has " + str(free_ram) + "M.", ) if not core.cfg["QUIET"]: print " -> Not enough ram, starting it on %s." % node.get_hostname() # Start the VM self.activate_vm(node, vmname) try: node.start_vm(vmname) except Exception, e: node.deactivate_lv(vmname) raise e
def start_vm(self, node, vmname, console): """Start the specified VM on the given node. If there is not enough ram on the given node, the VM will be started on the node with the highest free ram and the autostart link will be updated accordingly. node - (Node) Selected host vmname - (String) VM hostname console - (boolean) Attach console to the domain """ # Resources checks needed_ram = vm.VM(vmname).get_ram() free_ram = node.metrics.get_free_ram() if needed_ram > free_ram: # Not enough ram, switching to another node old_node = node # Get the node with the highest free ram (first fit increasing algorithm) pool = self.get_nodes() pool.sort(key=lambda x: x.metrics.get_free_ram(), reverse=True) node = pool[0] # Last resources checks free_ram = node.metrics.get_free_ram() if needed_ram > free_ram: raise ClusterNodeError( node.get_hostname(), ClusterNodeError.NOT_ENOUGH_RAM, "need " + str(needed_ram) + "M, has " + str(free_ram) + "M.") if not core.cfg['QUIET']: print " -> Not enough ram, starting it on %s." % node.get_hostname( ) # Start the VM self.activate_vm(node, vmname) try: node.start_vm(vmname) except Exception, e: node.deactivate_lv(vmname) raise e
def cxm_deactivate(cluster, vm, options): """Deactivate the logicals volumes of the specified VM. If options.node is not given, use local node. """ if options.node: node=cluster.get_node(options.node) else: node=cluster.get_local_node() if not core.cfg['QUIET'] : print "Deactivating LVs of",vm,"on",node.get_hostname(),"..." node.deactivate_lv(vm)
def cxm_activate(cluster, vm, options): # Exclusive activation """Activate the logicals volumes of the specified VM. If options.node is not given, activate on the local node and deactivate on all others. """ if options.node: node=cluster.get_node(options.node) else: node=cluster.get_local_node() if not core.cfg['QUIET'] : print "Activating LVs of",vm,"on",node.get_hostname(),"..." cluster.activate_vm(node,vm)
def check(self): """Perform a sanity check of the cluster. Return a corresponding exit code (0=success, 0!=error) """ if not core.cfg["QUIET"]: print "Checking for duplicate VM..." safe = True # Get cluster wide VM list vm_by_node = dict() for node in self.get_nodes(): vm_by_node[node.get_hostname()] = node.get_vms() if core.cfg["DEBUG"]: print "DEBUG vm_by_node=", vm_by_node # Invert key/value of the dict node_by_vm = dict() for node, vms in vm_by_node.items(): for vm in vms: try: node_by_vm[vm.name].append(node) except KeyError: node_by_vm[vm.name] = [node] if core.cfg["DEBUG"]: print "DEBUG node_by_vm =", node_by_vm # Check duplicate VM for vm, nodes in node_by_vm.items(): if len(nodes) > 1: print " ** WARNING : " + vm + " is running on " + " and ".join(nodes) safe = False # Check bridges if not self.check_bridges(): safe = False # Other checks for node in self.get_nodes(): # Check (non)activation of LVs if not node.check_lvs(): safe = False # Check autostart link if not node.check_autostart(): safe = False return safe
def cxm_destroy(cluster, vm, options): """Terminate the specified VM immediately. If options.node is not given, search for the vm over the cluster. """ node=select_node_by_vm(cluster, vm, options) if not core.cfg['QUIET']: print "Destroying",vm,"on",node.get_hostname(),"..." if(raw_input("Are you really sure ? [y/N]:").upper() != "Y"): print "Aborded by user." return node.shutdown(vm, False)
def activate_vm(self, selected_node, vmname): """Activate all the LVM logicals volumes of the specified VM exclusively on the selected node. selected_node - (Node) Node where to activate the LVs vmname - (String) hostname of the vm Raise a ClusterNodeError if the VM is running. """ for node in self.get_nodes(): if node.is_vm_started(vmname): raise ClusterNodeError(node.get_hostname(), ClusterNodeError.VM_RUNNING, vmname) else: node.deactivate_lv(vmname) selected_node.activate_lv(vmname)
def check(self): """Perform a sanity check of the cluster. Return a corresponding exit code (0=success, 0!=error) """ if not core.cfg['QUIET']: print "Checking for duplicate VM..." safe = True # Get cluster wide VM list vm_by_node = dict() for node in self.get_nodes(): vm_by_node[node.get_hostname()] = node.get_vms() if core.cfg['DEBUG']: print "DEBUG vm_by_node=", vm_by_node # Invert key/value of the dict node_by_vm = dict() for node, vms in vm_by_node.items(): for vm in vms: try: node_by_vm[vm.name].append(node) except KeyError: node_by_vm[vm.name] = [node] if core.cfg['DEBUG']: print "DEBUG node_by_vm =", node_by_vm # Check duplicate VM for vm, nodes in node_by_vm.items(): if len(nodes) > 1: print " ** WARNING : " + vm + " is running on " + " and ".join( nodes) safe = False # Check bridges if not self.check_bridges(): safe = False # Other checks for node in self.get_nodes(): # Check (non)activation of LVs if not node.check_lvs(): safe = False # Check autostart link if not node.check_autostart(): safe = False return safe
def cxm_migrate(cluster, options, vm, dst): """Live migrate the vm to the specified dest. If options.node is not given, search for the vm over the cluster. """ vm=os.path.basename(vm) node=select_node_by_vm(cluster, vm, options) src=node.get_hostname() if src == dst: if not core.cfg['QUIET']: print "Nothing to do, %s is already on %s." % (vm,dst) else: if not core.cfg['QUIET'] : print "Migrating %s from %s to %s..." % (vm,src,dst) cluster.migrate(vm, src, dst)
def getNodeMetrics(node): metrics=node.get_metrics() return '%-40s %3d %3d %8d %3d%%' % (node.get_hostname(),node.get_vm_started(), metrics.get_used_irq(),metrics.get_free_ram(),metrics.get_load())