def down(configuration, context, **kwargs): hv = vl.LibvirtHypervisor(configuration.libvirt_uri) hv.init_storage_pool(configuration.storage_pool) for domain in hv.list_domains(): if context and domain.context != context: continue hv.clean_up(domain)
def ssh_config(configuration, context="default", **kwargs): """ Generate an SSH configuration based on the running VM """ conn = _connect_libvirt(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) ssh_host_template = ("Host {name}\n" " Hostname {ipv4}\n" " User {username}\n" " IdentityFile {ssh_key_file}\n") output = "" groups = {} for domain in hv.list_domains(): for group in domain.groups: groups[group].append(domain) if domain.context != context: continue template = ssh_host_template output += template.format( name=domain.name, username=domain.username, ipv4=domain.ipv4.ip, ssh_key_file=domain.ssh_key, ) for group_name, domains in groups.items(): output += "\n[{group_name}]".format(group_name=group_name) for domain in domains: output += domain.name return output
def viewer(configuration, name=None, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) def virt_viewer_binary(): paths = [ pathlib.PosixPath(i, "virt-viewer") for i in os.environ["PATH"].split(os.pathsep) ] for exe in paths: if exe.exists(): return exe raise Exception("Failed to find virt-viewer in: ", paths) def go_viewer(domain): pid = os.fork() if pid == 0: os.close(1) os.close(2) os.execlp( virt_viewer_binary(), "virt-viewer", "-c", configuration.libvirt_uri, "--domain-name", domain.name, ) else: sys.exit(0) if name: go_viewer(hv.get_domain_by_name(name)) ui.Selector(sorted(hv.list_domains()), go_viewer)
def ssh_config(configuration, context, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) ssh_host_template = ("Host {name}\n" " Hostname {ipv4}\n" " User {username}\n" " IdentityFile {ssh_key_file}\n") groups = {} for domain in hv.list_domains(): for group in domain.groups: groups[group].append(domain) if domain.context != context: continue template = ssh_host_template print( # noqa: T001 template.format( name=domain.name, username=domain.username, ipv4=domain.ipv4.ip, ssh_key_file=domain.ssh_key, )) # noqa: T001 for group_name, domains in groups.items(): print("\n[{group_name}]".format(group_name=group_name)) # noqa: T001 for domain in domains: print(domain.name) # noqa: T001
def parse(self, inventory, loader, path, cache=False): super(InventoryModule, self).parse(inventory, loader, path, cache) try: configuration = virt_lightning.configuration.Configuration() conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) for domain in hv.list_domains(): if domain.name not in self.inventory.hosts: if not domain.ipv4: continue self.inventory.add_host(domain.name, group='all', port=22) self.inventory.set_variable(domain.name, 'ansible_host', str(domain.ipv4.ip)) self.inventory.set_variable(domain.name, 'ansible_user', domain.username) self.inventory.set_variable(domain.name, 'ansible_python_interpreter', domain.python_interpreter) for group in domain.groups: if group not in self.inventory.groups: self.inventory.add_group(group) self.inventory.groups[group].add_host( self.inventory.hosts[domain.name]) except Exception as e: raise AnsibleError( 'Something happened, this was original exception: %s' % to_native(e))
def exec_ssh(configuration, name=None, **kwargs): """ Open an SSH connection on a host """ conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.get_domain_by_name(name).exec_ssh()
def status(configuration, context=None, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) results = {} def iconify(v): if isinstance(v, str): return v elif v: return symbols.CHECKMARK.value else: return symbols.CROSS.value for status in get_status(hv, context): results[status["name"]] = { "name": status["name"], "ipv4": status["ipv4"] or "waiting", "context": status["context"], "username": status["username"], } output_template = "{computer} {name:<13} {arrow} {username}@{ipv4:>5}" for _, v in sorted(results.items()): print( # noqa: T001 output_template.format(computer=symbols.COMPUTER.value, arrow=symbols.RIGHT_ARROW.value, **v))
def fetch(configuration=None, progress_callback=None, hv=None, **kwargs): """ Retrieve a VM image from Internet. """ if hv is None: conn = _connect_libvirt(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_storage_pool(configuration.storage_pool) configuration = configuration if configuration is not None else Configuration( ) image_found = False for images_url in configuration.private_hub: try: fetch_from_url( progress_callback=progress_callback, storage_dir=hv.get_storage_dir(), url=images_url, **kwargs, ) image_found = True break except ImageNotFoundUpstream: logger.info("Image: %s not found from url: %s", kwargs["distro"], images_url) if not image_found: # image not found from custom url # fetch from base url fetch_from_url( progress_callback=progress_callback, storage_dir=hv.get_storage_dir(), **kwargs, )
def list_domains(configuration, name=None, **kwargs): """ Return a list Python-libvirt instance of the running libvirt VM. """ conn = _connect_libvirt(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) return sorted(hv.list_domains())
def ansible_inventory(configuration, context, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) ssh_cmd_template = ( "{name} ansible_host={ipv4} ansible_user={username} " "ansible_python_interpreter={python_interpreter} " 'ansible_ssh_common_args="-o UserKnownHostsFile=/dev/null ' '-o StrictHostKeyChecking=no"') groups = {} for domain in hv.list_domains(): if domain.context != context: continue for group in domain.groups: if group not in groups: groups[group] = [] groups[group].append(domain) template = ssh_cmd_template print( # noqa: T001 template.format( name=domain.name, username=domain.username, ipv4=domain.ipv4.ip, python_interpreter=domain.python_interpreter, )) # noqa: T001 for group_name, domains in groups.items(): print("\n[{group_name}]".format(group_name=group_name)) # noqa: T001 for domain in domains: print(domain.name) # noqa: T001
def distro_list(configuration, **kwargs): """ Return a list of VM images that are available on the system. """ conn = _connect_libvirt(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_storage_pool(configuration.storage_pool) return hv.distro_available()
def storage_dir(configuration, **kwargs): """ Return the location of the VM image storage directory. """ conn = _connect_libvirt(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_storage_pool(configuration.storage_pool) return hv.get_storage_dir()
def fetch(configuration, progress_callback=None, **kwargs): """ Retrieve a VM image from Internet. """ conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_storage_pool(configuration.storage_pool) storage_dir = hv.get_storage_dir() try: r = urllib.request.urlopen( "https://virt-lightning.org/images/{distro}/{distro}.qcow2".format(**kwargs) ) except urllib.error.HTTPError as e: if e.code == 404: logger.error( ( "Distro %s not found!\n" "Visit https://virt-lightning.org/images/ " "to get an up to date list." ), kwargs["distro"], ) raise ImageNotFoundUpstream() else: logger.exception(e) raise lenght = int(r.headers["Content-Length"]) chunk_size = MB * 1 target_file = pathlib.PosixPath( "{storage_dir}/upstream/{distro}.qcow2".format( storage_dir=storage_dir, **kwargs ) ) temp_file = target_file.with_suffix(".temp") if target_file.exists(): logger.info( "File already exists: {target_file}".format(target_file=target_file) ) return with temp_file.open("wb") as fd: while fd.tell() < lenght: chunk = r.read(chunk_size) fd.write(chunk) if progress_callback: progress_callback(fd.tell(), lenght) temp_file.rename(target_file) try: r = urllib.request.urlopen( "https://virt-lightning.org/images/{distro}/{distro}.yaml".format(**kwargs) ) except urllib.error.HTTPError as e: if e.code == 404: pass with target_file.with_suffix(".yaml").open("wb") as fd: fd.write(r.read()) logger.info("Image {distro} is ready!".format(**kwargs))
def down(configuration, context, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_storage_pool(configuration.storage_pool) for domain in hv.list_domains(): if context and domain.context != context: continue logger.info("%s purging %s", symbols.TRASHBIN.value, domain.name) hv.clean_up(domain)
def exec_ssh(configuration, name=None, **kwargs): """ Open an SSH connection on a host """ conn = _connect_libvirt(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) domain = hv.get_domain_by_name(name) if not domain: raise VMNotFound(name) domain.exec_ssh()
def hv(scope="function"): conn = libvirt.open("test:///default") conn.getURI = Mock(return_value="qemu:///system") hv = vl.LibvirtHypervisor(conn) with patch.object(pathlib.Path, 'exists') as mock_exists: mock_exists.return_value = False hv.init_storage_pool("foo_bar") hv.init_network("my_network", "1.0.0.0/24") yield hv clean_up()
def start(configuration, context="default", enable_console=False, console_fd=None, **kwargs): """ Start a single VM """ conn = _connect_libvirt(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_network(configuration.network_name, configuration.network_cidr) hv.init_storage_pool(configuration.storage_pool) host = { k: kwargs[k] for k in ["name", "distro", "memory", "vcpus"] if kwargs.get(k) } _ensure_image_exists(hv, [host]) domain = _start_domain(hv, host, context, configuration) if not domain: return loop = kwargs.get("loop") or asyncio.get_event_loop() if enable_console: import time if console_fd is None: console_fd = sys.stdout time.sleep(4) stream = conn.newStream(libvirt.VIR_STREAM_NONBLOCK) console = domain.dom.openConsole(None, stream, 0) _register_aio_virt_impl(loop=kwargs.get("loop")) def stream_callback(stream, events, _): content = stream.recv(1024 * 1024).decode("utf-8", errors="ignore") console_fd.write(content) stream.eventAddCallback(libvirt.VIR_STREAM_EVENT_READABLE, stream_callback, console) async def deploy(): await domain.reachable() loop.run_until_complete(deploy()) logger.info( # noqa: T001 ("\033[0m\n**** System is online ****\n" "To connect use:\n" " vl console %s (virsh console)" " vl ssh %s"), domain.name, domain.name, ) return domain
def ssh(configuration, name=None, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) def go_ssh(domain): domain.exec_ssh() if name: hv.get_domain_by_name(name).exec_ssh() ui.Selector(sorted(hv.list_domains()), go_ssh)
def console(configuration, name=None, **kwargs): hv = vl.LibvirtHypervisor(configuration.libvirt_uri) def go_console(domain): os.execlp("virsh", "virsh", "-c", configuration.libvirt_uri, "console", domain.name) if name: go_console(hv.get_domain_by_name(name)) ui.Selector(sorted(hv.list_domains()), go_console)
def fetch(configuration, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_storage_pool(configuration.storage_pool) storage_dir = hv.get_storage_dir() try: r = urllib.request.urlopen( "https://virt-lightning.org/images/{distro}/{distro}.qcow2".format( **kwargs)) except urllib.error.HTTPError as e: if e.code == 404: print("Distro {distro} not found!".format(**kwargs)) # noqa: T001 print( # noqa: T001 "Visit https://virt-lightning.org/images/ to get an up to date list." ) sys.exit(1) else: logger.exception(e) sys.exit(1) lenght = int(r.headers["Content-Length"]) MB = 1024 * 1000 chunk_size = MB target_file = pathlib.PosixPath( "{storage_dir}/upstream/{distro}.qcow2".format(storage_dir=storage_dir, **kwargs)) temp_file = target_file.with_suffix(".temp") if target_file.exists(): print( # noqa: T001 "File already exists: {target_file}".format( target_file=target_file)) sys.exit(0) with temp_file.open("wb") as fd: while fd.tell() < lenght: chunk = r.read(chunk_size) fd.write(chunk) percent = (fd.tell() * 100) / lenght line = "[{percent:06.2f}%] {done:6}MB/{full}MB\r".format( percent=percent, done=int(fd.tell() / MB), full=int(lenght / MB)) print(line, end="") # noqa: T001 print("\n") # noqa: T001 temp_file.rename(target_file) try: r = urllib.request.urlopen( "https://virt-lightning.org/images/{distro}/{distro}.yaml".format( **kwargs)) except urllib.error.HTTPError as e: if e.code == 404: pass with target_file.with_suffix(".yaml").open("wb") as fd: fd.write(r.read()) print("Image {distro} is ready!".format(**kwargs)) # noqa: T001
def stop(configuration, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_network(configuration.network_name, configuration.network_cidr) hv.init_storage_pool(configuration.storage_pool) domain = hv.get_domain_by_name(kwargs["name"]) if not domain: vm_list = [d.name for d in hv.list_domains()] print("No VM called {name} in {vm_list}".format(name=kwargs["name"], vm_list=vm_list)) exit(1) hv.clean_up(domain)
def down(configuration, context, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_network(configuration.network_name, configuration.network_cidr) hv.init_storage_pool(configuration.storage_pool) for domain in hv.list_domains(): if context and domain.context != context: continue logger.info("%s purging %s", symbols.TRASHBIN.value, domain.name) hv.clean_up(domain) if bool(distutils.util.strtobool(configuration.network_auto_clean_up)): hv.network_obj.destroy()
def main(): argument_spec = dict(distro=dict(type=str), name=dict(type=str), state=dict(default='present'), root_password=dict(type=str), groups=dict(default=[], type=list), root_disk_size=dict(type=int, default=32)) configuration = virt_lightning.configuration.Configuration() module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=False) if not HAS_LIB: module.fail_json( msg='virt-lightning Python library is required for this module') state = module.params['state'] context = 'default' distro = module.params['distro'] groups = module.params['groups'] root_password = module.params[ 'root_password'] or configuration.root_password name = module.params.get('name') or re.sub(r"\W+", "", distro) vcpus = 1 memory = 512 root_disk_size = module.params['root_disk_size'] conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_network(configuration.network_name, configuration.network_cidr) hv.init_storage_pool(configuration.storage_pool) domain = hv.get_domain_by_name(name) if state == 'absent' and domain: hv.clean_up(domain) module.exit_json(changed=True) elif state == 'absent' and not domain: module.exit_json(changed=False) elif state == 'present' and domain: module.exit_json(changed=False) else: domain = create(hv, configuration, distro, context, name, root_disk_size, memory=memory, vcpus=vcpus, groups=groups) module.exit_json(changed=True, name=domain.name, ipv4=str(domain.ipv4.ip))
def ansible_inventory(configuration, context, **kwargs): hv = vl.LibvirtHypervisor(configuration.libvirt_uri) ssh_cmd_template = ( "{name} ansible_host={ipv4} ansible_username={username} " 'ansible_ssh_common_args="-o UserKnownHostsFile=/dev/null ' '-o StrictHostKeyChecking=no"') for domain in hv.list_domains(): if domain.context == context: print( # noqa: T001 ssh_cmd_template.format(name=domain.name, ipv4=domain.ipv4.ip, username=domain.username))
def up(virt_lightning_yaml, configuration, context, **kwargs): def myDomainEventAgentLifecycleCallback(conn, dom, state, reason, opaque): if state == 1: logger.info("%s %s QEMU agent found", symbols.CUSTOMS.value, dom.name()) loop = asyncio.get_event_loop() try: import libvirtaio libvirtaio.virEventRegisterAsyncIOImpl(loop=loop) except ImportError: libvirt.virEventRegisterDefaultImpl() pass conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) conn.setKeepAlive(5, 3) conn.domainEventRegisterAny( None, libvirt.VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE, myDomainEventAgentLifecycleCallback, None, ) hv.init_network(configuration.network_name, configuration.network_cidr) hv.init_storage_pool(configuration.storage_pool) pool = ThreadPoolExecutor(max_workers=10) async def deploy(): futures = [] for host in virt_lightning_yaml: futures.append( loop.run_in_executor(pool, _start_domain, hv, host, context, configuration)) domain_reachable_futures = [] for f in futures: await f domain = f.result() if domain: domain_reachable_futures.append(domain.reachable()) logger.info("%s ok Waiting...", symbols.HOURGLASS.value) await asyncio.gather(*domain_reachable_futures) loop.run_until_complete(deploy()) logger.info("%s You are all set", symbols.THUMBS_UP.value)
def up(virt_lightning_yaml, configuration, context="default", **kwargs): """ Create a list of VM """ def _lifecycle_callback(conn, dom, state, reason, opaque): # noqa: N802 if state == 1: logger.info("%s %s QEMU agent found", symbols.CUSTOMS.value, dom.name()) loop = kwargs.get("loop") or asyncio.get_event_loop() _register_aio_virt_impl(loop) conn = _connect_libvirt(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) conn.setKeepAlive(interval=5, count=3) conn.domainEventRegisterAny( None, libvirt.VIR_DOMAIN_EVENT_ID_AGENT_LIFECYCLE, _lifecycle_callback, None, ) hv.init_network(configuration.network_name, configuration.network_cidr) hv.init_storage_pool(configuration.storage_pool) _ensure_image_exists(hv, virt_lightning_yaml) pool = ThreadPoolExecutor(max_workers=10) async def deploy(): futures = [] for host in virt_lightning_yaml: futures.append( loop.run_in_executor(pool, _start_domain, hv, host, context, configuration)) domain_reachable_futures = [] for f in futures: await f domain = f.result() if domain: domain_reachable_futures.append(domain.reachable()) logger.info("%s ok Waiting...", symbols.HOURGLASS.value) await asyncio.gather(*domain_reachable_futures) loop.run_until_complete(deploy()) logger.info("%s You are all set", symbols.THUMBS_UP.value)
def down(configuration, context="default", **kwargs): """ Stop and remove a running environment. """ conn = _connect_libvirt(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_network(configuration.network_name, configuration.network_cidr) hv.init_storage_pool(configuration.storage_pool) for domain in hv.list_domains(): if domain.context != context: continue logger.info("%s purging %s", symbols.TRASHBIN.value, domain.name) hv.clean_up(domain) if bool(distutils.util.strtobool(configuration.network_auto_clean_up)): hv.network_obj.destroy()
def stop(configuration, **kwargs): """ Stop a given VM """ conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_network(configuration.network_name, configuration.network_cidr) hv.init_storage_pool(configuration.storage_pool) domain = hv.get_domain_by_name(kwargs["name"]) if not domain: vm_list = [d.name for d in hv.list_domains()] if vm_list: logger.info("No VM called %s in: %s", kwargs["name"], ", ".join(vm_list)) else: logger.info("No running VM.") raise VMNotFound() hv.clean_up(domain)
def status(configuration, context="default", name=None, **kwargs): """ Returns the status of the VM of the envionment """ conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) for domain in hv.list_domains(): if context and context != domain.context: continue name = domain.name if not domain.ipv4: # Not a VL managed VM continue yield { "name": name, "ipv4": domain.ipv4 and str(domain.ipv4.ip), "context": domain.context, "username": domain.username, }
def start(configuration, context, **kwargs): conn = libvirt.open(configuration.libvirt_uri) hv = vl.LibvirtHypervisor(conn) hv.init_network(configuration.network_name, configuration.network_cidr) hv.init_storage_pool(configuration.storage_pool) host = { k: kwargs[k] for k in ["name", "distro", "memory", "vcpus"] if kwargs.get(k) } domain = _start_domain(hv, host, context, configuration) if not domain: return loop = asyncio.get_event_loop() if not kwargs["noconsole"]: import time time.sleep(4) stream = conn.newStream(libvirt.VIR_STREAM_NONBLOCK) console = domain.dom.openConsole(None, stream, 0) import libvirtaio libvirtaio.virEventRegisterAsyncIOImpl(loop=loop) def stream_callback(stream, events, _): content = stream.recv(1024).decode() sys.stdout.write(content) stream.eventAddCallback(libvirt.VIR_STREAM_EVENT_READABLE, stream_callback, console) async def deploy(): await domain.reachable() loop.run_until_complete(deploy()) print( # noqa: T001 ("\033[0m\n**** System is online ****\n" "To connect use:\n" " vl console {name} (virsh console)" " vl ssh {name}").format(name=domain.name)) if kwargs["ssh"]: domain.exec_ssh()