def _get_mapped_slave(self, tm_id): pm_id = self._get_machine_mapping(tm_id) pm = self._pool[pm_id] hostname = pm["params"]["hostname"] libvirt_domain = None if "libvirt_domain" in pm["params"]: libvirt_domain = pm["params"]["libvirt_domain"] machine = Machine(tm_id, hostname, libvirt_domain) used = [] if_map = self._map["machines"][tm_id]["interfaces"] for t_if, p_if in if_map.iteritems(): used.append(p_if) if_data = pm["interfaces"][p_if] for t_net, p_net in self._map["networks"].iteritems(): if pm["interfaces"][p_if]["network"] == p_net: break iface = machine.new_static_interface(t_if, "eth") iface.set_hwaddr(if_data["hwaddr"]) iface.set_network(t_net) for if_id, if_data in pm["interfaces"].iteritems(): if if_id not in used: iface = machine.new_unused_interface("eth") iface.set_hwaddr(if_data["hwaddr"]) iface.set_network(t_net) return machine
def _prepare_virtual_slave(self, tm_id, tm): pm_id = self._get_machine_mapping(tm_id) pm = self._pool[pm_id] hostname = pm["params"]["hostname"] libvirt_domain = pm["params"]["libvirt_domain"] rpcport = None if "rpc_port" in pm["params"]: rpcport = pm["params"]["rpc_port"] machine = Machine(tm_id, hostname, libvirt_domain, rpcport) # make all the existing unused for if_id, if_data in pm["interfaces"].iteritems(): iface = machine.new_unused_interface("eth") iface.set_hwaddr(if_data["params"]["hwaddr"]) iface.set_network(None) # add all the other devices for if_id, if_data in tm["interfaces"].iteritems(): iface = machine.new_virtual_interface(if_id, "eth") iface.set_network(if_data["network"]) if "hwaddr" in if_data["params"]: iface.set_hwaddr(if_data["params"]["hwaddr"]) if "driver" in if_data["params"]: iface.set_driver(if_data["params"]["driver"]) return machine
def connect_host(self, hostname, timeout=60, port=None, machine_id=None, security=None): ctl_config = self._controller._config msg_dispatcher = self._controller._msg_dispatcher if security is None: security = {"auth_type": "none"} if machine_id is None: machine_id = hostname m = Machine(machine_id, hostname, msg_dispatcher, ctl_config, None, port, security) def condition(): try: m.init_connection(timeout=1) return True except: log_exc_traceback() return False msg_dispatcher.wait_for_condition(condition, timeout) host = Host(m) self._controller._prepare_machine(m) m.start_recipe(self._recipe) return host
def _prepare_virtual_slave(self, tm_id, tm): pm_id = self._get_machine_mapping(tm_id) pm = self._pool[pm_id] hostname = pm["params"]["hostname"] libvirt_domain = pm["params"]["libvirt_domain"] rpcport = None if "rpc_port" in pm["params"]: rpcport = pm["params"]["rpc_port"] machine = Machine(tm_id, hostname, libvirt_domain, rpcport, pm["security"]) # make all the existing unused for if_id, if_data in pm["interfaces"].iteritems(): iface = machine.new_unused_interface("eth") iface.set_hwaddr(if_data["params"]["hwaddr"]) iface.set_network(None) # add all the other devices for if_id, if_data in tm["interfaces"].iteritems(): iface = machine.new_virtual_interface(if_id, "eth") iface.set_network(if_data["network"]) if "hwaddr" in if_data["params"]: iface.set_hwaddr(if_data["params"]["hwaddr"]) if "driver" in if_data["params"]: iface.set_driver(if_data["params"]["driver"]) return machine
def _get_mapped_slave(self, tm_id): pm_id = self._get_machine_mapping(tm_id) pm = self._pool[pm_id] hostname = pm["params"]["hostname"] rpcport = None if "rpc_port" in pm["params"]: rpcport = pm["params"]["rpc_port"] machine = Machine(tm_id, hostname, None, rpcport) used = [] if_map = self._map["machines"][tm_id]["interfaces"] for t_if, p_if in if_map.iteritems(): pool_id = p_if["target"] used.append(pool_id) if_data = pm["interfaces"][pool_id] iface = machine.new_static_interface(t_if, "eth") iface.set_hwaddr(if_data["params"]["hwaddr"]) for t_net, p_net in self._map["networks"].iteritems(): if pm["interfaces"][pool_id]["network"] == p_net: iface.set_network(t_net) break for if_id, if_data in pm["interfaces"].iteritems(): if if_id not in used: iface = machine.new_unused_interface("eth") iface.set_hwaddr(if_data["params"]["hwaddr"]) iface.set_network(None) return machine
def _get_mapped_slave(self, tm_id): pm_id = self._get_machine_mapping(tm_id) pm = self._pool[pm_id] hostname = pm["params"]["hostname"] rpcport = None if "rpc_port" in pm["params"]: rpcport = pm["params"]["rpc_port"] machine = Machine(tm_id, hostname, None, rpcport, pm["security"]) used = [] if_map = self._map["machines"][tm_id]["interfaces"] for t_if, p_if in if_map.iteritems(): pool_id = p_if["target"] used.append(pool_id) if_data = pm["interfaces"][pool_id] iface = machine.new_static_interface(t_if, "eth") iface.set_hwaddr(if_data["params"]["hwaddr"]) for t_net, p_net in self._map["networks"].iteritems(): if pm["interfaces"][pool_id]["network"] == p_net: iface.set_network(t_net) break for if_id, if_data in pm["interfaces"].iteritems(): if if_id not in used: iface = machine.new_unused_interface("eth") iface.set_hwaddr(if_data["params"]["hwaddr"]) iface.set_network(None) return machine
def _check_machine(self, machine: Machine): """Method checks if the agent process inside of the container is running.""" hostname = machine.get_hostname() logging.debug(f"Checking connection with machine {hostname}") connection = socket.socket() connection.settimeout(self._start_timeout) retry_counter = 5 for i in range(retry_counter): logging.debug( f"Connecting to {machine.get_hostname()}, retry counter: {i}") try: connection.connect((hostname, machine._port)) except (ConnectionRefusedError, ConnectionAbortedError): sleep(1) continue logging.debug(f"Connected to agent process at machine {hostname}") break # successfully connected else: raise PoolManagerError(f"Could not connect to machine {hostname}") err = connection.getsockopt(socket.SOL_SOCKET, socket.SO_ERROR) if err: connection.close() raise PoolManagerError(f"Could not connect to machine {hostname}") connection.shutdown(socket.SHUT_RDWR) connection.close() logging.info(f"Agent process is running at {hostname}")
def _create_container(self, name: str, reqs: dict): logging.info("Creating container " + name) if "rpc_port" in reqs: rpc_port = reqs["rpc_port"] else: rpc_port = None self._pool[name] = { "interfaces": {}, "params": { "hostname": "", "rpc_port": rpc_port }, "security": { "auth_type": "none" }, "available": True, } try: container = self._podman_client.containers.create(self.image, hostname=name, privileged=True) except APIError as e: raise PoolManagerError(f"Could not create container {name}: {e}") machine = Machine( name, "", self._msg_dispatcher, self._ctl_config, None, rpc_port, self._pool[name]["security"], reqs, ) # to get hostname the container needs to run self._start_container(container, machine) if self._pool_check: self._check_machine( machine) # checks if the agent process is already running self._pool[name]["params"]["hostname"] = machine.get_hostname() return container, machine
def _start_container(container: Container, machine: Machine): logging.debug("Starting container " + container.name) container.start() container.reload() container.wait(condition="running") machine._hostname = container.attrs["NetworkSettings"]["Networks"][ "podman"]["IPAddress"]
def _connect_and_configure_machine(self, hostname, port): """ Connects to Slave and configures it """ try: self._machine = Machine(1, hostname, None, port) self._machine.set_rpc(self._msg_dispatcher) self._machine.configure("MachinePoolWizard") return True except: sys.stderr.write("Remote machine '%s:%s' configuration failed!\n" % (hostname, port)) self._msg_dispatcher.disconnect_slave(1) return False
def __init__(self, pools, msg_dispatcher, ctl_config, pool_checks=True): self._map = {} self._pools = {} self._pool = {} self._msg_dispatcher = msg_dispatcher self._ctl_config = ctl_config self._allow_virt = ctl_config.get_option("environment", "allow_virtual") self._allow_virt &= check_process_running("libvirtd") self._pool_checks = pool_checks logging.info("Checking machine pool availability.") for pool_name, pool_dir in list(pools.items()): self._pools[pool_name] = {} self.add_dir(pool_name, pool_dir) if len(self._pools[pool_name]) == 0: del self._pools[pool_name] self._machines = {} for pool_name, machines in list(self._pools.items()): pool = self._machines[pool_name] = {} for m_id, m_spec in list(machines.items()): params = m_spec["params"] hostname = params["hostname"] if "libvirt_domain" in params: libvirt_domain = params["libvirt_domain"] else: libvirt_domain = None if "rpc_port" in params: rpc_port = params["rpc_port"] else: rpc_port = None pool[m_id] = Machine(m_id, hostname, self._msg_dispatcher, ctl_config, libvirt_domain, rpc_port, m_spec["security"], params) pool[m_id].init_connection() #TODO check if all described devices are available logging.info("Finished loading pools.")
class Wizard: def __init__(self): self._msg_dispatcher = MessageDispatcherLite() self._pool_dir = os.path.expanduser("~/.lnst/pool") def interactive(self): """ Starts Wizard in interactive mode. Wizard requests hostname and port from user, tests connectivity to entered host, then he tries to connect and configure slave on host machine, and finally he requests list of ethernet interfaces in state DOWN. Then he writes them into .xml file named by user. User can choose which interfaces should be added to the .xml file. """ pool_dir = raw_input("Enter path to pool directory "\ "(default: ~/.lnst/pool): ") if pool_dir != "": self._pool_dir = os.path.expanduser(pool_dir) print "Pool directory set to %s" % self._pool_dir while True: while True: hostname = raw_input("Enter hostname: ") try: # Tests if hostname is translatable into IP address socket.gethostbyname(hostname) break except: sys.stderr.write("Hostname is not translatable into valid"\ " IP address.\n") continue while True: port = raw_input("Enter port(default: 9999): ") if port == "": port = DefaultRPCPort try: port = int(port) break except: sys.stderr.write("Invalid port.") continue msg = self._get_suitable_interfaces(socket.gethostbyname(hostname),\ port) if msg: self._write_to_xml(msg, hostname, port, "interactive") next_machine = raw_input("Do you want to add another machine? "\ "[Y/n]: ") if next_machine.lower() == 'y' or next_machine == "": continue else: break return def noninteractive(self, hostlist): """ Starts Wizard in noninteractive mode. Wizard gets list of hosts and ports as arguments. He tries to connect and get info about suitable interfaces for each of the hosts. Noninteractive mode does not prompt user about anything, it automatically adds all suitable interfaces into .xml file named the same as the hostname of the selected machine. """ self._mode = "noninteractive" for host in hostlist: # Checks if port was entered along with hostname if host.find(":") != -1: hostname = host.split(':')[0] try: port = int(host.split(':')[1]) except: port = DefaultRPCPort else: hostname = host port = DefaultRPCPort msg = self._get_suitable_interfaces(hostname, port) if not msg: continue self._write_to_xml(msg, hostname, port, "noninteractive") def _get_suitable_interfaces(self, hostname, port): """ Calls all functions, which are used by both interactive and noninteractive mode to get list of suitable interfaces. The list is saved to variable msg. """ if not test_tcp_connection(hostname, port): sys.stderr.write("Host destination '%s:%s' unreachable.\n" % (hostname, port)) return False if not self._connect_and_configure_machine(hostname, port): return False msg = self._get_device_ifcs(hostname, port) self._msg_dispatcher.disconnect_slave(1) return msg def _get_device_ifcs(self, hostname, port): """ Sends RPC call request to Slave to call function get_devices, which returns list of ethernet devices which are in state DOWN. """ msg = self._machine._rpc_call("get_devices") if msg == {}: sys.stderr.write("No suitable interfaces found on the slave "\ "'%s:%s'.\n" % (hostname, port)) return False return msg def _connect_and_configure_machine(self, hostname, port): """ Connects to Slave and configures it """ try: self._machine = Machine(1, hostname, None, port) self._machine.set_rpc(self._msg_dispatcher) self._machine.configure("MachinePoolWizard") return True except: sys.stderr.write("Remote machine '%s:%s' configuration failed!\n" % (hostname, port)) self._msg_dispatcher.disconnect_slave(1) return False def _write_to_xml(self, msg, hostname, port, mode): """ Used for writing desired output into .xml file. In interactive mode, user is prompted for every interface, in noninteractive mode all interfaces are automatically added to the .xml file. """ if mode == "interactive": output_file = raw_input("Enter the name of the output .xml file "\ "(without .xml, default is hostname.xml): ") if mode == "noninteractive" or output_file == "": output_file = hostname impl = getDOMImplementation() doc = impl.createDocument(None, "slavemachine", None) top_el = doc.documentElement params_el = doc.createElement("params") top_el.appendChild(params_el) param_el = doc.createElement("param") param_el.setAttribute("name", "hostname") param_el.setAttribute("value", hostname) params_el.appendChild(param_el) interfaces_el = doc.createElement("interfaces") top_el.appendChild(interfaces_el) devices_added = 0 for interface in msg.itervalues(): if mode == 'interactive': answer = raw_input("Do you want to add interface '%s' (%s) " "to the recipe? [Y/n]" % (interface['name'], interface['hwaddr'])) if mode == "noninteractive" or answer.lower() == 'y'\ or answer == "": devices_added += 1 eth_el = doc.createElement("eth") eth_el.setAttribute("id", interface['name']) eth_el.setAttribute("label", "default_network") interfaces_el.appendChild(eth_el) params_el = doc.createElement("params") eth_el.appendChild(params_el) param_el = doc.createElement("param") param_el.setAttribute("name", "hwaddr") param_el.setAttribute("value", interface['hwaddr']) params_el.appendChild(param_el) if devices_added == 0: sys.stderr.write("You didn't add any interface, no file '%s.xml' "\ "will be created!\n" % output_file) return mkdir_p(self._pool_dir) try: f = open(self._pool_dir + "/" + output_file + ".xml", 'w') f.write(doc.toprettyxml()) f.close() except: sys.stderr.write("File '%s.xml' could not be opened "\ "or data written." % output_file+"\n") raise WizardException() print "File '%s.xml' successfuly created." % output_file