pax_global_header00006660000000000000000000000064143706467540014532gustar00rootroot0000000000000052 comment=37b331a2f5bd61b16539aba0da62d17243c90e25 distribution-management-modules-0.1.1/000077500000000000000000000000001437064675400200305ustar00rootroot00000000000000distribution-management-modules-0.1.1/.gitignore000066400000000000000000000000701437064675400220150ustar00rootroot00000000000000*__pycache__* *.deb dmm_highvoltage.egg-info build dist distribution-management-modules-0.1.1/COPYRIGHT000066400000000000000000000013361437064675400213260ustar00rootroot00000000000000Copyright 2022-2023 Jonathan Carter Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. distribution-management-modules-0.1.1/MANIFEST.in000066400000000000000000000000571437064675400215700ustar00rootroot00000000000000graft dmm prune tests global-exclude *.py[co] distribution-management-modules-0.1.1/README.md000066400000000000000000000065401437064675400213140ustar00rootroot00000000000000Welcome to distribution management modules! ------------------------------------------- This is all still in a very alpha state, but, you could probably already use it to build some basic system images. The goal is to include various useful modules that are typically used for creating images (containers, boot images, VMs, live images), installing bare metal machines and building and publishing packages. The initial release aims to have modules for grub, squashfs, {de/mm/etc}bootstrap, apt, dd, xorisso and similar tools. The dmm.py python module along with the dmm-perform-recipe tool allows you to string together actions for these modules in one larger recipe. The modules indluded in dmm are meant for generic common tasks when it comes to putting together a distribution. They are not meant to be general python modules for the tools mentioned above. For example, if you are building a tool that specifically needs to interact with APT, then you should use something like the python3-apt package instead. modules ------- Modules are stored in subdirectories under src/modules. They are simply python modules. The collection of modules shipped with distro-installer are considered as the standard dmm modules collection. 3rd party modules can be added to distro-installer or even replace standard ones by simple configuration. DMM is completely modular. This means you can choose to use partman or fdisk or your own module for the partitioner. Or you can choose between something like using debootstrap to install a system, or extract from tarball. You can also use a combination of modules to get the best of either. Each module takes an action, and along with that some parameters for that action. For example, if you call the apt module, you can specify an action to update, remove or install packages, with the parameters being the packages that you'd like to add or remove. Some parameters might be optional and others mandatory. More module documentation will follow! :) anatomy of a module ------------------- A module is just a bunch of functions, it should call it's own init function when imported that does the minimum initiation needed. It must have a depends() function that returns the list of dependencies it needs, and specify both a description of why it's needed and whether it's required or optional. Later on, dmm will be able to tell you which Debian packages it needs to perform the actions it needs based on the enabled modules. dependencies ------------ core (essential): python3-yaml python3-loguru dependend on task (recommended): squashfs-tools xorriso reprepro grub-common mtools grub-pc-bin uuid-runtime debootstrap debsums licence ------- Copyright 2022 Jonathan Carter Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. distribution-management-modules-0.1.1/dmm/000077500000000000000000000000001437064675400206055ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/__init__.py000066400000000000000000000000001437064675400227040ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/dmm.py000066400000000000000000000033731437064675400217420ustar00rootroot00000000000000""" DMM Recipe runner """ # Imports import importlib import sys import yaml from loguru import logger # Initialize logger logger.add(sys.stderr, format="{time} {level} {message}", filter="my_module", level="INFO") logger.add("out.log", backtrace=True, diagnose=True) @logger.catch def read_recipe(recipe): """ Read the list of stuff that we should be doing. """ logger.info("Reading recipe: " + recipe) configfile = open(recipe, "r") global config config = (yaml.safe_load(configfile)) logger.debug("Config:\n" + str(config)) sys.path.append(config['module_path']) logger.debug("Add-on module path(s): ", config['module_path']) return list(config['recipe']) @logger.catch def perform_recipe(recipe): """ Runs through the tasks in our recipe. """ for task in read_recipe(recipe): run_task(task) logger.info("Recipe: %s has completed." % recipe) @logger.catch def get_task_config(task): """ Returns configuration for a module. """ return config['recipe'][task] @logger.catch def run_task(task): """ Run through a task in a recipe. We load a module as needed and then unload it when we're done to conserve memory for installer environments. """ logger.info("Running task: " + task) module_name = config['recipe'][task]['module'] globals()[module_name] = importlib.import_module("dmm.modules." + module_name + "." + module_name) task_config = get_task_config(task) logger.debug("Task details: " + str(task_config)) #try: eval(module_name).recipe_run(task_config, config['global_settings']) #except: # logger.error("An error has occured!") # #sys.exit() del globals()[module_name] logger.debug("DMM Recipe reader loaded") distribution-management-modules-0.1.1/dmm/modules/000077500000000000000000000000001437064675400222555ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/aptpkg/000077500000000000000000000000001437064675400235435ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/aptpkg/__init__.py000066400000000000000000000000001437064675400256420ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/aptpkg/aptpkg.py000066400000000000000000000047101437064675400254050ustar00rootroot00000000000000""" apt functions for for dmm. """ import os import subprocess def init(): """ Initialization for aptsetup module """ def install(chroot, packages): """ Install APT packages. chroot - specify chroot to do this in, False for host. """ if chroot: result = os.system('LANG=C LANGUAGE=C LC_TYPE=C LC_MESSAGES=C ' 'LC_ALL=C DEBIAN_FRONTEND=noninteractive ' 'chroot %s apt-get -y install %s' % (chroot, packages)) return result else: result = os.system('LANG=C LANGUAGE=C LC_TYPE=C LC_MESSAGES=C ' 'LC_ALL=C DEBIAN_FRONTEND=noninteractive ' 'apt-get -y install %s' % (packages)) return result def clean(chroot): """ Clean apt archives. chroot - specify chroot to do this in, False for host. """ if chroot: os.system('chroot %s apt-get clean' % chroot) else: os.system('apt-get clean') def update(chroot): """ Update APT archives. """ if chroot: os.system('chroot %s apt-get -q update' % (chroot)) else: os.system('apt-get -q update') def download_only(chroot, packages): """ Download packages for apt cache. For download-only, we do this one package at a time, since it's possible that some packages might conflict, and you might want to ship conflicting files on media. chroot - specify chroot to do this in, False for host. """ for package in packages.split(): if chroot: os.system('chroot %s apt-get -d -y --reinstall install %s' % (chroot, package)) else: os.system('apt-get -d -y --reinstall install %s' % (package)) def recipe_run(config, globalconf): """ Perform actions for apt module """ if config['action'] == 'install': if config['download_only']: # The use case for download only is mostly for cases like live # media preperation where we want to set up a package pool for # the media download_only(globalconf['chroot'], config['packages']) else: install(globalconf['chroot'], config['packages']) elif config['update-sources']: update(globalconf['chroot']) if config['action'] == 'clean': clean(globalconf['chroot']) if config['clean_cache']: clean(globalconf['chroot']) init() distribution-management-modules-0.1.1/dmm/modules/aptsetup/000077500000000000000000000000001437064675400241225ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/aptsetup/__init__.py000066400000000000000000000000001437064675400262210ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/aptsetup/aptsetup.py000066400000000000000000000012071437064675400263410ustar00rootroot00000000000000""" aptsetup functions for for dmm. """ import os import subprocess def init(): """ Initialization for aptsetup module """ def recipe_run(config, globalconf): """ Perform actions for aptsetup module """ text_file = open(config['chroot'] + config['sources-file'], "w") sources_write = text_file.write(config['sources-list']) text_file.close() if config['update-sources']: result = subprocess.run("chroot %s apt-get -q update" % (config['chroot']), capture_output=True, text=True, shell=True) print(result.stdout) return result.returncode init() distribution-management-modules-0.1.1/dmm/modules/cfdisk/000077500000000000000000000000001437064675400235205ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/cfdisk/__init__.py000066400000000000000000000000001437064675400256170ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/cfdisk/cfdisk.py000066400000000000000000000002001437064675400253250ustar00rootroot00000000000000""" For now, simply run cfdisk. """ import os def recipe_run(): """ Run cfdisk """ os.system('/sbin/cfdisk') distribution-management-modules-0.1.1/dmm/modules/checksums/000077500000000000000000000000001437064675400242425ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/checksums/__init__.py000066400000000000000000000000001437064675400263410ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/checksums/checksums.py000066400000000000000000000014611437064675400266030ustar00rootroot00000000000000""" Generate checksums module for dmm. """ import os def generate_checksums_file(sumbinary, path, output, cut): """ Create a file that contains checksums: - sumbinary: md5sum, sha1sum, sha256sum - path: path that will be checksummed - output: where the checksums will be outputted to if empty, the result is returned - exclude: a list of lines that will include these strings will be excluded """ checksum_output = os.popen("find %s -type f | xargs %s" % (path, sumbinary)).read() checksum_output_cut = checksum_output.replace(cut, '') if output == "": return (checksum_output_cut) else: file = open(output, "w+") file.write(checksum_output_cut) file.close() def confirm_checksums(file): #TODO pass distribution-management-modules-0.1.1/dmm/modules/dd/000077500000000000000000000000001437064675400226445ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/dd/__init__.py000066400000000000000000000000001437064675400247430ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/dd/dd.py000066400000000000000000000016051437064675400236070ustar00rootroot00000000000000""" Python wrapper for dd, usually to create large empty disk image """ import subprocess def create_file(ifile, ofile, bs, count, timeout): """ Create or overwrite a file using dd. As with dd: - ifile: inputfile - ofile: outputfile - bs: size of block - count: how many blocks should be written - timeout: make the operation timeout after amount of seconds """ dd_output = subprocess.run(["/usr/bin/dd", "if=" + ifile, "of=" + ofile, "bs=" + bs, "count=" + count], check=True, timeout=timeout, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True,) return (dd_output.returncode, dd_output.stdout, dd_output.stderr) distribution-management-modules-0.1.1/dmm/modules/debootstrap/000077500000000000000000000000001437064675400246035ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/debootstrap/__init__.py000066400000000000000000000000001437064675400267020ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/debootstrap/debootstrap.py000066400000000000000000000010071437064675400275010ustar00rootroot00000000000000""" debootstrap functions for for dmm. """ import os def init(): """ Initialization for debootstrap module """ def recipe_run(config, globalconf): """ Perform actions for debootstrap module """ os.system("debootstrap %s %s %s %s" % (config['debootstrapopts'], config['release'], config['destination'], config['mirror'])) init() distribution-management-modules-0.1.1/dmm/modules/download/000077500000000000000000000000001437064675400240645ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/download/download.py000066400000000000000000000013321437064675400262440ustar00rootroot00000000000000""" download remote content for dmm """ import os import pathlib def init(): """ Initialization for download module """ def recipe_run(config, globalconf): """ Perform actions for download module """ if config['action'] == 'download_files': for download in config['files']: download_file(download['url'], download['destination']) def download_file(url, destination): """ Download file using curl and store it at location """ print("Downloading %s to %s" % (url, destination)) # Ensure that base path exists path = pathlib.Path(destination).parent path.mkdir(parents=True, exist_ok=True) os.system("curl %s -o %s" % (url, destination)) init() distribution-management-modules-0.1.1/dmm/modules/dpkgsetup/000077500000000000000000000000001437064675400242635ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/dpkgsetup/__init__.py000066400000000000000000000000001437064675400263620ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/dpkgsetup/dpkgsetup.py000066400000000000000000000005701437064675400266450ustar00rootroot00000000000000""" dpkgsetup functions for for dmm. """ import os def init(): """ Initialization for dpkgsetup module """ def recipe_run(config, globalconf): """ Perform actions for dpkgsetup module """ text_file = open(config['chroot'] + config['options-file'], "w") options_write = text_file.write(config['dpkg-options']) text_file.close() init() distribution-management-modules-0.1.1/dmm/modules/fallocate/000077500000000000000000000000001437064675400242075ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/fallocate/fallocate.py000066400000000000000000000006561437064675400265220ustar00rootroot00000000000000""" fallocate functions for dmm. """ import os def init(): """ Initialization for fallocate module """ pass def create_file(size, destination): """ Create an empty sparse file. """ os.system("fallocate -l %s %s" % (size, destination)) def recipe_run(config, globalconf): """ Perform actions for fallocate module """ create_file(config['size'], config['destination']) init() distribution-management-modules-0.1.1/dmm/modules/grub/000077500000000000000000000000001437064675400232145ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/grub/grub.py000066400000000000000000000034621437064675400245320ustar00rootroot00000000000000""" grub functions for for dmm. """ import os def init(): """ Initialization for grub module """ def recipe_run(config, globalconf): """ Perform actions for grub module """ if config['action'] == "install": os.system("chroot %s apt-get -y -qq install grub-%s" % (config['chroot'], config['platform'])) os.system("chroot %s update-grub" % config['chroot']) if config['platform'] == "efi": return os.system("chroot %s grub-install --target=x86_64-efi" % config['chroot']) if config['action'] == "mkrescue": return os.system('grub-mkrescue --modules="multiboot fat linux part_msdos minicmd ls iso9660" --output="%s" %s' % (config['isopath'], globalconf["live-media-path"])) if config['action'] == "create_legacy_boot_img": setup_grub_legacy_boot(globalconf["live-media-path"]) def setup_grub_legacy_boot(path): """ Create PC BIOS (amd64/i386 legacy) bootable grub image. """ # TODO: should probably happen inside the chroot? os.system('grub-mkstandalone --directory=/usr/lib/grub/i386-pc --format=i386-pc ' '--themes="" --fonts="" --locales="" --modules="linux normal iso9660 ' 'biosdisk search png gfxmenu" --install-modules="linux normal iso9660 ' 'biosdisk memdisk search tar ls png gfxmenu" --output core.img') os.system('cat /usr/lib/grub/i386-pc/cdboot.img core.img > bios.img') def depends(): """ Returns a list of dependencies for this module. Currently this is Debian packages. """ return ({'xorriso': {"priority": "required", \ "description": "command line ISO-9660 and Rock Ridge manipulation tool"}, 'mtools': {"priority": "required", \ "description": "Tools for manipulating MSDOS files"} }) init() distribution-management-modules-0.1.1/dmm/modules/linux/000077500000000000000000000000001437064675400234145ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/linux/__init__.py000066400000000000000000000000001437064675400255130ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/linux/linux.py000066400000000000000000000004611437064675400251260ustar00rootroot00000000000000""" mkfs functions for for dmm. """ import os def init(): """ Initialization for linux module """ def recipe_run(config, globalconf): """ Perform actions for linux module """ os.system("chroot %s apt-get -y -qq install %s" % (config['chroot'], config['package'])) init() distribution-management-modules-0.1.1/dmm/modules/livedisk/000077500000000000000000000000001437064675400240675ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/livedisk/__init__.py000066400000000000000000000000001437064675400261660ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/livedisk/livedisk.py000066400000000000000000000023721437064675400262570ustar00rootroot00000000000000""" livedisk functions for for dmm. """ import pathlib import os from shutil import copyfile def init(): """ Initialization for livedisk module """ def recipe_run(config, globalconf): """ Perform actions for livedisk module """ # Create working directory for live media path = pathlib.Path(globalconf['live-media-path'] + "/live") path.mkdir(parents=True, exist_ok=True) # copy squashfs, kernel and initramfs into livedir print("Current working directory is " + os.getcwd()) squashfs = globalconf['squashfs'] kernel = globalconf['chroot'] + "/boot/vmlinuz*" initrd = globalconf['chroot'] + "/boot/initrd*" livepath = globalconf['live-media-path'] + "/live/" os.system("cp %s %s" % (squashfs, livepath + "/filesystem.squashfs")) os.system("cp %s %s" % (kernel, livepath + "/vmlinuz")) os.system("cp %s %s" % (initrd, livepath + "/initrd.gz")) # Copy template directory to live media teamplate_dir = config['template_dir'] os.system("cp -r %s/ %s" % (config['template_dir'] + "/*", globalconf['live-media-path'])) # Update initramfs if config['update-initramfs']: os.system("chroot %s update-initramfs -u" % globalconf['chroot']) # TODO: create md5sums init() distribution-management-modules-0.1.1/dmm/modules/losetup/000077500000000000000000000000001437064675400237505ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/losetup/__init__.py000066400000000000000000000000001437064675400260470ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/losetup/losetup.py000066400000000000000000000006351437064675400260210ustar00rootroot00000000000000""" losetup functions for for dmm. """ import os def init(): """ Initialization for losetup module """ def recipe_run(config, globalconf): """ Perform actions for losetup module """ if config['function'] == 'unmount': os.system("losetup -d /dev/%s" % config['loopdev']) else: os.system("losetup -P /dev/%s %s" % (config['loopdev'], config['diskimg'])) init() distribution-management-modules-0.1.1/dmm/modules/lsblk/000077500000000000000000000000001437064675400233645ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/lsblk/__init__.py000066400000000000000000000000001437064675400254630ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/lsblk/lsblk.py000066400000000000000000000012251437064675400250450ustar00rootroot00000000000000""" Show disk / partition information. """ import subprocess def list_scsi_devices(): """ Just return a list of available scsi disks. """ lsblk_output = subprocess.check_output(["/bin/lsblk", "--scsi", "-noheadings", "--output", "NAME", "-n"]).decode("utf-8").split("\n") scsi_devices = [x for x in lsblk_output if x] return scsi_devices def get_device_information(device): """ Get ..." """ def list_partitions(): """ TODO: We need to list the partitions somehow so that we can present a disk to run cfdisk on. """ distribution-management-modules-0.1.1/dmm/modules/mkfs/000077500000000000000000000000001437064675400232155ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/mkfs/__init__.py000066400000000000000000000000001437064675400253140ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/mkfs/mkfs.py000066400000000000000000000005521437064675400245310ustar00rootroot00000000000000""" mkfs functions for for dmm. """ import os def init(): """ Initialization for mkfs module """ def recipe_run(config, globalconf): """ Perform actions for mkfs module """ for filesystem in config['partitions']: os.system("mkfs.%s %s %s" % (filesystem['fstype'], filesystem['options'], filesystem['partition'])) init() distribution-management-modules-0.1.1/dmm/modules/mmdebstrap/000077500000000000000000000000001437064675400244135ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/mmdebstrap/__init__.py000066400000000000000000000000001437064675400265120ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/mmdebstrap/mmdebstrap.py000066400000000000000000000010021437064675400271140ustar00rootroot00000000000000""" mmdebstrap functions for for dmm. """ import os def init(): """ Initialization for mmdebstrap module """ def recipe_run(config, globalconf): """ Perform actions for mmdebstrap module """ os.system("mmdebstrap %s %s %s %s" % (config['debootstrapopts'], config['release'], config['destination'], config['mirror'])) init() distribution-management-modules-0.1.1/dmm/modules/mountfs/000077500000000000000000000000001437064675400237505ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/mountfs/__init__.py000066400000000000000000000000001437064675400260470ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/mountfs/mountfs.py000066400000000000000000000023121437064675400260130ustar00rootroot00000000000000""" mountfs functions for for dmm. """ import os import pathlib def init(): """ Initialization for mountfs module """ #def recipe_run(config, globalconf): # """ # Perform actions for mountfs module # """ # print(config['mount']) # for filesystem in config['mount']: # print(config['mount'][filesystem]['mountpoint']) # path = pathlib.Path(config['mount'][filesystem]['mountpoint']) # path.mkdir(parents=True, exist_ok=True) # os.system("mount -t %s %s %s %s" % (config['mount'][filesystem]['fstype'], # config['mount'][filesystem]['mountopts'], # config['mount'][filesystem]['source'], # config['mount'][filesystem]['mountpoint'])) def recipe_run(config, globalconf): """ Perform actions for mountfs module """ for filesystem in config['partitions']: path = pathlib.Path(filesystem['mountpoint']) path.mkdir(parents=True, exist_ok=True) os.system("mount -t %s %s %s %s" % (filesystem['fstype'], filesystem['mountopts'], filesystem['source'], filesystem['mountpoint'])) init() distribution-management-modules-0.1.1/dmm/modules/networking/000077500000000000000000000000001437064675400244445ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/networking/__init__.py000066400000000000000000000000001437064675400265430ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/networking/networking.py000066400000000000000000000005571437064675400272140ustar00rootroot00000000000000""" networking functions for for dmm. """ import os def init(): """ Initialization for networking module """ def recipe_run(config, globalconf): """ Perform actions for aptsetup module """ text_file = open(config['chroot'] + "/etc/hostname", "w") hostname_write = text_file.write(config['hostname']) text_file.close() init() distribution-management-modules-0.1.1/dmm/modules/parted/000077500000000000000000000000001437064675400235345ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/parted/__init__.py000066400000000000000000000000001437064675400256330ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/parted/parted.py000066400000000000000000000005171437064675400253700ustar00rootroot00000000000000""" parted functions for for dmm. """ import os def init(): """ Initialization for parted module """ def recipe_run(config, globalconf): """ Creates partitions on device. """ for partedcmd in config['static-layout']: os.system("parted -s -- %s %s" % (config['destination'], partedcmd)) init() distribution-management-modules-0.1.1/dmm/modules/readme/000077500000000000000000000000001437064675400235125ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/readme/__init__.py000066400000000000000000000000001437064675400256110ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/readme/readme.py000066400000000000000000000012741437064675400253250ustar00rootroot00000000000000""" readme functions for for dmm. """ import os from datetime import date # TODO: the date should come from our framework def init(): """ Initialization for readme module """ def recipe_run(config, globalconf): """ Perform actions for readme module """ template = config['template'] destination = config['destination'] + "/" filename = config['filename'] product = config['name'] version = config['version'] readme = open(template).read().replace("__PRODUCT__", product).replace("__VERSION__", version).replace("__DATE__", str(date.today())) readme_w = open(destination + filename, 'w') readme_w.write(readme) readme_w.close() init() distribution-management-modules-0.1.1/dmm/modules/remove/000077500000000000000000000000001437064675400235525ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/remove/remove.py000066400000000000000000000005751437064675400254300ustar00rootroot00000000000000""" delete functions for for dmm. """ import os def init(): """ Initialization for delete module """ def recipe_run(config, globalconf): """ Perform recipe actions for delete module """ for path in config['paths']: delete_path(path['path']) def delete_path(path): print("Removing %s" % path) os.system("rm -rf %s" % path) init() distribution-management-modules-0.1.1/dmm/modules/reprepro/000077500000000000000000000000001437064675400241135ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/reprepro/reprepro.py000066400000000000000000000035131437064675400263250ustar00rootroot00000000000000""" reprepro functions for for dmm. """ import os import pathlib def init(): """ Initialization for reprepro module """ def recipe_run(config, globalconf): """ Perform actions for reprepro module """ #TODO: break all these into smaller functions outside this one pkgpool_path = globalconf['workspace'] + "/pkgpool" path = pathlib.Path(globalconf['workspace'] + "/pkgpool/conf") path.mkdir(parents=True, exist_ok=True) package_path = config['prepend_path'] + "/" + config['package_path'] version = globalconf['release'] architectures = globalconf['architecture'] components = config['components'] description = globalconf['description'] name = globalconf['name'] reprepro_config = ("Origin: %s\nLabel: %s\nSuite: %s\nVersion: %s\nDescription: %s\n" "Codename: %s\nComponents: %s\nArchitectures: %s\nUDebComponents: %s\n" % (name, name, version, version, description, version, components, architectures, components)) text_file = open(pkgpool_path + "/conf/distributions", "w") sources_write = text_file.write(reprepro_config) text_file.close() path = pathlib.Path(config['live-media-path']) path.mkdir(parents=True, exist_ok=True) # generate package pool # - usual debs: os.system('reprepro --ignore=extension -b %s includedeb %s %s' % (pkgpool_path, version, package_path + "/*.deb")) # - udebs, for installer: os.system('reprepro --ignore=extension -b %s includeudeb %s %s' % (pkgpool_path, version, package_path + "/*.udeb")) # Copy pkgpool to media os.system('cp -r %s %s %s' % (pkgpool_path + "/dists", pkgpool_path + "/pool", config['live-media-path'] + "/")) if config['clean_package_path']: os.system('rm %s/*.deb' % (config['prepend_path'] + config['package_path'])) init() distribution-management-modules-0.1.1/dmm/modules/squashfs/000077500000000000000000000000001437064675400241125ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/squashfs/squashfs.py000066400000000000000000000011541437064675400263220ustar00rootroot00000000000000""" mksquashfs functions for for dmm. """ import os def init(): """ Initialization for mksquashfs module """ def recipe_run(config, globalconf): """ Perform actions for mksquashfs module """ if config['action'] == 'make-squashfs': blocksize = config['blocksize'] compress = config['compression-method'] source = config['source'] dest = config['destination'] opts = config['options'] os.system("mksquashfs %s %s -b %s -comp %s %s" % (source, dest, blocksize, compress, opts)) #if config['action'] == unsquashfs: # todo init() distribution-management-modules-0.1.1/dmm/modules/umountfs/000077500000000000000000000000001437064675400241355ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/umountfs/__init__.py000066400000000000000000000000001437064675400262340ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/umountfs/umountfs.py000066400000000000000000000004671437064675400263760ustar00rootroot00000000000000""" umountfs functions for for dmm. """ import os def init(): """ Initialization for umountfs module """ def recipe_run(config, globalconf): """ Perform actions for umountfs module """ for mount in config['mounts']: os.system("umount %s" % (mount['mountpoint'])) init() distribution-management-modules-0.1.1/dmm/modules/users/000077500000000000000000000000001437064675400234165ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/users/__init__.py000066400000000000000000000000001437064675400255150ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/users/users.py000066400000000000000000000015001437064675400251250ustar00rootroot00000000000000""" aptsetup functions for for dmm. """ import os def init(): """ Initialization for aptsetup module """ def recipe_run(config, globalconf): """ Perform actions for aptsetup module """ for user in config['users']: print("username: " + user['username']) password_hash = os.popen("mkpasswd --method=SHA-512 " + user['password']).read().replace('\n', '') os.system("chroot /tmp/disk useradd -p '%s' %s" % (password_hash, user['username'])) os.system("chroot /tmp/disk mkdir home/%s" % user['username']) os.system("chroot /tmp/disk chown %s home/%s" % (user['username'], user['username'])) if user['sudo']: os.system("chroot /tmp/disk adduser %s sudo" % user['username']) os.system("chroot /tmp/disk apt-get install sudo") init() distribution-management-modules-0.1.1/dmm/modules/vmsetup/000077500000000000000000000000001437064675400237605ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/vmsetup/__init__.py000066400000000000000000000000001437064675400260570ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/vmsetup/vmsetup.py000066400000000000000000000020471437064675400260400ustar00rootroot00000000000000""" vmsetup functions for for dmm. """ import os def init(): """ Initialization for vmsetup module """ def recipe_run(config, globalconf): """ Misc setup tasks for virtual machines """ chroot = config['chroot'] os.system("sed -i 's/%s/%s/g' %s/boot/grub/grub.cfg" % (config['root-during-build'], config['root-dev-in-vm'], chroot)) os.system("echo '/dev/%s / ext4 defaults 0 0' > %s/etc/fstab" % (config['root-dev-in-vm'], chroot)) # qemu's bios only reads the EFI files if it can find them in the right place, and # vfat doesn't support symlinks os.system("cp -r %s/boot/efi/EFI/debian %s/boot/efi/EFI/boot" % (chroot, chroot)) os.system("cp -r %s/boot/efi/EFI/boot/grubx64.efi %s/boot/efi/EFI/boot/bootx64.efi" % (chroot, chroot)) os.system(("sed -i 's/quiet/quiet console=ttyS0/g' %s/etc/default/grub") % chroot) os.system("chroot %s /usr/sbin/update-grub" % chroot) init() distribution-management-modules-0.1.1/dmm/modules/writefile/000077500000000000000000000000001437064675400242475ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/writefile/writefile.py000066400000000000000000000012741437064675400266170ustar00rootroot00000000000000""" write some data to files for dmm """ import pathlib # todo: add options to allow appending/replacing def init(): """ Initialization for writefile module """ def recipe_run(config, globalconf): """ Perform actions for writefile module """ if config['action'] == 'write_files': for file in config['files']: write_file(file['destination'], file['content']) def write_file(file, content): """ Writes one or more lines to a file. """ # Ensure that basepath exists first path = pathlib.Path(file).parent path.mkdir(parents=True, exist_ok=True) file = open(file, "w+") file.write(content) file.close() init() distribution-management-modules-0.1.1/dmm/modules/xorriso/000077500000000000000000000000001437064675400237625ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/xorriso/__init__.py000066400000000000000000000000001437064675400260610ustar00rootroot00000000000000distribution-management-modules-0.1.1/dmm/modules/xorriso/xorriso.py000066400000000000000000000015061437064675400260430ustar00rootroot00000000000000""" xorisso functions for dmm (not yet implemented) """ import os def init(): """ Initialization for xorisso module """ def recipe_run(config, globalconf): """ xorrisso actions: - geniso generates an iso image """ if config['action'] == "geniso": generate_iso(config["isopath"], config["live-media-path"]) def generate_iso(isopath, live_media_path): """ Generate an ISO image using xorriso """ print("xorriso -outdev %s -volid d-live testing ci amd64 -padding 0 -compliance no_emul_toc -map %s -chmod 0755 / -- -boot_image isolinux dir=/isolinux -boot_image isolinux system_area=/usr/lib/grub/i386-pc/boot_hybrid.img -boot_image any next -boot_image any efi_path=boot/grub/efi.img -boot_image isolinux partition_entry=gpt_basdat % (isopath, live-media-path)") init() distribution-management-modules-0.1.1/examples/000077500000000000000000000000001437064675400216465ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/aims-desktop-2020-amd64.dmm000077500000000000000000000115671437064675400262540ustar00rootroot00000000000000#!/usr/bin/env dmm-perform-recipe ### # DMM Recipe to build AIMS Desktop # You can get the latest version of this file at: # https://git.aims.ac.za/aims-desktop/aims-debmower/aims-desktop-2020-amd64.dmm ### #TODO: # Remove build files like dpkg options # write a final sources.list # perform md5sum tests # report with debsums and sizes module_path: - local global_settings: name: &name AIMS Desktop version: &version "2020.2" description: &description Live installer image chroot: &chroot /tmp/debmower/disk live-media-path: &live-media-path /tmp/debmower/live-media squashfs: &squashfs /tmp/debmower/filesystem.squashfs architecture: &architecture amd64 workspace: /tmp/debmower/ release: &release buster additional_modules: /usr/local/example/path recipe: install_debian: module: debootstrap destination: *chroot release: buster mirror: http://deb.debian.org/debian debootstrapopts: '' setup_network: module: networking chroot: *chroot hostname: live dpkg_setup: module: dpkgsetup chroot: *chroot dpkg-options: | Dpkg::Options { "--force-confdef"; "--force-confold"; "--force-unsafe-io"; } options-file: /etc/apt/apt.conf.d/buildoptions apt_setup: module: aptsetup chroot: *chroot sources-list: | deb http://deb.debian.org/debian buster main contrib non-free deb http://deb.debian.org/debian buster-updates main contrib non-free deb http://deb.debian.org/debian buster-backports main contrib non-free deb http://security.debian.org/debian buster-updates main contrib non-free deb [trusted=yes] http://ppa.aims.ac.za/aims-ppa buster main sources-file: /etc/apt/sources.list update-sources: True setup_bind_mounts: module: mountfs partitions: - dev: source: /dev mountpoint: /tmp/debmower/disk/dev mountopts: '' fstype: devtmpfs - proc: source: /proc mountpoint: /tmp/debmower/disk/proc mountopts: '' fstype: proc - sys: source: /sys mountpoint: /tmp/debmower/disk/sys mountopts: '' fstype: sysfs install_main_meta: module: aptpkg action: install packages: aims-desktop gnome grub-efi-amd64-bin esmtp antiword- catdoc- cython-doc- docutils-doc- ecl-doc- gmp-doc- mercurial- sagetex-doc- sphinx-doc- texlive-fonts-extra-doc- texlive-latex-base-doc- texlive-latex-recommended-doc- texlive-latex-extra-doc- texlive-pictures-doc- texlive-pstricks-doc- texlive-publishers-doc- texlive-science-doc- qmail- clean_cache: True download_only: False setup_live_packages: module: aptpkg action: install packages: aims-live clean_cache: True download_only: False install_linux: module: linux chroot: *chroot package: linux-image-amd64 linux-headers-amd64 download_pool_packages: module: aptpkg action: install packages: grub-efi efibootmgr grub-efi-amd64 grub-efi-amd64-bin grub-pc-bin clean_cache: False download_only: True setup_package_pool: module: reprepro prepend_path: *chroot package_path: /var/cache/apt/archives architectures: *architecture description: *description components: main live-media-path: *live-media-path clean_package_path: True #clean_apt: # module: aptsetup # chroot: *chroot # sources-list: '' # sources-file: /etc/apt/sources.list # update-sources: True unmount_filesystems: module: umountfs mounts: - proc: mountpoint: /tmp/debmower/disk/proc - sys: mountpoint: /tmp/debmower/disk/sys - dev: mountpoint: /tmp/debmower/disk/dev make_squashfs: module: squashfs action: make-squashfs blocksize: 1048576 compression-method: zstd source: *chroot destination: *squashfs options: -noappend prepare_live_environment: module: livedisk template_dir: './live_template' update-initramfs: False generate_readme: module: readme template: ./live_template/README.txt destination: *live-media-path filename: README.txt name: *name version: *version generate_checksums: module: checksums checksum_binary: md5sum checksum_path: *live-media-path checksum_output: /tmp/debmower/live-media/md5sums.txt cut: *live-media-path make_iso_fs: module: grub action: mkrescue distribution-management-modules-0.1.1/examples/debian-live-12-amd64-xfce.dmm000077500000000000000000000141541437064675400266100ustar00rootroot00000000000000#!/usr/bin/env dmm-perform-recipe module_path: - local global_settings: name: &name Debian version: &version bookworm (bookworm) description: &description Live Xfce installer image (bookworm) live-media-path: &live-media-path /tmp/debmower/live-media architecture: &architecture amd64 workspace: /tmp/debmower/ chroot: &chroot /tmp/debmower/disk squashfs: &squashfs /tmp/debmower/filesystem.squashfs release: &release bookworm apt_depends: debootstrap reprepro squashfs-tools mtools xorriso curl recipe: install_debian: module: debootstrap destination: *chroot release: *release mirror: http://deb.debian.org/debian debootstrapopts: '' # setup_proxy: # module: writefile # action: write_files # files: # - proxy: # content: Acquire::http::proxy "http://localhost:3142"; # destination: /tmp/debmower/disk/etc/apt/apt.conf.d/02proxy setup_network: module: networking chroot: *chroot hostname: live apt_setup: module: aptsetup chroot: *chroot sources-list: | deb http://deb.debian.org/debian bookworm main deb http://deb.debian.org/debian bookworm main/debian-installer sources-file: /etc/apt/sources.list update-sources: True setup_bind_mounts: module: mountfs partitions: - dev: source: /dev mountpoint: /tmp/debmower/disk/dev mountopts: '' fstype: devtmpfs - proc: source: /proc mountpoint: /tmp/debmower/disk/proc mountopts: '' fstype: proc - sys: source: /sys mountpoint: /tmp/debmower/disk/sys mountopts: '' fstype: sysfs install_main_meta: module: aptpkg action: install packages: live-task-xfce clean_cache: True download_only: False setup_live_packages: module: aptpkg action: install packages: calamares calamares-settings-debian live-boot live-config-systemd live-tools user-setup live-config live-boot-initramfs-tools grub-common live-task-recommended live-task-localisation grub-common grub-efi-amd64 grub-pc-bin clean_cache: True download_only: False install_linux: module: linux chroot: *chroot package: linux-image-amd64 download_pool_packages: module: aptpkg action: install packages: grub-efi efibootmgr grub-efi-amd64 grub-efi-amd64-bin grub-pc-bin grub-pc clean_cache: False download_only: True download_pool_udeb_packages: module: aptpkg action: install packages: netcfg ethdetect pcmciautils-udeb live-installer udpkg clean_cache: False download_only: True setup_package_pool: module: reprepro prepend_path: *chroot package_path: /var/cache/apt/archives architectures: *architecture description: *description components: main live-media-path: *live-media-path clean_package_path: True clean_apt: module: aptsetup chroot: *chroot sources-list: '' sources-file: /etc/apt/sources.list update-sources: True clean_chroot_environment: module: remove action: delete paths: - proxyconf: path: /tmp/debmower/disk/etc/apt/apt.conf.d/02proxy unmount_filesystems: module: umountfs mounts: - proc: mountpoint: /tmp/debmower/disk/proc - sys: mountpoint: /tmp/debmower/disk/sys - dev: mountpoint: /tmp/debmower/disk/dev make_squashfs: module: squashfs action: make-squashfs blocksize: 131072 compression-method: zstd source: *chroot destination: *squashfs options: -noappend download_debian_installer: module: download action: download_files files: - initrd.gz: url: https://d-i.debian.org/daily-images/amd64/daily/cdrom/initrd.gz destination: /tmp/debmower/live-media/boot/d-i/initrd.gz - vmlinuz: url: https://d-i.debian.org/daily-images/amd64/daily/cdrom/vmlinuz destination: /tmp/debmower/live-media/boot/d-i/vmlinuz - gtk-initrd.gz: url: https://d-i.debian.org/daily-images/amd64/daily/cdrom/gtk/initrd.gz destination: /tmp/debmower/live-media/boot/d-i/gtk/initrd.gz - gtk-vmlinuz: url: https://d-i.debian.org/daily-images/amd64/daily/cdrom/gtk/initrd.gz destination: /tmp/debmower/live-media/boot/d-i/gtk/vmlinuz write_disk_metadata: module: writefile action: write_files files: - base_components: content: main destination: /tmp/debmower/live-media/.disk/base_components - base_installable: content: '' destination: /tmp/debmower/live-media/.disk/base_installable - cd_type: content: live destination: /tmp/debmower/live-media/.disk/cd_type - info: content: Debian GNU/Linux Live FIXME 0.12.0.0 "Bookworm" - UnOfficial amd64 20220326-11:22 #FIXME destination: /tmp/debmower/live-media/.disk/info - udeb_include: content: | netcfg ethdetect pcmciautils-udeb live-installer destination: /tmp/debmower/live-media/.disk/udeb_include prepare_live_environment: module: livedisk template_dir: './debian_template' update-initramfs: True generate_readme: module: readme template: ./debian_template/README.txt destination: *live-media-path filename: README.txt name: *name version: *version make_iso_fs: module: grub action: mkrescue isopath: /tmp/debian-live-12-dmm-xfce.iso distribution-management-modules-0.1.1/examples/debian-live-testing-xfce.dmm000077500000000000000000000071761437064675400271400ustar00rootroot00000000000000#!/usr/bin/env dmm-perform-recipe module_path: - local global_settings: name: &name Debian version: &version bookworm (bookworm) description: &description Live Xfce installer image (bookworm) chroot: &chroot /tmp/debmower/disk live-media-path: &live-media-path /tmp/debmower/live-media squashfs: &squashfs /tmp/debmower/filesystem.squashfs architecture: &architecture amd64 workspace: /tmp/debmower/ release: &release bookworm recipe: install_debian: module: debootstrap destination: *chroot release: *release mirror: http://deb.debian.org/debian debootstrapopts: '' setup_network: module: networking chroot: *chroot hostname: live apt_setup: module: aptsetup chroot: *chroot sources-list: | deb http://deb.debian.org/debian bookworm main sources-file: /etc/apt/sources.list update-sources: True setup_bind_mounts: module: mountfs partitions: - dev: source: /dev mountpoint: /tmp/debmower/disk/dev mountopts: '' fstype: devtmpfs - proc: source: /proc mountpoint: /tmp/debmower/disk/proc mountopts: '' fstype: proc - sys: source: /sys mountpoint: /tmp/debmower/disk/sys mountopts: '' fstype: sysfs install_main_meta: module: aptpkg action: install #packages: live-task-xfce packages: xfce4 lightdm clean_cache: True download_only: False setup_live_packages: module: aptpkg action: install packages: calamares calamares-settings-debian live-boot live-config-systemd live-tools user-setup live-config live-boot-initramfs-tools grub-common live-task-recommended live-task-localisation grub-common grub-efi-amd64 grub-pc-bin clean_cache: True download_only: False install_linux: module: linux chroot: *chroot package: linux-image-amd64 download_pool_packages: module: aptpkg action: install packages: grub-efi efibootmgr grub-efi-amd64 grub-efi-amd64-bin grub-pc-bin grub-pc clean_cache: False download_only: True setup_package_pool: module: reprepro prepend_path: *chroot package_path: /var/cache/apt/archives architectures: *architecture description: *description components: main live-media-path: *live-media-path clean_package_path: True clean_apt: module: aptsetup chroot: *chroot sources-list: '' sources-file: /etc/apt/sources.list update-sources: True unmount_filesystems: module: umountfs mounts: - proc: mountpoint: /tmp/debmower/disk/proc - sys: mountpoint: /tmp/debmower/disk/sys - dev: mountpoint: /tmp/debmower/disk/dev make_squashfs: module: squashfs action: make-squashfs blocksize: 262144 compression-method: zstd source: *chroot destination: *squashfs options: -noappend prepare_live_environment: module: livedisk template_dir: './debian_template' update-initramfs: True generate_readme: module: readme template: ./debian_template/README.txt destination: *live-media-path filename: README.txt name: *name version: *version make_iso_fs: module: grub action: mkrescue distribution-management-modules-0.1.1/examples/debian-live-xfce.dmm000077500000000000000000000075021437064675400254560ustar00rootroot00000000000000#!/usr/bin/env dmm-perform-recipe module_path: - local global_settings: name: &name Debian version: &version 11 (bullseye) description: &description Live Xfce installer image chroot: &chroot /tmp/debmower/disk live-media-path: &live-media-path /tmp/debmower/live-media squashfs: &squashfs /tmp/debmower/filesystem.squashfs architecture: &architecture amd64 workspace: /tmp/debmower/ release: &release bullseye recipe: install_debian: module: debootstrap destination: *chroot release: *release mirror: http://deb.debian.org/debian debootstrapopts: '' setup_network: module: networking chroot: *chroot hostname: live apt_setup: module: aptsetup chroot: *chroot sources-list: | deb http://deb.debian.org/debian bullseye main deb http://deb.debian.org/debian bullseye-updates main deb http://deb.debian.org/debian bullseye-backports main deb http://deb.debian.org/debian-security bullseye-security main sources-file: /etc/apt/sources.list update-sources: True setup_bind_mounts: module: mountfs partitions: - dev: source: /dev mountpoint: /tmp/debmower/disk/dev mountopts: '' fstype: devtmpfs - proc: source: /proc mountpoint: /tmp/debmower/disk/proc mountopts: '' fstype: proc - sys: source: /sys mountpoint: /tmp/debmower/disk/sys mountopts: '' fstype: sysfs install_main_meta: module: aptpkg action: install #packages: live-task-xfce packages: xfce4 lightdm clean_cache: True download_only: False setup_live_packages: module: aptpkg action: install packages: calamares calamares-settings-debian live-boot live-config-systemd live-tools user-setup live-config live-boot-initramfs-tools grub-common live-task-recommended live-task-localisation grub-common grub-efi-amd64 grub-pc-bin clean_cache: True download_only: False install_linux: module: linux chroot: *chroot package: linux-image-amd64 download_pool_packages: module: aptpkg action: install packages: grub-efi efibootmgr grub-efi-amd64 grub-efi-amd64-bin grub-pc-bin grub-pc clean_cache: False download_only: True setup_package_pool: module: reprepro prepend_path: *chroot package_path: /var/cache/apt/archives architectures: *architecture description: *description components: main live-media-path: *live-media-path clean_package_path: True clean_apt: module: aptsetup chroot: *chroot sources-list: '' sources-file: /etc/apt/sources.list update-sources: True unmount_filesystems: module: umountfs mounts: - proc: mountpoint: /tmp/debmower/disk/proc - sys: mountpoint: /tmp/debmower/disk/sys - dev: mountpoint: /tmp/debmower/disk/dev make_squashfs: module: squashfs action: make-squashfs blocksize: 262144 compression-method: zstd source: *chroot destination: *squashfs options: -noappend prepare_live_environment: module: livedisk template_dir: './debian_template' update-initramfs: True generate_readme: module: readme template: ./debian_template/README.txt destination: *live-media-path filename: README.txt name: *name version: *version make_iso_fs: module: grub action: mkrescue distribution-management-modules-0.1.1/examples/debian_template/000077500000000000000000000000001437064675400247635ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/debian_template/README.txt000066400000000000000000000004411437064675400264600ustar00rootroot00000000000000 Welcome to __PRODUCT__ __VERSION__, built on __DATE__. ---------------------------------------------------------------- For support, please visit: * https://www.debian.org/support For release notes pertaining to this release, please visit: * https://TODO.debian.org distribution-management-modules-0.1.1/examples/debian_template/boot/000077500000000000000000000000001437064675400257265ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/debian_template/boot/grub/000077500000000000000000000000001437064675400266655ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/debian_template/boot/grub/font.pf2000066400000000000000000000116141437064675400302470ustar00rootroot00000000000000FILEPFF2NAMEUnifont Regular 16FAMIUnifontWEIGnormalSLANnormalPTSZMAXWMAXHASCEDESCCHIX.X*T~    & Pz " L v     H r     D n! x" # $ % & ' ( ) * + , '- 2. =/ H0 Z1 l2 }3 4 5 6 7 8 9 : ; < &= 6> D? T@ fA xB C D E F G H IJK,L>MPNbOtPQRSTUVWXY,Z?[Q\`]r^_`abcdefg hi2jCkVlimznopqrstuvw x1yAzS{c|t}~!!!!% %%$%2%=%I%U%c% n% z% % % %%%%%%%% %/%>%N%]%m%|DATAJQPZQɞUU9R2_RqUU;$1$q%UU{$y$y%UUyHzIHyUUzMR{SzOUU1(z1(IUUsss߀UU9@1qUU" >! "!UU >UU" "! !UU>> UU UUq>>UU:]R2]RqUUyyP{UUqaUUqaUUqaUUqa UU c $I/@ L|  c*SA )S 8C bŒ )I$ I)@%QRD G  1(caH #(B' zA  zA8a b/   A 9 a B!@ zaza za|A < 5     zAA@ 9kg@ 1$a aa z`! (aa       z`xa aa B' > @D )( $    a iYc zaa a  y (PZ aH z```a  @  aa  $H  a 0Ē "  B  I$p  A I$ 1(@ z_ .az ^ ]a7@z^ O! آ .a@ !!> @0!C& "((@ aB!>&L2daaza^ an v8a` z^ !  aRI# &L2d1(a a3A!? 4B$"D0 $BD"c&0sKs߀UU #B!B !B! WT   1c c B! ! 1c 1c 1c 1cB? B?1 1distribution-management-modules-0.1.1/examples/debian_template/boot/grub/grub.cfg000066400000000000000000000040231437064675400303040ustar00rootroot00000000000000insmod gfxterm insmod iso9660 insmod png insmod play set theme=($root)/boot/grub/themes/debian/theme.txt export theme set gfxmode=1920x1080x32,1440x900x32,1280x720x32,1024x768x32,800x600x32,800x480x16,auto set timeout_style=menu set timeout=30 set menu_color_normal=white/black set menu_color_highlight=black/light-gray play 960 440 1 0 4 440 1 if [ x"${grub_platform}" = xpc ] ; then # BIOS insmod vbe insmod vga insmod video_bochs insmod video_fb PLATFORM="PC BIOS Mode" terminal_output gfxterm else # EFI set gfxmode=1920x1080x32,1440x900x32,1024x768x32,800x600x32,auto insmod efi_gop insmod efi_uga insmod video_bochs insmod video_fb insmod gfxterm insmod font PLATFORM="UEFI Mode" terminal_output gfxterm fi # Menu Entries menuentry "Debian Live ($PLATFORM)" { set gfxpayload=keep echo "Booting Debian live environment..." linux /live/vmlinuz boot=live quiet splash -- initrd /live/initrd.gz } menuentry "Debian Live with Localisation Support" { echo "Booting Debian installer..." linux /boot/d-i/vmlinuz boot=live quiet splash -- initrd /boot/d-i/initrd.gz } menuentry "Graphical Debian Installer" { echo "Booting graphical Debian installer..." linux /boot/d-i/gtk/vmlinuz boot=live quiet splash -- initrd /boot/d-i/gtk/initrd.gz } menuentry "Debian Installer with Speech Synthesis" { echo "Coming Soon." } #menuentry "AIMS Desktop Advanced Installer" { # echo "Booting Advanced Installer (debian-installer)..." # linux /boot/d-i/vmlinuz quiet file=/cdrom/boot/d-i/aims-desktop.preseed -- # initrd /boot/d-i/initrd.gz #} #menuentry "Diagnostics: Check installation disk" { # echo "Booting media checker..." # set gfxpayload=keep # linux /live/vmlinuz.efi boot=live quiet splash checksums -- # initrd /live/initrd.lz #} #menuentry "Diagnostics: Test memory" { # linux16 /boot/memtest86+.bin #} menuentry "README" { set pager=1 cat /README.txt echo Press any key to continue... read } distribution-management-modules-0.1.1/examples/debian_template/boot/grub/loopback.cfg000066400000000000000000000000251437064675400311350ustar00rootroot00000000000000source /grub/grub.cfgdistribution-management-modules-0.1.1/examples/debian_template/boot/grub/themes/000077500000000000000000000000001437064675400301525ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/debian_template/boot/grub/themes/debian/000077500000000000000000000000001437064675400313745ustar00rootroot00000000000000background.png000066400000000000000000006106421437064675400341530ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/debian_template/boot/grub/themes/debianPNG  IHDR3tk 'zTXtRaw profile type exifxڭmz$s  ~igգR5~rJFk2ʐIXJl_c~΄K~;\}/;=tȯ?j/~\Ox2qqh_Z<72Kc%>gܸZ/rk A>e,pb[qJ|uwR{@xve2SI\SKQE4%g2sEV ;ZŘN~uTw^굜N]:>bZ ND.D74mҌií+2/t8H mBjɍCYl09 W}DJƉH"IN!:wi9>4$-(ke $ϯPiY`0PGKj6!6g<>C^2sCB{>4D&ig`(tj[/k8Dd8]A`&3zF]"(d`LU{Q%ڑ2>ۘh! DŽ١˹Cɮb\Oh#eZqwujKD/# 7$`_ L"ªqC(0g'O00'ƍ'3aua~c--x߅4Aބn!Fp0lv#&Ѝ r БR_Rϩm {8أ & 0<>H)Af">K4#}㧠MO0b!`n{ b[̗qGC(2ɵCW&ѯQT|e^*$>9qgG˰,7`cGHGVj 3W#h%rGtcANI/_OmG(Ŧ!x^w#As^Ehef|(K6159lټ*[vl)6v0ֈdE7j -тV7ߙn6=+^IeFdzqreOİ_h_M|ߑq ҁ 7/#&<\ߦ?H;ȽIȻ;1wi%B"@?I c$!PM oFJPeoݙ rTu)mpx~ a=f| aJ7f@榲H,DSD|n 9E&Bw^EÍ[kgIH @Jw͈ww::2xG~?SZSC:n Z~ QTETnjH>#v]Ǐ}.,W&LBc؀Jg L?F.Ш94N3p:0IzEm⺥){0dȦJAB>7e[{뭹 M]%oC`@>j3~axrr'iTXtXML:com.adobe.xmp wbKGDo pHYs+tIME &v,tEXtCommentCreated with GIMPW IDATxkuw / H8,-$k-Qf[^3Hf[dFP(BkfUωQU#Q~|[U> UTG{E(9{||0ϼZ=OSW>VvSgϝJWV޹~}죺6xW|m]`\+]rM..d(3fWyOk9 \dE"nEr-GT9' `_]޳ںUO~ӟF+j=W;gۙ@pDEgV[O~UQՎNQFoڭNcU_j`w)TcojԠWݞx4jzWZD| o!P<>Vb0}kw{_UyrЫYXxzư#|>dlOݾ)7x0H˩m>Mu^?uVTo Mx QY*D]t_-E)1KByjJ>RV?Ytޖ*ھr#X_bob^oP3]xߖu:|So?_kz\s _~Un\[}h:b;\f-^bFLE?k0,g/ gNHբЛ7NƼ!7um}4~7] 1yDM]2075hhU^j>s} @8ݍGHM΍?ɵ}H8Jt4U榦%h+WZ]TKY,r蝅  c]Y=<~ Vy~=̦) M;<ø$ξ+O\E0#rHnc4n<,[ׯ#7ny;}9)>+͹Y:\U5tDwtzy+W#"|ȴC M}&yZHm/5 '"߻~wm΍GHMiЄ|#8$޵ OE01+ozJphH  im,1׉A~S1/H|U !6US]5йj#O9^s6I!К`d\7^R`<ʳoxհ+τF611{jƘ9a7UO ]/{v6OB86L|lGxP޺Гs+"1&}a6C.Mi4?8*yw"362T񪈷 }Jy$iu+o5΁S`UyO~4Dbmݩ$Xf u mhݠtsnP-ѥS9q^buZ/;OkqzϢsby•iKgV.3SNo4  s890}3r7' @-*;W8 KSW"{WM#z[ 7>[M>u6](Yļn1l1y}Y3fS` i4olV~=!6 P7^y;xg5y/-[Mfq*<8gۙfJ9T шbʹSj;ݻg˟y'CS#&x_+wne'eSL+HfkIV%D-s~2ZC#sNnӖG/ҽ}FȝoH~޼x tvںT^MnFGZEޛn'"Caݍg i؇ϼz;OMgY!wgLoHFkM=)s2lLO?nμlx"[1C,!6UDG"O6>9sX,, N/3݉Rt9gʖk(վCmy7|La oxb> %ә_Iw^%.Ca3 iwEHDkSM6ϧnߔfD! R9 9xo?2c ij z>S.1O5&ļ^}5ٌ ANs{Gbad~Ap~88Qo+[7uv(VB Ƴۖ %m*{'[˝7u1/VT hj`azix\y+5#/8Iɑy 9čn<)W xuk|Z7.=* kd|o޵Ōn8Bx<ohz,Zh0_"?y|?OȗwB2}7ޜa_[_>8b:Gu< ⢞L 5EAU'GS%1v A еļ.fq>Tdߔ)VxQaբfߢ /9?;iy1PA 6#&4x[ʧn=I<_ M`l]&/},\OY)]JI]?Yfi.x2BOHA{, -ywT3i!@x6>|w3~K@SX3̨F.An_gQz Aҋ;rb\ $f f1{,3R 1l) j}[bhzGOwz2)O<<Kx! yNCjF &/t#ޝoUtϦ+IgGR2ܕ"m6nP1O;s^g_ާ:BQJ_+?>;}616[, 0n\y."C:Ż3Ζ`ǫ&K8sc z2Gy㶫jS,N™qJ:!65Cm{uR\~"cڟކ.;3ޑル՞*SGVԉ+g-=<{SOBGBmۗpDW5/יH/QZvpNUQidOllki} zs<;<λY"D.|ٯcizMei8"Bd#œ#/4x 7fO]%IVL wL?s {'-Qޥ\3ky&AׅmBm:; Y]S@ka1ϴr_Le}0}Ӽ^.!LsZڣsX l_}S}㕳YBFql:Pח(g>6WPm'i$bb^w gN̙guem"<jLe]*9OxOHMͳ i5|ﭤӚ:<7DHBlv_|X?]ߩ(Kb fyX$+-jA/.miu,ɐuGW,춓xbQ֑Ԓ5]{Es9 'KE $$Ku8Tw\|hpEf{"׆3^ұőpƳ;GHM)p77^aΖ?41k,_tCIə*Y[q|c*:[_WfbS#V̙wR9Sui{ρ~K]E?]=_fslߛBsǑp mc`\}K^x_N:Wg,z1侳< VT7oRLs/tr镴o0kY䚎t$ygQyicM?PT!+/sQ֌\yKRնr</h??j{uAT4QHn>3)$emT n=[ͳkv}Bm&vG̋jtSTyƉEO oj KokB 9.Cj.8 &ffZԺUe$\c rEžrg.my j(ib^zj2KԄD~0cf8.%d!n<,[ׯɋ%( /r7T˗ZWĻ㐟 sUy5ڮul A&.Y>@i55{M-L'"vx9i%_5_y @MHMbjl&5ZP[pߙȔ]1+7)9y}É4K #;0iWmf^1XL9/OCϭvDQzݚ!>Y+!²7z]nǟj/0Ɣ-6FPl*$ iȳ⭑ڣN氛A/,y7y+2@;B,  /N#l^>OLSѬk!B2$fʍh򊐚 ;2 j*C|+ueISVay IDAToK2RA;/XjBC}] #&xȕoJ.h+d j1?o51 y^Zgz ^P̝;fS ^3+S<ͽmu4m g6 x'DxA.*4qЙnb:E"_^ ӓ)iJsy6Y%U2(1uHNZ߈xs eZ kkX= B"exӠRӫ!>xz6K $WK!\!::sTWyV9=V~ڔ0I蹞'EJ,;ݏDfeٛu!\:ZF 0 n\}N.o+!bm-#m xb l;f[A5αs;/j3V 1rbk쾊.EJ,[:""ǩG\yCyčy|4f%7f(:,^/fvBۤX7n}łE- ;jW<ٵXjؐz!6%9C=Ո͝t#iW[̋^[j|y֖3i y"Y!ouorT4WWy:7ql0p{*Y6a*iE O%~L=[S- zγjFb^V=][U+Yztg#,!6o~()G{jZYfpğ˫‰syo#[ːN<ɝ={}~VUd:] Kfz5<{kr<-rhk]wTtw?>2\e!߬9'Ͼz--oQ^L$4BdjP ntNe8Ϗ}C}=;*ڬ"济UrÁɕ-RK"}\*"By \ #yz!5x.4/ #=bF2GԨHoj$˙gcpIaMOv۹bP{E-uV&J\>g=DHMip뽷˷~O/xlj xx4jJQӳXHŸ1dٓrdA&>:'Ԧ ޙCb^•W?_^nkNʮ}TSѕW~b o}d) ÍyvG^;p"/%PQ5y+RSr#:Q=)q}Pm\뜺YihYډ3KmPudHg5.<_69GHM)EĭoOeGKD@ 3͏Ip7X7iߞY{4O:fƵj*y:Po-+/RM<iV]۲&-hX˜ȯS !\-z(ryBu\y{75sN Bjl[%ϾM$ġ4Xo«-yŻ"F5YZW󾘄ڐEW_B 4nX{͸n({4{yo yR!5rrÔ:  y=]bj z}wrujμ|yjsժpkr]yCDG>h~Tȣ=!yV~!L%=L"dq(uۙY1ߗ <[ۻa,Eު|vVw^NնY#_B%d0^/3+meL]Wmr\x0Y088UvgriQMu[shaՃ!6YƍknnPdqgzcl!\8,; 0n<+{קiaHd huE< x"uVd0.s4ӝcTbO|yZ)[FJeԔT}gem@JUww$҉PDcW5.6ϕo x%*0EK tjmX[Wb*QB+z6=o^Mga|y6g0(<&P,ο_ Mi9|ʥÏkwG5FcpE;)C/lݸʗ :)".IWl.?ŭ }Weg+PysBHM p CjZ鱊5xcI}݋,/^SA3G+i"^t<<Ks 6`l]&/+/5RMk"^e& uCXfļ~~]k 1W)mr߻+rA=6ټyΨrpcX8yVBjj<L4W)/^9X-^WkmxrPaDY͍V)T9lyyDdn˓T.xg5y_WҔfKB77y&VǞa #"ꐝ4(˞6o=]o<Kj-e afȽ"v_ OJ׈y3ZyD 'ǡӊnDwwwEu _>Yܦqv=jݯ0y-0m򭺷nnB"f'4bm{Nds XƇFf0a4m1"ٺ~MnQ] 9O} CjF(*lAoT1Sb V7"flw(r["} ,EdgOAeU`%BzP\ Y Xh7S3̐z#x5;6$J]y7#B|w߇@ӵ7.YB12A8g{}BuVhŝW[̋7Lˆn gًQ({M{;"sFxZvs]%9hfʝ7\̋TMV?+CӈfVš"$ޣ-S' 7XㆍP+^y!doweE zE2BjE<¨rm9hb5s孛x~"m9̚W4M!0 nlݸ/HTxT~,!Ms 2+e xC>S<1VxaҲEo쁸" E1p.q;xb"x˦ l47^Lx%.<t$MqjvybAW^l£=깉W5}'ߊ=&[ׯ ].+x֙ w'%1x8 B@ 9PWKQZEa5c}x}"^7(I^{fAPiYgln5sphdx%D>lw @|qrrQ3Q"!@Dܾ)5FWQƳ) yUT>SBl檟>Vk_Gq(+ib*Q. ۬Koo&ay[5lCiC_o'aQ-9BNXYBl'~#M^;xw<蓃 9۫Qqk9c#W Ͼ^'SRG4?a ]L0짚X%Oʬ:oUlbn;Leht“'<+nF?^ypYoHM;"+Nai,N;..YO{)<4S!5Ēa#iBIwjċqXƣ'O& fcGCpylݸ-SJ/ԙ[Ț˃\xۗsk"{uu"2ѸVusgMhypig[!ߧu\yj\ jIj}%h2|'ra;>XфE0:yLo;%?;:ia}Ѕgs鍾 ,Cf>s(6sc=|,r4?˿O/qDgRҷ S0 jgV43u%ە=scŚDn\}+w}Y8#̈́k\kem79Dp@ FLiQRu_ Xi`>hrߝSkiA|sx82@Kŭ-W^F6вhOFA6/wه| !. >/yH[溻}ÍUX8_f`by ޥ߃9 6ػם`CSt"vE4"^H1S8lD\<Wv#0`'"3)\ 7HAK3,틓θil'ZL\=zj)0煋Yc~OĻnK='ly?ǃ<𜿐]S1S36jx.Vhno^#n~/ `'|Bc=9(ח=?q!湯|A}6zZ Yv*Z\yJ\8'ra#B@s4y<8.93ŢJWypark@H8Pۑ){-P4X$e{"`*MV@rVF4oLdV/!B\X^˯֍kr"ؓh 5 \y:m""Obv7ʡx-l\앛x~Ɗ|!G"n]:ol&r<%B\Hk>m" Q-fb]@}9VryzpaO˸Gu"aUyOa-2hX8j pY{ 9gʽm{;Ϳ3NiܤKM;ѬqOXSd`#[_\}HN&8Sɑfbn4+wVOĻm;6"^M7[Yv 5rFZ~Iσbzt9@t-}mPskicZl޸B\^˯\5e SOVBl<AˣF=wF/HՍw vjpR/ .Z!B{nCjV(L7"L7Z26Y+/GIdjj;{"=hr %nzWnӣ9?* NBkye/;H>T LԻ~iw+' Y>'"Z_[q'#w k]Vm\H 7а0Z- r @P #fyܐ  bHHuL+6&sbHZ;5ǃŻW'&%di&4\Y+q֊5/baܗ?/ϼzGDO-ryn"G` W^$ڝE[֓NG{Gx~x"m !5R}JL䗻6Z;3#(CL"a^yp.r\ߜΔkm}dK~[2 V^8W`+8fqT;jww<[;B4X'G9ilz|ca5%-Zy Uԍ1D95?!P+uZSB5#\U{cGⴆDL{T&l^bz BAKobƾ]x2MMsO9|Ѹ6r7 @|R"@}r7>c\K2wo+3hPgtY½q?ޙN,"%f 7^Jpr־SK 1޶{;yNY8x:܏)a5};OEV9oC~מrBGDz0a..!~@pb)r )Ruٌۙ KZ+ighwDClNΧWEsU/nu?93zgXW`2Ǎ#oF ry5菥Z ~/|6X>DzZD^o"Fem;_AgkVo nj2[4_8:޸V4ע>n#D#|i#4! `9 K-Ql@y򼮷Pj"2|b3)<衸{ۢGF1 zkrY+D); xG=ops;M-x.r<+H_*#8wXj6w/~rCXV YYʕWڋ秋,ȗkroqKWy IJ/E_eUOsUoUUUsO][{?KUj ʦ6&y0[sVAjқ`v6i=Ͱu_g:qz6 3C=`֙R2k<;, F« jH"9^ȸhƫаNDc\85!`t;/7Y' 6+!O7<X9qV5zCU7!t==/%W3&k"k/Z$\y{GK(]Ӯ\z>Cha;'6QU Yi"OLvX͡=Ygpp4=]BDQQZVF>J'=pS/ݔ1-&^3-3LRs^#} hl{ĉ:5g'2L[m{!!W>SīϲweB\<9 K9Ltn#sNdHd1iTDhBj|q]盩zНØ.$fY;T v ݦxNDw}؄?5rQMbjBļCdtdhw@CrmNīR6_3EquWNJp8jw5VƳZ9Q0ko,aQ3~ZyP6fd8+/5ˊl)vrXwۆjS*gƚ/0y;|ZTYSQwjjnKvDf+m婘'b*;kӞ :ϛn G oy,u-Zc s/D,uw IDATrn\Ot;wDZoU* ;0pĽu {2?M_3^Ϫnh]E[(n'N]wΥD\y6"!AO~ZzrŅJ5ծѼ˂ytXM71[l<_OvV<Χ>ewpʻ8=D=Wÿoa㘤nf%VL.2_cWQ$mѡ.Dyv"1QOds^^Uk,w%+rpݑ}[jw /N6 'έpm<JgE! {/io.1"ׯkP [J%]ysxTGE<}pڏ}ʗ7:~~̰OͿls,w"='NLColkp yC]d;_5N<zEzUEuk폿-Ľu`3<_|Jj85SQʕ/$;;ʕ3:7λ"{G=M&={غX'||~Ckb]$5 լ!KWtE[Sw T3x365L<֖hѧ뇼q͕{{czzP?X/miO,_sPrN:;r]t?{omq}q\#0A AIQ$eI%JK(kh.S[m{-?~ZZn[-QZ&5P* uV՝ΰg!yν.ޓ1s22F'y3;C٘WW7_ū hI>hD6fT!2*oUVL+aib|R#͍7g\MH3$pnQy!B!ŏcѮͪ5HOSnTo@K[!N/ˆB!5>oi顳^G3>yQ#B!Ҏǹ_gI5kt+{ɒW\u͛JC˳-a\y Nܣ:^jx1ctԼv4ϭ18;YL2xݽSR#"=7"ZFN[ׇGAB!B ڳjHHTLLJ>uK7ݕ]`xq܄jym횱^&Ԝ-ISK]{v彡UBF]JI%pE1yl^Ma5mkGI&R>?\ ,fW_z#B!?.y𧏙mPd#y,k.pyrcЊxjlLlGb?C)cù~gGIo/kN,ٰI dB!$c 1Y8RodŽžtо7)B!Bև󗪏!I镧,W|w k̓wb6_^170&$#c>Ɵ\|b!6]W&0qyDMr4J[d8&62l@!Zo2Inċ{] {}yKB!BF&.~ ~V>b܅!+$&N,wz6ggw%-6]8hED< iR3XVRSRZz/.9th h  Es_!Q 0_h_򅾮6_]z륉zKB!B.~ 0;'eFKk<WI^y6U&ıJuVywFCp~+nstCL}+!q^TĻ[ňM2VoVƅ&34 pZ~P#Q_EIo^;Q'ꭒ=B!B9]l>(>tTk+ϝkS hWnBw or> Wj.WޣДzMw.mr'S/]j'x#[tZU.m*6wNBhωI"xgUNbw`u~ ׊ y݇z]zHS歷:^N/=#B!s?XzWZvk(=jmz뷁r^ !6}# yżCwޢJ= 7UڎoKU xuDѤY;qyuqXMGE y kᵌ1q+ݥW'-_kGAB!BڧOpWzɕW7Pp(w*bz^#7ױNtF 7G$o2%-3IƓata5 yZZn<ﺧ͵n0Žt[_^W^zmn ̣G!B!_(/_}5iuZ 8,m}+0Wv])P TE[y2ynVwyvDi}g87߇6&xE"ފxe&RT$Wa7[x yz5QHYq{yѺv!B!^c>/s{gq1ppvh<=.fa861UBЛ'VO'_6Sklҵ=p(KS#KHMyD<XZAڴQXMB!Y c=:z^.o^./= zB!r>|^F 7!y$)j7܌)]،2߫%yDP)-ړ>I2 wyvn+!p)D38v' >:cO'+UB!bakxGb*ݓ.^^znR#B!rs`ώҚ~[ׁ`\e{KoMyYy}C([Ы[`a_̳9Z^MB4 iXYBlj'G[b^lCsϢW߯e9 >$c{Gb,ZO56mI+^7^SWNQd-ܭ7zpL!2[CjhUj\'aQ/^=}!B!b땧Sq]dD n1.[ׁ[z&W^iER]xS$y@y}C[A_*qH3 xW!w`)Oēfc?/^zHMi{-H&o<X7pի1x7U5 #`B>vv_b!-󚶋1j1;%1/6fRT zډzPhhnՒώb<޽ ա46ɋ5[U۪(l'L&5lo<y"^{ y l%Ø €gPHCׂ^}A1p=i~xҷ! {,Z/GHM1f?.rN$7d{;-sH!`n]/RǨVTzSvs=$B!XpA6uwu g~'^_Ľ|oPmwR'c ISs%琚2ñw_Y6`wNͽhosB!wG?ҲU޽  K"}~]XĽmKW*A͠d4ecy<+n`؞U6r }6k)U^"mı.Sċ?Rs<SJZ {e$o"H~\3!Y6~ԙZhX w (^;7='ޅ\Na+o<?:^y!1&!B!nloϽx2%>qqXoУ^ynD)S(M=gST=c^do<`8bHEnt+Qèy .&!Ks?V*<3kEw~UrzuS< IDATv I!B{|Il~";7[*J虋5!m!6b_P *_^̬01ЦΛתR=^\{tI 9Żj6k"ƚ gcxhu^2C`"yIң7pTՌ:cBַ;E $+ĵSrJ5}M[I6 (U סW1Zf8Vx*υ:Z8@oզlmi*.gNWSۤ|Z~woɽwPWw|N'Uc|yVWknS2sB!B鎍{aZX+^iI7!6#xhmpb^lJx҃1qmb򓼩*{h+UZ8^]1UWvEyCo<)l!5EyD`9rENu[£GjOR0ѕFR?Xs:=x)\ |q2lfx8tͿeVгM^l}};4%3|o??(wP|k5kzyS?jۤw!B!dya _ zA/kBl6jU{YP;cCm \7o]Sss jKn9EYW^xՂ4{/ak{ЃaWJ W{ΰG y\x#d l"fn-^ggڒlKNan_M`3q{ 77X>w܄z݅<ɂ!B!'s;1FWzzՔHa,OA7ЗyONS*Fc `?+zW "0G]ےN7R=F3&1xIř7޲ "q$pE߬m} I;Q,ΡD^B!Bb{>mRpI@q'Ze7cq7^jұi<}Y5-^5\t6Oʾ-7Ku3Ӳ<}$kg M&EZ1+zwxo8.ʭRS ӞZ2{]uEP-i<Bg?6'{f +ž=$<o\{{D= z@ܹG!BY5ο,6_ .7N1&zyc %yu{& Rb1QS~* z^ާMVWTV }j/wㅷ(|D䐚 \m{ҵ/Di @f,荭BM yf"]z[3s;τܱ8j up0 ŘJ/K|xMj{iEěTRRׅDe[o<hj73n!Ȅ#!~K-e2@qk߄[$㖸jQuz ֻW wZzzOw6B!B2ąOf}- ޸BlFz'". ]l 9b^@٬ЙZ<^{1[Q9.Qn]ha!ۉJ`[߲%"D !5tB^L:ƛ~ )"L&M$d =3av6Ag/*/Y)ms}]Lj /C^ ΒG z!B!pJG.1+ Xb+Sw.dY3_]/y{|hR,G;mDGI:y&&Y-LE^bO;!5t7qP O cÎxgY|A_9iTLa2cchJ ზ_ y29譎2m !B!ąY_ş4f{ fq8[+*d%)kwϗ)Mfٓ$57Z\TP[)N0wўWI_ o:(QĉxchXmϼx֗%zNEi`X&yB^9^,=3k;v~[ |17蕍+2O@l1o\)(Q!B!'x[/3uQe34q(_x` %MbSUBl6L8-b^G_h[zS]xB^c<"mP/^lDMf+RNo<{c")й7yRJjHYf{g﫝LwL'B!y$iNfD z%^|?^[wxQ#B!Y,&CWԄVf* Ao_118k-jy0Qj\y9l+.<=^6ε=Q;T/nD콪VIOEV](4zϖExg,7cz/`'NJ&$W@ŀg"GwMj_^\qWGAyB!Ĭǟ`{˒,B$1Wi d0=nyyNeo`4IfN(}Mxxk$Qo}GZPUT"^;Gxry,sRBj OGfx^!8?*ˁjJ-E =Ƭc_'m$PN+inid ~WHW,p<~|#ex)^xvۖ&ijD Y/[ě1aGڊxsP瓒!5 Y K=ܑьto[Th̘%;zꘈRг7~ |-Ml/un6 !B!=s%Ѯ #8fA {Bl`0(Sʫ59}.MsMff%12y ̰6؊<˕ugX"(l##.0GL_$ ׊h14ԡ8^moR"&hwq (Es!@o -w\08mD2>,бYE3Kkv>oP h*IGsEcfYZj\^y?i,<1,V˛ќ{xX+~Oͷ.x81吙iH"LR 9גJtoaWQ x0yr``)o/h$~W\>kSŻ[8muD<(I3x"(?=v"^xٽdNsw]ĕٺ?#dJ\Rtr0mn?".'')ZI4\JD9\M+Y͖Y9~OCL?H3KT zŷ5m\S=zqCw!B!w l=-y`2{=kS i`ps/ &O/wȧW idy6cbFj=ekZxy}h%1>rĈwc|7ֹg̛ Oڴ+]Lgbx$I"^v" 9Gx9޿.[yO0pesD2Xҧ(/H6AbnPKw 4D} \7ckN߿|[u.z)B!={6 BU1!5SJf1K1om7I|#/a2d/5{\xS'^3ʃHՋ!E3u/gh;2OwטHLdF;=A K>;(ֳ F}ҬNTkp Ֆ3ӊx+o8Ry1OHM7^P#ovG{[? K1ٽ>* {Pe7ėPlm‹oޙA=eo&N+۽^z0Yx3癒!' q8UEL!5F;y#B!lݽ,h*Az)T‹/>L (t:އ>G.Sͼ3L= O|yy9H̛T61yQy:ՁsH׊Tt`] u)yخM&ݩr.LI6Nw<^QarO"r3(-V<"j|_MF$gWrƹvOXBFI׶jʛtG𝷀o4fiܩ!B!p'qyT$PjQTRɆWwf&O _"̟G +c 1o,~o50Ck΄Ïu!ɕ  ŊyG<_z&RR4|-ҧU܋b7;Ǻ-'QsO<DYx"Qߐ7x,/z`-rgM32~i.ElvkW0P<&8Bm7Ӝ ǍeeӕlP׭8 v켧ps./6 !BXTij<<P8帍fQVNyzMMtT1ݻ5TfSb-$`0h%%Ĺ`:ˋ{֚/dfxZ΀'I`Sړ-eE׹揄$Jl&41f}"DzbʤxھDYD 1rx@-GE(6[H+7[sˎ% x@^xrx8)_hmW= [^+];Bę`&>S|_RԫS-$^!T][ZBfVΰPˮ2ʋ>o3XX8IF K7[0=~i{)4ڇ tãG!BIcl^޳BM6? (#}LL)M6)=|;/;ȕpH홴x<{HLBmVEym nz ϺZ}w逨g骬?|)4޹lfĻfG;܂[K_M/KēsQ tJGc`ѯ5j %uZRZAQ/-JK6Pnʓ{2;(Fbtz{ a B޾睇pZ11M5fl<ۜB!BVv?]{" 5L0s+DO]½/b,Ju KJED=}y=oOOYKz W8>njnL›[~4Qd!im]эQtn=rzQ7kKBĹ0g6U4z"O.\ 5yV|_r_:^485sParқLFyYVY\0?ӧ '[nY>\nM6Zq~NB! n +J~ϵ8e ^?Ƙjd!x^1fۗq}JT `7L9aJH<9_<3laO-룳z$x|MT;&"ü̥2ʗ[r{I.3&Px o>R*"(CjΠG O`W|E+"Wٲ釾,žݏf.(m[U&w*9Fa{p4jq O</ŨXU1DsA1B!3/?z.l .1bϔO k?_k8:Gz!a*~o 3o.Mw9_kZOOЋyιuԊӭz3iWTp~3%A=&@OHMv{y ):_3#nS/YE<`xVOip$ra_ W\<.݇3ӧpdbfZ{J9=a6A)~mf6 zEwNcjOs1Lm,VAԿ :e*!BгE;q&k~n3LDjyB!L6/݇?T'W^S3gSR_`BȅW'vuMg"MKڷ[DULbFxYdH9"T ΢ǝѱ(= {DD7tCc>k<_5Z.Yy`{Q@f_̾9(r JC>>{.^ѿs?h7ᮜ3"=W.M3PE+O _Lf,2ffKGohw:Y2w Lt&zM*0ƧF C7/2#k^<>/Y"_|;S?N5Q߱Hsi׾W{fOQBR ^}!%. O'U^\ PyJA/JU/}$\G!BIOa3N<:>b 1$_9J<d|՗W_/źՐsjgOŀ\l2|'O@e՚]-؜!QO,i;K'4aF'Uݷ?}=MyB!_x zUie+~ӄج)"YXga5kMseo gPys/gN8J1;N_gm=u}m*;[=yrm}SĻ^'!EkV oֶpP])Tx8E!wl҅5.hW;;LJ53t&^sVb^': M/b^\yB! Ow3uwa^.9T (aYʗ7 iL"s0kpʦ7LyX[f@gBgE!)†7^Zۤ4=Ȓonuaw&[JJu;+`r"Ej{yGDvɝߡ!5sMՁ; |Ղ& {} zy5^bKMP J80hap٧#^v3{,"[~N xyR4c`Os9"pT䀴lf  y¹ _9DLK[W ;HKKl^'p`\hV'ǸɒPbglmƼw|{w܄cCm7b!B!'ƣ7 ZQL͚b|na62W+1Sn`(bRKu5!2:,n-GzMJzƛW!ȋ7n;ê x Ҝr8*P+Ŕ͓ҥ{7DNQcZ3xAٟ{t /"O I"ƞ. s=E$PK8C[hb=hO|~ȟu8޼<]lg!T+2/ 67a |6)B!rr1k| 7\%T^y~MW@*!;M)EɃ)ļzhML > ,38JS k9J -kdx\1>xWڱWԋ7tPx˜]b(-M:umJ%thEዕ\==ZxK5bt7aVf.28d <5n׌ΤiZSfd]3=nq- qk4Lr08Y\w4i#)W~qGoGCݘgmsuS,9 2rC1B! ދm <˜bmj͙'{5##qu(>-8xyfsf[7'[Qfqacx줹Ph5R;IMd eq+svSċM]RQW<6i]ěpHiH-h7":ZVUě}#n1;Ͽ}OȀfdWE`RTD0<lӮ8V" C1V)Wzo nyB!"3[ Q)\eWe?¸\L`S&$5*t3OD<ymmN[^a@l7;LӴVkv܏^;5hE4gJzT i;VեXrŊyVzLy]y* γel;}v[Dma_6F3O[:ANs恓.iǐCX{XXuY yts~ye,ܟ>Q: P'I`㐠y$x ëx1V^\? xtH7(B!rjx~l>@e-{V_UyF_Ksycy8:Ҋy?3y/&+KYŰWL>[Go2]DMŠxIi7p n+vo8%x<tGH\_cwx^$鶟L+I6[$Z'&7rSM"vz,"@Wż5bѨ/gIUYyB!Y4y>1glFf7e<|B!l;_O& {}z>`;lK4sXs0)E̫L/aq̭Up1ϗOm7n̎\bxP#B!.¬ g`(+aB^`ʙ@9 }h8'|n"Dy)l*/%9>1O yB!<.Tb^W:J{W^y: W`vM^1kQZ&3gDЛL׭ /+ƶDoѨrE?n=^i:ñ*xrOsSQGH&_~;"f‚aptqf%Z'Jb|qbs-"ԅCl6LԈy \vN =otx"TO:B!O~z:ϒԇgLZH~s3W/~b't zrcrygN1[6bsaR?{ߧG!B)Ĭ 0[%iƄ=>8ߒQ ^7e ( 0fEE"Z!tP"#B!Oc +V<>&H?%U[ħ6p+6oR=QVF;] )b׻mq,m@O݁\=8zK"Jw[M]r8ތsH_jL-Uj :JS_޷h9';vxx/xDisIaGqJB$;ulK' xr*H]̈]Mҵ cZjżPN6QL%^}q(Y~BbÈIG!BN'އKU7ڈy>@F̳ >b &_+KxNv*;/[XOT^ήE>\ G]`>tp\wmBb9Q dlà11SL$pL蔪fڙ]+Qf'_ B! K.^P{NP9 ʯ{6بS&l˧/=-,6D4sb$)/qDsHoRP$^% .<`@O\Dw>R\Q1U]Ñ^ijvxto,'Jz/Vy$q?K:?e2v3rrA氛pC%oxRS0'Gq!SE<1uVSs(G#߿vp9}{1_!Bz% T^yգϼW^36//46F&hg˨-Ly1a,-'^`Q5meUoejq2)[J]=7ZW[kEHPבJWB2} 㡽ƊxNo݇]'[JN4/wYT1)<)\x)R^EкuuD[f=?<[IeRJ=z"͛\i]U#Eud*Dw8Ѭbs ʶi1| A]+/i"XN1ϓh6›q;EZ1rXs7Wk8!=^^bm䪭27{WזWG᮴ LNnI.xխiuWh) pQ/֞DuW8=R庌(Ǚ_xuk鴸VxڒbUM/q^c=qX+՟;O<"<w.X%bڭME%؟'=A B!d|vJ1W^gl-~yR-g65jyr̓j y#=TU7 {y[CڭT uI6B8ExT[0JRҽwx<ω6ր.m[ ֹ"D}qg>tsNYiY\o7z1bLiz(4i>{X@vʹMqޕJ R;Op4!kMm>ZaTP1>ټ9L.$lΌ)C#_{0c*/&zѭiÚ3ᧁ~AMj/ojYB!rr\<[ʋ~4ST-"'Gj[cKvJ/6N$by7};![5d+ Kq8)=ǀ/1.c..L#B!8s\<ƎK=~  o mo GomKUA`UzJ&CyM.fY3JְT)wH?i$dڱq=6Ɠ' v /:fo"蔈xw"TEش+pRS5EMg^I:eDf=(h2޻5ےa__̙` YiY$ael$JGXo~%!P"d09g>{釾dVeuڗ3M9{uWeee3S0f&JDTi5QL2X"/)Qd_zj+g-CͲlS'p8~1vve2OBӣl }f" D鎕cB[6o+tS*uP"Mkzw=1Nɤ iFBkGO4rh3$ ֜ӯtm|q*ǒ9. 9_ =Q͖Ļ}x5$КG/哫 J6\Tz7Ȃ"g+d3\תvn!:^žq `b$|&s̒Y))|B[I(ڴ,Sx5p8|8|^+)1S9 _lTvĦR\KgxA2g HR=ae2Plq5)Iw5̩R9 k漣y/hb?>~js喿k bH;3_a# f~^urְwI[oGx!W8s87fw4>YS4,[ڒ;aUYxcMnIX题l|w&.#^f'WBq0LjeN )8>H,[ͅ3*HnO$h$WKN9~M_ YW#^v>۟>DzfbO9VN=HuIp&/̝'b|{O:-29J"t}P,kFtQ3 ~_wV<<)},s8p\tp\ȼZs92S"P/yc , 7/8{xxL{DunkAשTҧg,kNpZ~5jtm4ifn ZLxCMs@66{Z@7ѽݱiWc5ڴda46ǥ\KՎ;se+--e=Yϩ'sCpR(M?V~?)oa6[*cϗp8 7oQC}9悂QxJ*b=qˣc5ފ5dΆZJ͊m} bTǤ4$Aj6Ic-̐u$e%-^ڕdHsxUZu_d/4`ӳ )jk7 SGÑo2݋tuځS[M<*WiR%ZzT[P۬z{5j6IȱʇD9jZTiELmF" ջWϰ{Y:qs;p8eۏ!eh<ė ҆°7i-#Q+fw+zګ;hzk0ɱ6VZx?>yv$sGdw;i >b3@iAƮ(K]S"Z q=GTMqӚ+]5t ͋Bjx@>Vm8m ~eDsJ4aΦWJBJq)M_ ywOc΀5J.N_Ѧu2!Zxq$őx0UWV$ s{9n_ŝ}WO\qKo{`XaϚ[͓z8[cwSp\("޼,墫ٝ5,QdÚ'QãX‡+o˶77?_/=p8x;7 #4/3RT lHPŁؗb޼gYKlTta4oΣZjui.q5/=b-J,^{%}r]΋KĮBޙ 񤳗kKu/엞nneHFMԫ^{WJ6&zcJd$xjz{W"5xvK-UCcē;{_nx?r9RS#^ n->#{BQzMa( yۧ GicdMIHTuS8N 6n!T B=&Y#<̫Ӭ I@?hd%p8ñ7|`S`ukلy~#WSde RHsUeY3y)pbZW.P(]KOPC|NrQ%U:ר廟y ߟn\[@ qǧϢvl!"“/Sюpp$ރ[7[HM}6B̦NhQǿu[Yq2U~"2KfH-s8pͽ[ܽ$[<0AN MpLQ7=z]ְLJ΄c +wB+ +3ĭăh%! z~89=O'֮u'Fkk4-uNΊq[ #n_izH<٧o֖K_y'| pyT4}{MMoxƀ24ي1+`၈K.?ơ X; O#QyZ̊`"'Ϗ00&aډZj܌3Ӳ{/\Oϳ;e),:Bd)[xh18b۬حC4(6Oo6Er b@2=.c@1<K)lgUi i<oa i˅Ji)ߒO|Q]߬K+f#L s|IA7?b&IWȸ[3{&RH5̑zI^Y22fd <*C#}_մEJ۵P2 `mݔ;[apxS7^*׶WX 㯗u2p8q. cn~B$ n"|y,J%gRN|K.xצ~s ]8:W{;=>yr_+[*$;*$^A-«ߠb,z/sCix]#hSs$,>DK{7u};dn(7IuK ^z ޝBQ.H)T„S›d6W%S 6 ^MA S<*V C.y c ?ە{]NFMrIYgp8p+`3OʫFr{z3򾢤|2VD Ž`EVFyz%3v6*bo$({Bu?I[s7yimf89N x^OZt $ˇ7}pr6{aJˡ?9lKoq)FxQerskMnH>zS|&2\Bd%,5ޝjM 2vy!IP'i"p.m(P~r%6ODԇ,p87刧S$og<ڙm 5_It{c$o}şal2kp8pHjUOlٳaFV lr7_f;B"@H b'p8mKGؼv̪a;L[7S%l 2#"&X~VWV2/\ ȼɼ\W"p/a%]6X[d91!X@vhQ$5+zٶ gAG>wDR/Gish,|f>zғ@ ^t]Ѕeo{9VgEo^x [Iyާvq*]kg幃]7!^󓜠y9+5;,Pg&.CF䄠p8ClOn52Oc\E2Olymd^*we[ĥdOA R ҕFz:_=.D-&'̖BU:By^X|z'gR.u|~..$6e8F2߷V·7F7M^HYArkw"Uj4.qILȻxi\^Us\[o-/H'2qQ},#~UV 7G,tLQۭ<CU,Ki$ycu 5&7acK#3i^~M{yG_}wV-KZ*77J{jCN -Y1G\"^p8x `=#nBaėBImxhH)y\YS@vHtŴȼ̅pf JlX u1 PўA`۟=zQ2 63׺ǶMp=wrce>x>U7Oc*NNgϺ>W;Κ VY.$2BjNHi_uŒd2/YLd^Զ3/ U2ȭ d^RLrFA4<͓p>Lw לAh>Ph3ڡl?c!+o =S{\FR$"S7X&JA26osxOͭP%'\x4Xu.7ű/= ^z^ЫW0TɼJ;/ɹI8 P KnZ$bz@^tR2/ u'NGiˆIz-!8 |y>ʤRMi>޿\?~A<Ħp8a+f@rljNk7eZDn!ObϼPp\Ey bz5gg_| .sɯgc8=Ό\TL 'g$=%f6VG=ɣd]˦px?9^Fw A)7 *|̠ f[Ɔ TX$)sǜ2b!ZnƬR(ɽ%aT'%c)=1J(d1Cb%I'亩00OV18b k!p8c_ؼz)nR*Dr,mzBU@Yl{,MlrVRIq_5(u)ucQƮdǁE xAwwY7#o|n?!tBW׍"s1,W*d@fԄ¾pz|%±3_ۆnR /4'F3]lk$zɽ`lo?;ěKQ\pNf$pVzy 3Jִ[ۙ{5}VUTo4nK=A(-}to:\rb[ghOu٘HӃYYg<`}՟ꯕUX.'e2 |_uA(d SrTcMt'@<cж+2٦4 P9~pH7Y20p8/>J./+bp }'D E2-!+MYY#8|K+\4pOԅx;@@ !37ob"ln<m6x]pLB ';=~Ok# IBI39LB)[/<'z4r3ת1^Nڌ/-4[ȃ[ĻO~3OH<4x7CTs,ܼ[cIm|CbҼj5Č>đ)"u\0ݕWB5e`!l!~;gKJPjBTcXe{A=qYhqĥ^ј2\>8 JhCiwRxz/}Pw*mπ] at>ee_{o?~p|Z yMp8/ ޭqP*] LrH$baqR/mE9奷^Zfh6JZ +T*/}Z''HQF"֭'ֆDm7ymy $AnuGKm[@۾MMT<:󵲑nP+i {٦+m-Gǵ[IGRگ L柾'. IZ4 )bt82y7-Ħp8A!Ch6\x럥a\fM.e!x!Oލz2S0vY㋵Q)\n4&nw7&3IԇmvHɛFO`CAaLSMئЛ\!0ZE{n4-~ZR>y9D NS$wQ;ǧ}Xٽ_.IiQ _ۋ0OzEsh6k m,9S^W葅+==T2#O(hpo{췋k۪+٬N[B_8CmҒvQMC;/!Gi6Go0g)rˊg^цIϨZϼbTmI#-x+λ?7NGT0#~ op8%p%=Xfϼ'z㶓Bd]WPٺZš'.R'"zly2L[&Km#-t`EDl>o-R 1)fM^ؘUnyoʈӳ>/xdnɫy| u3ūuhI̼~ {"73fcW+1pX $CF\MZk--oՉ<Ǖ8xtOA.7uzwHV!V$٤A$D sdN{!+@cf [HĔ<)gdx$U`׈+L/ Q9pB޺cLdW)r~0 iJ0'+ _ɼ8̦ U4!6p8/+R f 9YRl4$5bZ>y22/XƨMDzi-P52o#1Znstcc7=7yԍyG؂#x IYιN_I 5Ij"lPi D0x nͱ}o>3s'[tՕ 3lI \x7I<x!pnf&,L1G>Xss\i*ŷE=wW%kk2G34At_k!Ɩ{qp8bpT˵ˊ {Nۉd^-#B[hd^JDԞfOEgљ'ρ;ZPm+[/<^C,2 T1tkN/7+3Y;&-9r@_*yԢ+{9p\}ܾܾQ,y)My7⁠n`Z䙧+"l^wY:LS{7G}Xt]}F/gרd$n,LiXz ·ʜ4:ԫɲe6q#[i#/ۓK<}' V4ld"L{%2OKc#Po )n1D]ei;P7 TE5G"nK_%&Vk$Gn W!zo[pFm1kM](kM xɭF{u"qpo%\[уą"y7#BmE.gVVGg6۩wKzhv0ۦp`h*o r'+R+;\" 2kDXxdl8NI}׌?u)L;<1ۏdiS"qp8ջ}DcqIyjCq̅?nYtYiOnmy\kzvWB/+킉@b&Xȍ\g  F/o9|HNVͤg_v 0^۽wyeMVbrZ#kyǍ}UKo<߸fEZ( NH'!É<Ǖ5%O#yJT{"u^v֢EZoV8JkKhy96;Ԧl*=R^#pe!X&f =|&ڣJK+/IS7&iS*F2`q?U9p8. nwo_X杄3䯇u&/m0֣z77wwX#awif^qlGblW>ʄul(]<`2/:a HTHӸ4ͶGƜ O wxD T 8~u elP¹08ɯJ>1{qOwwR&NY鬀"o0$KU@4X0H,Z5"7 "9a MO'u uI2z9Xfwno<({Ux/p8 qύW[R0tx*-w{\q2^1HYM8%emZ߃z׏!+*l=ObC,N3#6X(mp _G;f04 cSp{6'}`8 ;HݯIz8SiwK (mԌUyK;+8||^qGKQZI:s9`"XNCc]|= QXUH7APDHV/P<T XBA :,eAM*.Ogu&ʢ@hVE灭Lig;⻟VY0̖EDp8Nn$e y_VObD¥N $a\Uۉ7hdpU2OY ['sNH CT&|lF>[*a!sciLn{nͷaC;Xqt[vdڊĞ9R{cP܌dsq|u泳ޫ2:in.g@O $g$mV^߿c& O9ʅZkHQv"qq{n.okȯ@TymrZL%HYxdS$Wld)ʄd:hZ5a'z ʫ $fE#Hy,ALwտpۭ֪:d3Su4W^TMO6_UL0p8qpp|D\ O?q495-2嗐yJJ r5A/KXVK.yf v4 w_O1]kbl8MI=9!j}= == {'eRdpDQ5ǻ6%!=N/#\g\-yPm;l/o^7Y3dѫV(m\V>{39i)Fa RQQ^rNܼ,*!K`6nMJ;/@aH-g>@]$OD2z7=%wU|@0W`k!.1Le!8fm,ud4yL)l!4 G}kK\^oኣ9:HU:yAsKC8n^ZWzOuŋSI -JMYR.}_(r"q ?>6'*LU⒗bMk-$,Icwф^sD才ʝwȼRs*S0@w܇l@Cxs}-8{p$ʋrКX=`~,ROr6V L>9CjZx_B}=S_I}wv2 <8퀳$'ϳͪa4;$ÜnJ≡4<`-x,I%s\*_|5MόF%h ~fހޅSLm AtByZNy7[Dy-d^nD VSt/bKn38KK{ [Z⅛7^#g;7^АێK^x\F5H)xyK=dk%pݻc\+yY zp8ok%I8I9^z=*Ga7]-TyB +j+')*b<=Z"1&Ɵ VwO'Bt@B+e!ZQm,qhuZp8qq9-V 6ٔ'JjLRVKvS*V!BwެP+yfkyjQ%&,QAqe'A9)ړN?3`/ӃE_{MXB BP5#{Wp8ˆ1ekʛAe9#$xLqDGB=Jd^AP;j*:M2RJ-f̮$ooV /׋ʳ=ꦜPS9o_|O䝅[-湢AB Xxbn?El{z|P0t^ͣRbF٠e&! $L/x\qiXN9.?|MAJҊm] GTg{fjN%ZQisNM%ۏw^JN-%j]LLrfdkG@b)YH~cgyTS8o;qb W{^yp82p|qyR2c+ԼCa5&*z3q}a1OY䰃1gx]To5sAb-s8xt'X&doT'3oZI- DU NCl8ĤAD d޸.zB=줖pA]Ji_o:2.0 ܑz2xv8P|n"6t/ЋdVjJiF?|*/ҿv4^n=ΝkP3x]xUy 8i'n\5Y,JS_PezXwdD=gf3tFmz^zxSl4,)B24(Y}n]{be$gtd^O6s2~1p"=J '^x\ \·yUCAe1T`& Xە_oJEJagbwFBo6Er#GA&[j#tUB8>9Cjk6lc[&Lp+HuN7 <{A%'p8k=rզɎn׷y\9d-. ڗ6.lT9X^xeG ycH;w#3uB(8iXf䃁8L6ØhN5u@EO=תbn vYߩm(6jx ݛ#Ruň&{PxC%)^b7{gRQjSx_A=Wp8;K$\y%B]2CE*)ݒ|ufϫL;G0nH]YCm}hJjxv\pPx%fRx=i,]ZHS ї݄b opq当Z8!ꢝ>7@l}bcxBTeũ鲽lF-2U>e}p xAUl/T֜51Z-fREFuH9s\~nאy[1[<Y+uB]GTkQ$bU2o(`;7a%'cΉ #B%g^_EG{+FlN)ţE^-DK=\6iꚙ0S:MXv?T޳s b/4(hð@OWr:٭.nd]ROUjitx|LҼ%>mOd}xNޅʞn{/ɻU <8 _Q%nh[$XVhg!j |]Fu )sl8O>D*tdp3xT*LbR@ 6U+B+#ǰ\tRII)jXRdf5&HhKXJOP%p'}ƀZ?rQ\'SDfM`jo_2:'poG3.#]kp8ˎ[ׁψwkrmw!ō]8/6ew~c3#݈F _T6; 4u.to}Js{1Gˮ*x?988_ϿoS}L,*ZNUqܾ@wO?q}v)2 !?QV=xhs}no)ϣp8 †*^~M[ C z~*і6$lc;r9uz5"L#X#D#dDwf" ң 1M#7^lu'JN-'Kmxz{Ug /kL~+wѯ UxYoCK3벪u~ܹJ70[ndA)n УgkV=+xْ /1gCBll8 D[;_5;m4R`pͽ4x}mA䩶 [֗a1ىϞ.ps9cHM d!sBd=ky}n'S2QL֎fMyyj)}[].: zdeyp8*n^?3V"}f(T*W(3pM8[)͟'n\ /CG.^x˙%K= MTL%C[4-[Hب!sk{P3s{PWS?|j 8ظ¾يk/y^f\W6Cސ0xKߵےտ}xA~]*)NCuG ^HK-2Lv"q.x6o,DO5&gOtA.{v~FbO.6k#,V$ *PW*7.yٌ P'~+ #N:1lqa5㑪`uv@brGOTWJr8>c>*X>p8lƋ) W ϊ\T^,܆rG%n`37UdM-|b`L7UZh,<{SN kZ4zIӻZ,fLe20q5!@E^6OX?ʆNR$g X&N0UY5:Ԫ3%ok^ٮ<#oRJR[I<9ez]ui5DiZϮ#Uo'{ͿMϿU-'?Aޭt+cS{673z dw^iw7NRȼ~֤L<6:ͳBqٺU<L;H/=}ǧ0ʴu8p,ݛ6H-5xkˌH#Q]ԋO#dAה(;/ FT`=(zZ70'?UBOi($l+PEs}1h3Tzbz e/k~KOhstOv!sy>G/n ?̎o6!qcn7 {&']C?_$^K2ۉ<^yߵ%oo݂{7]j,:b[cC]d'UJMH~NA-)W YʓE O?V[MkEC_eL<2g^8*ŹyY17++MZep8w>MMd^GFd^tS&uE_p FpqV …pV`?5{POߗz,BGFΊ^\z=1GZ^ƘXk)./ gۨϔrk_L,(MhI JS[in >x,4N9Gm-B n"h~f= gk:F!U {0xqB  =i N, SSI=zƹ=H(~n\\Z^jYwn6Ih?9 6 IDATr4\ym_[{h&MyІ84ڄ]g|3㧹}:t ׿wi<'֭P@UR[nڌlklHmƌ\w0_g6͘MKnDn" @[uu_g{"2<=<"3sp7P?HYS!A{EW vOeSqDL7Y HZ{#5I>;aJTE ~o`<'>ȴ;=FDˋSySR~0c7+'Ѽ^ ܽlXTTTTTTT `e,YMbV =w,NՔFLgTZdٽhLls\cK=l&8}lܟW}oKUܳ'{g>?>;O2?ai8za8RF}1s)9V G%ظn3]'2^ iFN{?)foܰ飭@4kQ?8uX5?x@q-$WHM{ <⾯0ͲE7K/{QoCnBnϋqCmJM}Dazр%+Kfn˝R '\l5"[#* h Zyjيv 2)D9

??UOϦO>qF'E J1a 犖n ^ r6A=K/ ϓb l喆yǬXr#ˈ>޽Cn=DOOe!u=I18`TP^v rc(ym|NؗRA7=QC _mͲc|t"QyEƟ~<_9 C(w)Uq^[++Hz=2֝ yY`[@Xtb]sEL O* q8UqLȕ>j7 RO^`8ަ+*******2Tn<\68r|ܻٮ|!6)2b}zCm:hW5} u6C7~݃n?'[1|^g5:)?u-fkdVXБF% i;ٚyq<}+VǪfa[l}]xv}^[?sNK\U/Gt j:G+<WU8|aR.0ēHPF?>N-R78 PoLĸ$60;\6R> 9ID̃d{#1??> CɨY/L- (w17/"o9-n O )|[rKuez.B@70lN \G|yt~fsCѤ>#eM oUED) Tt)W5R0v^\oZwAV@˗0]aݻeuy'.W$4yBlJ纱},F@eDyBFMV}Np}1ZX;Oq84rQ9iOҧongXی9^ 86ʻsb=n^*jIgKu{i5L6 7ч'o>9;7wyzd,vve'{RJg(!MͿŽHA R߰%l[QB"֝ր^.8{3n(&楀y1#!]X4+/IP\yKR;1!L\yՕmqL8Ie/2#lqҺ~ qm: iۇ-sW^}0P1uk0Sl`kV+0͡K%X79E"7##bdMÞnCCb7d_ Qy :|!<.=s &X ²bY5ő+e hP ;=}g8qEpߩ8n_G_[ij2i9㲟'o5/f/~3. µ:s»yrHK"N'XDA=ATγ 4s1y`Aȕ tIMaGBl F뺒<)=JqNɕ'ɓ']og^WCs/,********r<7~+-+buRU;ڗ0I[3Vu0$P`i"870 E:=73ա^7|PO);r4.I<^q0^^ʹ.\%BbV 9Ϩ`u؝/6,K9k+^=ˇ<2e3AS鐴!v\G*8\h4^gmdc|9.w][C;GAXK-5QgՌ J''m;g;ڛn#~6x7P4Gѝ@FQMh'!{O՚R_򊒥Bjވr?rAy׈+`Qsl b@/;ρ<6S0+3.Z <]#gg>n)ʋ nsIC]ҁ\%u \NcH7u1{>]x1ٮĭ?#lOpᾣR4(1䑨+ԣÚa7=@/;/&%ta8jai ,e|qI5 ]y] /~vl`rz@6 b_(\$¹j]3A 8]#]?7 -[,E YTTTTTTT4*5Fp3րrUxwzwpxdεFt72 _?hxgiq \J@niզQvmBajg]{סww<~N%쟲;:g1p߹_EK_7%mbJм;2I^ iuet 74.e.̳My&i]n%'hrWlj/hGPДbF+W^t&csF:Jwxq5>jǿׁlEUe}#,*******pn]#˸.yM|y]M&/t:GIf_vNӴ4*[=-j9zhY1ȟμ"a$ՒY9bHz i5YjF0/5]fOw 9zqty,uFASk#GZBorB9RW5"x [ Yޕ7#";5_b\wTU(r+R0rYrZU $B>WM'pY7-bS8AZn׺j *_-~ ?~O<~ nS7Q^jܶѴsj-eҌsyxqx %ܻCBO؏-1UZ< :h8Kׇ*rCB{݄9V6i_sG!ȗ>F])A9^@9u_ywR/![so*co}g>Ws +MCj΂xAw3ܛw(qK䤣r+! }(襄یuCm:?6l bb+![Fy"&1 9Ms,p\wt! _z Ӛy;*[V+U;J |ts`іm̑/O'aiubo½{7/azvg_Wc|_~-8!dOоËVw$(n;)УaK 09o_5M tp;v~ A:λ )9X˫օ>Uw$.<]l_?[x֯L`E72fKkkοM4Y;|!ppҸx,. qfs_ M}@}% xt4޼]@^H?Ľ}C@4Qp/މM[Qҍ鹋0zn691b`^d~ش>{avxv ^ `<.-WFcu0w`6=Djb+e(ȋF²}l۶bsPA'TncT>Fŕk?x[XpPgk|m 5-B IDATȝc[nS#[&Q@E4:V\U\U=n[C*~nP:FEl^'9/i( QNf0n"!q"ecl/8#w3>A|,χ}`ϸ <~|uw +It%C< / RhxqnAH9p>x⠞q^mƺ6-ڗw)Ev`ٺoZdA>:WZ`n!oxJ`faZ8Uyzx:Fwb3(76u*˕'/gv[.]f`SϺ8r ݻs/_BM,:=lCC \N/O_wpz#'v.6Rғɥ1vP/16cyP~'g,$Ce˗bSzh7% Z%y(l08\y9a{8zhS7 e2 |Blfx8^F1se^pb 2ؔ( ir/Z pdcXՁ4 yɭS_i%fسV'uM_o5`ivkP-]xUx5|UmhJ9V;.@˜vIB/ B:6G:r&xۇ{ծ@.[:'AT᷏6-b)O;σ-'zu7p|0i@*ΈD n:/㊁@u5 q09Quq)Ӈ8~92O'x$]N'ۼ5 g~r6 wk@tʖt'18 A}|Z%HQQQQQQQ%е iJ␓䴛@DWU 2re?^uo;^kG06ֱ:r01yW0<`^xׁd 9f|s 2QX zccwbBmzC-bCl7zTX&¦&ĕ7僤H1 >=¡P0b83ND$u&)n|wkm6M}fxͮ EEEEEEEWSD|Ia EdUwjNUI=o'Yu+v1f^ _~'`znT%/{ <OAHNYmgkёZ?d* h[|py^ nK߁O#{wʻ@I_؝]q60͛28(|y=cBl[P5Z0\vWyOdW|p%6ŝ-T Fq]I#qU7|֙} 5P5`fPUUVԁ֊RVo*A} svB H$kB ס"ϓ 5Xᝬeyidb:;WEuXxd y||g eإkPo]ǬY 5'YnKuᑿ̱epyex Py1E D YFXBJ6_~ zvjwʛ i NQI$ŰfM'z!X"UϕJ\k]ݾn Bt麶$lٔ :;-x~z +upXģН$,ҜB^-p/<;Ϗ Y#?ȝg8O$a"}IjƇÕc5x<γ2L%t6 :=_lusάq22)6]D) 5Ez=!qY% >t ɗgp_4?6ʈ?>?|>k׿fH׆֙7QI0q@e+h'g\kk%#,ӮypT;ZGP}‹=fRTb MEo ߞ|ܸ j;/fvֵ6G ;"E0ۂfrҩas~}{gkJl}%tu(Imn'qzY%Ԧ海AY-жw䴪9wv<г16%0[:UyW^|y,#)ٕϓiɮ<$yUL7rܝ[탥#a:7;4{@+jyPTTTTTTTC:4f}B% H@>׃ `Z5ljЩ{կ ~_ZzR=H˃ymu/~pR荀^_{jlLZ<>_ 0݋E-c`nM\@io_yp/aK.G+}{u1b'x]΍,&N:}\~ |}iWWq"~#])&6!^^4dμSZ|$p8EKo6Ћp-ƽ%C_IT 0ʜ%5ٴ?LaϤksUh (0>Ϗ$7-5h]wrEkwUr,*',nXTTTTTTT_V2Fӧ} ) yZ!}NYC$u9ߴ5=4$hܦO h(i 0&|$3@Ęmm:T@޸|G!KQ6 m(W;_H72s %K@3fnE\f =r~j]!q|p|5v>'R8{5*R31|* hˋ?: m݅g vQ:y]z°nǝ )yF_$Ħ-zP4X>f༣79+o]kG0 \k(t=7 tFM܎I$A\pEEEEEEE*RJRy"yHQzKj-dyg6rn1nƀ8cgdjg4qHc-Gs'Нwm_B@CؖZLw mCn|w=ˉM7/st/0Ck#u7Qp/YrO5s׷f[gbcorhyep:/L+'j8Ij=l=)R3W&Ma\6tx$۸|v3՝g160Rt0fC|yBl 3#o _j%c/ o98}wQrݝ]h\\U7CNq ˶#ZVTwEEEEEEEO0m4)LuEߋ/#{̗;l3g]<4aW/#41=Is9S7'JQLU;}xGpMm:y*`D]+P7֙4Z..&C-?l!C/TJpL`fEp{%:soѠ3z*NVoG>O~ƾcdg[:[~I[r5T@+wUHM #=.榗 xrBz =E@ },6c`tPOHlBԷG1+85Oa ;ZOL;86 ;Otv^%0ҝ^U%O^QQQQQQ^@{&'[a8Ħ(;5Ħx"tW& aR͉ _WNo\xe tĎJ=5ۘ:fF1cɬ^;ܰzW O_@ v*̏qL~nլ^Өp 7s܄P٩}:]ɔێw2ӋB5}\-?,4zmy ;>xJwfPu}\oz;,NͬgEm=Ccm,+Y4%ʗc3F{uA׆dm9^.4vڹ6zPѲK Vx0@mͦQnԜ~%w{` ڎ+O5@M;fPS!65W^a^ԅ6=&eyɾno[HI֫h~eu6<ǻW[|4YCܹii,3օx,!Yx^}sT@+PH mÅGsky9fp<;ٍt%PK;Z}8ⶋu==oz<0[ )u N/h8(nU5:ޢZPX !5歋m77fxR*!Ԫ5R#lL= IDATɒ.噘P^y#̉3#]?P!}D@K/źB6wAR4ns!T2أ]A콒szzlm7'hѤr~d(q!RWlaQQQQQQQuz{̐o~M߶Cl4_换p`2.%+ Z׆`>t=`bV/ܭx t"W,rP} 蟎4g]Bhri0-hA֣oE}C>)YEVDDt5}1vJV`Maipn#[ׁ=wm6fp]9Xz>6;b,;//^ǰ^!U7/XOr;C!(%;.K@Y`^ _ cUT샖C K<kO[Ё^W6U 4yxqG-pikk9vn9,'/-kEK{ꄮKkW{Pg܆F/b}q+7u{?zVEVm0 MgqǠ~;xr.^=?l@(̓xaH+ޞE/t׀5 Х7zlO΋ɛgU <?~yZrk * :E i"a:1ɛYɓWTTTTTT# !5mGg8J{"W}=2 0zGtuWl1T)9*kԭ1+ +UWCBkvA=!TfќZ?Ds{O|v''KuEt5y,ek^SiZfxnɮq͞5.7߽ܹ!,ƙ㏛9G/3^pnB8 ⥆"oٔ:Xsb^;/a69ڒ)aQr5g(Fa瀷5|ȟ] OH[M~,|Fׇ Y#990&D)cM =yGqF r`{rIsp 5msJ+Aֱo9 !8 au<#-ԋ(k/c ԗmsUpag@pVʛ?f@ G׌|?9W,~K"f:Bw{w'hk+^QQQQQQ.4A1YVht!61_^hl6tԒu&# >1\B)a:AiP;q׀К U6?^3u$p==q^F/nHҮr%+'w.6lu|M%}~I ^U^tQ[yT@W֐ Ǹ GI2? yf()pŸb-]k3y#d_]yRj~7mLN=5aB<3CDK-\"o) ӘeQ4OvrOߘպșKm49<>|<1Ox/ρGG%^@]aqCǍs\yhg|sChJ7D.R{KĆb\w^L޼ "b$\ W\R7ϕG xfן_:EeB;ߨp/7D~\vqF f𭹝$@vӟoMHv4U1ʻ!6->V9Yw@x?~d]μn|i )Gf:/=/ceε"h]R(_x[ ]ǻx b#Z+-&LjZ <&m<8>x\* #F~oR` ^ OP4h%E?QYٛإGzyrüPMg,-)"u;ԛ: (pwz1#rNǠM}s6XJz{,Eupss's|ymZf|uGA<溭*co]|; 1د> 5gF7FPy45s*>gzZhM ̃y.h^7q]eh ǚ;˱goZB`rAl&s\zʰcnD4OCoJGوoBpˊd "C[nj`] yP7q$ta)Xk7p ‹t7:tv|mɇ }p~qs=Mv`HMi^1yf<||y2VWştmj~[zZ|ygٌ'aa `lL,p¶=׹3y,a]i'oMr!6c;?$vEXGP`7ԕx;<-H;S'6PC{=xiנ^D'J4vYcc9-;^X䥚oQ`. _؇VWD"F;wܷs:qpj-V:bi1Eyޚn}abHU1R3V{XɃŭ~7)Λ7/ E (p`$Օ7Xsى58$ܾ/Agw%o5(ե'i!v*.=+******zuuJ U)q*ĦNi|y6Eԯ~Z;@cjWUy3[״00Bm +\syVsA=O }+9ŭ]$)Ӌ8^}e5q ŁŮ]Yb/n/inwHj e񜾇^Kq HE*Vjy~ě}y;exgDzӲ&bW|gzM*s6Vk!湪RMc5}3(qky9I`ͧm-s5M?I}Ncg%!|z]'璟",moEG~̏RwMu]_Ǖ'NJׁ{K:* eF}^m!7^E>0YwfG5;c/ Ȼ{G:*"7vX(&UH#sᷚDDa7ǝ7/ 0W?s|bsiW6Ky<;~&ɡm-;o~~̗SURFK Gŕ׆K̢݊[貪\t-b3c<1;_8w`iWʙ?(WXۅY&帣.Zר6К?7 m7Ztt@>nFnu>e&3׀ :5GBi7Dhfr"nfcKjΦ@,sqSU]rCj^}qr'r??/sxn7`axn7]0q7KiԹDpGU@Im4xC%fQQQQQQQf5͌w[i+C}ΗK{)5o:Q_[;ayOQٍk?x+uݬ>zy<:*VyXoɏ7)mCHXo}zv^<}x2#z7CЋu- *Blp%x'`]Bz8g8mjz: ҷal/C)<LWđvBlնwZBj2\w`ݵ i _CoGϔSx IDATCs1u4*_k֮+M#di+\\IڹãCdU!}ߓ |\Y0!JyLO3#y=8s+ xVAVsf],}P!?Bpg wI~ʟ, EUDHMyI'O/#K~ ZO(tzRE8T_'<8~1ُX{@Н,aQR?s+:[ߓ{Mx̉Y:p;DG益&2tu1&y+-ȅu5uHy:}o6fCʅGP¶K0:@4m=h2Rwܼy@;V`./X߉wx" Yg{u 6הM[]d5| /sbu3k|ns\[D/ Y5oJ>-KGBxBjZ`^“ոG Gfz<y]b3+ g'>+7N.Tλl@̈-={'ugyCy7[G ۮhŬj5ƾwjOʫ׀w4v8uݾ\?}Ѿ\نjUN 6^TKϜk^*RjY Fl˾#{xsp)~.;>ہ Ȼd?Qpc#?#:HHMJ1 bc/@{+ <4ye $8բr-u%^sSkN[xDQif/YK7`ߕ I gEEEEEEE f` ٫[ɗurB!|2;@-GVyO5ث0w^OdjM Uww4G'IRCm:˸Zr˺o"]m >VJk.y\r.^JOgqyNڑ ȻL']Դ8tstm]_R3ě s$ 0˓7Ґ$_^}Ddž+/|VBKtxu ]lfaek_D9gGa4E}-Vxҝ["ueWӨ?UuyBl[3\nZ o7ۇj :WiD.ܻ} 8Y!:CЛe>z\Mqڴ6o״5^]rsAk/e-CS3:bU&5+S99 ^k"uwbB/=}.⧨n]|6rpn<˖p2c@Y,£܂;~.XwyˎKD-O(ά!6qɘ?.O0x")5wS9s% Pv'u967pa՜t7[mZj (ܾDsFҲGs\rBc}M9BlNe\y),9k|­ ׂy;G[tNuN]G_43JKw:Ħt_^|yb)ygnC|iGgP:_ZI^ZqۭCoAs_Ȓ7ع;,L闘g[ǚQ  By&I^ĭO>$ՔvƛRA< vQ"A=1 9*\yc%k>;Q/kH}@6Onxյ[6k,nK nmVqT&",u5y¼3jXK]L*1in<\ O^٘;aӁ=6Lnlvs]k{Oda3)1fd{n*œ{ |ZXz9=T$$j6CHV>xȼ|@S6u?.Yλ+uxsF81t*d??Vufp\~cq X4YڄM@ %Sh!<,Օxy:? [ZM~Gy$F˼+G m!Šh?蕗/~˗%xlj&Ky9pqf]b!Y2oRE3V!vc .*օxʔfV´:Zy4~CE:/˕D윞@_ b6-|CxIK<@D9JlZF^y#cnYmz/8:SDLP&R ˼;LQ$ BM 2 2lIE6njǩG[P0/O_o?,:V@d9%^ D^ǰ<g *isKԌDz%M4w^h2O'i.sz$3*ٵuw| k-RUn.G 2E1}Sb4Zx@ :Wǫ/O^w9^Jv^&U4yyڟ6 g-i*FV "/4,,]>2q*B s=$aÌײ@[_DzŊi]}3̫Kf"1h._^ [W](B/_2x4R3eK$')OfIe9R!V^ŷ\KJr<M <70 QJUI6 ս"б<%OSՑ"VD]KuoMR\cѻA8u@jӘ;o=BIqj䦨m<ͅ* ͆A%z7dwG/&ɥ|t B䵌.`y܋7/WIH<#[\K8dز%6?ؔzH_(ꁼp9p~f% Av`Pd~7*f?}h@ A,d]%6/OGA6V)iU_DݳdThHmٓ+p6ǽ f@]ˋ^x?LfmW6̎΄akͥ~Wjݲ{@a{K V}NJ߉4\o k'K6׌A_Yʺ/:Kj6Hxk& KLyNJzS%f&.2%]rrޥr~4dOnEb(y׮ٱTmL3.2F@W^\"s Nz2>f28(w#We ɖyF*~%ŭRcYy<_zd5<y ݜ̇uZ5YjӔ6'Rj;6PCHs~|-'P1fi),GKJx{bbq<MlŴwq-%Bv譗j J ់;3pf.-)$+;_wGRӑij"[VC9y~ʰ,rȻR@DnQ kL{Z€?UTag7wgKoƋ)W^{ڐ)UB3-2yEMJf}I=p~ |C IDATeK/FIX®荷W4mɼ-}n}7 P{8}"H'ކt x[c[MݥG!Z|3EY߰7^/6$Y^o[OmA]"{嵍d[3d1{LD8Y B,}l5Ko|t'B @ W^w'^^y:^^%!L탾yդdMݍy$6Z»T2'$Y-(xXz=W&Hy@4MƭZU ЖXtmfHZpj yJJ)Rw kݰ&'-11$-W!U[{<|!|qf(kz4˦>̡Rœl;\繛4&BK普ʋ ɾv\d~ZaPq[U`hBg &YuOy*Rف@ r`g2$VeH|qʨ*#^^]4?yuybyXwT\֭-OTYh–H5e*ԦJECb.gQS pZb χ/ĝ΀/K0܆$a6WwMc"}8b3S|]m@#4Ȋјy{;T@ Q90dΣ/GcKu4d^ѻmm靗J^d(l,FebƎh5|hW4&w]_PpdºJuM , 7Ot0cZ4{A/q/qM9* =WWby ȣg2SOk\F&ρlUѶ(9V#T>jDle{<=PD#{[d.@ dR(^l̀ W4NmZwI@Ρ&:V, ?P5$ iRDB= V1"UXDromډRQ/%#y _>v>Sq%wǒVF`Mx\=AN6 ^ o9<&~_rρY;Ryr5jpj:r` k4pS*L@igJxy:G#A>W?1W$6Jc>*)zՖM&.x 2&A%rRP߷x{gku]O?Z kd__ڒ$b/8a mI<{<-`gNbk.+*\ň;>W^xQzit&]׍x~Q56Y ^񟏝1=@ adG5u/OS y5dp/Myb^Z.0UeVg]0k#RO)šЮzXF"`>>iC256mS !PW >^Kfg64-cN8&1]cטf*.Ʌ"6tuf`X5ԂdH>ɇ[by@ a)z*|N[K9!L6?ȼ/2z%XJsJ}7HRwX kiaf0b\3"||8u`@[N.C8i{ؖ0d $rgje?V9 PIUy]m KP o{>YdsEܝ\iJևȴ8C I(+:h{Ah+{;h$@ aIF*=*Ցp$%%kvK1ؠ d?I{Z@T}0n;/M[^zC{4Z1I^[#towrV!bn^`S_>NOq۩{W\i״~7S񅒒4~QCOFkr$ kpGC9U-ޖ@YlUh1i0S90ʭIC612^#rȼŸzȼGy9DW (mMJKkf٫|FO+{#ȋZI&-'.ѷm{Qʳn9Sv1&"[wc kxϮDZ CV$ yH O>"O AXy>\QFq e"$"#T2n?B跫fEsvmR;mg/5,]m(J,QDn6'%ys'N2a`")4"U;cT,EOC" ְS.I!ti<>xg^,@ A Pr0`kDW+ɔW7,W΅6pyi\1lWG B/~~~x?.23'y1bin:ncZ`2W"YsE bba( 0y' 0dD1ym p{~Vs~2|/"/  a$yz}98SD<ȼ1OD-RP4"bao%B @ ,0r AY~̳돩6k=&U4Wxvֳwtۑ]맞$wN7Wb D^ '8d -m:~}Z*2l01J648:d)}'~ww[9(! #d<׫Z@UU.d^u9@6&feKztKoI,AC[q8S_C(jĥxA@xǠ E~P( 8RA 8HsS>p:Ux<=hdW<@ q )^.oeة"4s#з&o[dHMh#yuj+zz51&δgk)aB3z?;7  @EHsE0a<&Tơ/u݂$K-Q:-/r@ gYm f/&-g"Lҙ.d^mBylZWS;o2w#IyVGH*[1C9ܽ5>}m9lYH<'1ek<ګX<:0BNTW+; ZLfK8XW͘߸7SBd FeO@ AH9@IOU̳'d/#t< w|fnjct*jԛ i>P+ D&e<.Ծ@Byk ܐT2" x9Qɹ.4u3mV[lIjLoަ:cE;uoW @ U&ޜB36WỲw\2S߸/ϔ<@B9bO=$5;`CU7"]QUl4<>_jnOY bg@p$@nT6dOmd@ I*"-֮M^t?3LtrZڬHLgb߃%"Tꭗ (Vz۔'.#a ēH"/knE>J8y*W^ͺxk?-%yw^if$H/O]r-ܡN MElu{~H@<@ 1 =RyyfmE9yy@Roͭ' /Vb/'E!v7ƁƃEwKQPDy޷lzk cu pgZwCU+d^)N/G֡6 FONeF&/H|{}4{n4m(2o* @ "ٸll h<_Lkɺ<i6H2/c={!Ny73`\k^z_q~t![[ `xA2qcήe Cl.]٢I]Wupx,:-Oʊ~"lHP?x }ֽJ5@ DN@BJ @:6Ƽ"BpLc$L6 ЈR\_d3qCx^+X"\(HaOVdz,U$l!sLȳ;+(#QM eo zC_VNΕf'x}%d`oY?h"Ȃd|w~@ Ȗk{ w+̋M"[իSaWZj4W1X[XW%ŤE#) W~͆-IO_ێnz4!݃Qֲb>nO]N^yܒj~4oȩ8>/kMu:]v\BS=3wEcFa/=i?$$>|HboK<@ q%6 a_2u +~C;@3ql1uXfNj;&gو4Wsn^ɻo.e~ ZyL<֫2>Hp,H-aJl;gێ%#q|\Xx!霷 lHN^-->b#Y-&m|{y"R00@ AuJb+%ߝ<yki $cWyzx]2 (h)vϬIbԽT:{U}]06AK"_ʏyغ>uY-⧪OF^&Ϳ5 [JgrYRwKv6Ѭf996I~gl}x/C<^ ' ml}N^ykIk$6kɼKƈ(Sw>3IdSr:Kl_0yq|q'ޮJd9NoIjV]nfA[3q#k4ƂB_j6rNu1aJ#`<@ VbnO2]y5 WvL3):MXS8!ջHo=f ;Cւ@^㏔m5Viǃ.hVeI$6d"Uɒۭ1m< $aլ~3.&ql+m7ݯ-J ]+aJx|ʯN-U if:^ȼMd`7+*.Mm:gjvyו-R98CLu5gv=Y!=yK}[޷Yaqh+dk[mr5a&7+ϻt&8CvlRzV7^\ foY,ͭvմ"΂.jZ-2 >XD'Bz+"O Aش![BTܳgnӃuʙ馗;։ nM%|κॗÉ]8Sn{g@C%TQeUIiiO' @#~N^NԇdUX%Ġmk/=ҥfwtb y0ZXXw C22z#˲e9; GDM}5А޶@ Gɸd 8f20& h#F\կ'bz9ew^MHbO]9rV 'p XN8y^ouy܂jduF2 t|nn8?Ã5K10H.{v=rٶڽmuk)yg{z7V5>C~쬼@<|S1qdG~8g2fo 2h&n;vRwGM/r0WwV@s'ga |MxċLd]o)>^P>N RY׮"_UAnHSc7?j@2; DZ )52fImfWyE+Al)|$^>yr!ã%?}=5xcćzaN:f}߾281Ky*Q0ڽ"T-ۘ!$QxU ÏR vƀp@HȼwlLytNj"GѥF<{xR}]ٳ[R@ BId^W^7M/BqpRNf5gBEZwHɹ|"KBbǣ#ßbp۝tu5<8'8^y\`)st-YԆVAE jlS #[-7beǽU˼u9Ws)ϗ jx(JV 4MOy|,Cɼ sZ<@ /?`D<ӞEH2or&R@qu2I*3K7MV']mT[L=Y;xLeM5`xTDޓ?m',+ Vtti+(9#YwgA2oc`aT8#h78gΩ|7ud-p33TmYMc#n QSԝ-`%]@ AXIl"gK.(Wj'ԑyOqj괕L3nL܆w;QL}GIyڧ#͕q1%rQ:](o!2ǃG3~yÿ]+Ğ 렼u5Pmq3x?^Ni)p9y. %<Ғ I~u[xq΍cދr!,Iڶ񈀻yuu/`w_ZZu5f Q%MQ=V1irY @ hy6MRU[ ]&mVqƙk:nNI3;Ϙ?W[;襷H%vv͜c|y px3N.eN  nXV5W' ^yäo!I@ru |qNa72-'z*W^3Id;,l]HV`A Y<_M#,27fQ"[#ys!KE:4v[,Ĺ@>"{]^od%oap=Rk,5]w6<8zf ~8P4cĖcnd(t3i]= ˶\ÍW6vFƛBONY߲Uyx%j9qJֱs'/(sM|QƎy@ bi-̳#4yt^RnljeFc5B/P[leÉ띕{yxo-Kv*Fh1x"/wA)Z; DuƎl&X}%r"ޝSJX{'ͺ}+ڴ v0ȳ^x{IkW:ےսo n7nRxq(J炴.#Â-O/ ?3'O A d^v1n})'V( <-:o%j#ee|w7^u"~yqc/JRC[0&ߪMҟCUs sI7ڢyyu^T*9G.XÒ__̡HvYV",t%=ê6O G H@-u[mt3kw a흧o.Ndo'QQc(xg-'3!ay;1<ǰ4 "6kuDʫΤؼ'O4=y-<;Бz*4 Lqg .#<d5[$ 2s^36"EtƤebhG64bqb[[r:R Aw7NO6I&xEW{!\Tr* Y\Bf;t1Qzڱd?5"Z52-a3BFա /ϐ^WZМ&^UOU}b{1LM5ບ@L$Ii og]s:n6U@$d INxuzLsak|¡>v@A.Og2~i*Y;rf6=;w륗^y1X"I i> 5$>~ qԦ]9yyo1~uM#iL^y9A~6Ҝ^+ϻs`26Km$6Ud{jEI=^OкFH D9'x|2R6ρ㋥@ 0aPD+?Άˋ+N{~U0yx*B};/dkbMΓzȻBqdocK{lpo<ӽpS:n>' ͪĨ4͹O@`2j:C[)RۃTk@0$HYCU.;B ͫ(؆EH͡E<2}ˉ@}+I<4r|>#a˜/_^$@`ί|l8WB螏WII Gq̳͝"YǽA5mD[gmvxqn$y7(ԥV/ײ NFJYy5>}Nd ?Wnӹzf<ݙHmρ|3zӵ%Xtu[4򰋶vY͂$N|^ЂeV)!p> h}8} /&e-i2{1iRŦO =ܜqbUsbh8vպo)2P\LD^g'?h+^kj3^y|Oc]"LI^M?D&%2o3=rXmrEoXQN.yV?IMcZ&KM,;Kڌ 'L3`pm鞧vIC 橑 J'dbA@ >V^y5zdF9!Yfڜ.꽸:YLqh }>ZbZ IIbܿ2:F=+l[=3'xD޳$;O#sRo9yd5Eϳkyw| mhNd`"P*'6=>;$Dx$qgLd9h08Ymo[k8IqrN=_l|ҶaYĦW^d:n×Un8ϊES/FYu_ɳtvG b̪s~Kjdo5Hu;ο8cd wFo.YҶ}xhE^O64,@  D@2ڬzl8R |ۇNl>B]2CbXr4LeZ<@Scik̅@CdO2oP&Xd7.ΣooA~IT< =CXzRK:V{n936vԺgp=@"`W%ChF 8--?V  tIޕuFM#+AU+ڳ;`靧׭O gߑ_]aк$AU}] α$5%즇7G/$D$-;<#>q1Z_5@ f) knytU}iZC΋3\]+k'7[+#ȡ1siNJJS~A# +\=pg#%pz\M y㗇ZIM#&#,Vř"J_Hˉ홧x^ߚxPUʺ>%/^]!VXTCή9sO۴0J|^lKMqh s@ a.|jɪ_'G#&zܼ48d3tNnӣ ><ڵHB^Ggy t%-%5қږFx+nn]b " o<[*ۚcE%3U߉=l ,Qg x=pop{xU烑y@ WG)n^RS_y,_?RL;&pȻ}^zBc|F KE p3y t$vo>U^y5C4LwH8'|U1~$)Od q[- xSR]'l \v o5uIo]Ep$Ȱ!s$߹@񈀻9_m|5VaΌcdӂ|>sjv!K @ -O{$27ooZ'$ ǔl;%MHLH<2j`xԎK38'ΡsD^'\e{e&6j8Sw\F$69y\Bdރȣu(zoQ7tT,"•qJ{5`$9_[=iXx65M׆0Ϟq-2키7I|GMߦhjr@0L!27oAN=GYYL/,xgg{(8姟΁wW2?sDޓ_V,dk*l50o|0l~I{lED&&Bz*CW.,M7qo5Mg]KOqiyF.q#'f %8Lҋ!K,HDYJj~J붑|%eL*FYR> , @ Amy.{i굸d]%䠝va zv<㯃o@e]d*ݻ fKmNK<}Ҵf9!!󖅺yf+;hϺXP^zu*-do˺nص,ή"M#O%$ٔ7^jBxㅋ~bqCJjzp-1}klFBNĐeom-`h+nDWH ^:Mzim!|JMhBn+'N$@t;?nI<8K E޽4m6HlRbMmMm%fCJm'elBO_WUIr[MTח,cKinE&.hUEuɴC/7kεWӈp ijxz`7/ZA1A `I)`ye΃.tܼBK 7  q<_C9jPlI+.e{m-6GF "*d OX8o<_r1;!y|'h̩z!(iM@ yFnd&7+nRAj0;[O vD/=x♐=Y0SEI<<7KT8J^u^+xy2/צqȥ۔h)1Ыm}M'nE({"WwQJS'g^FL9r!]eXJj!M'7 B$ ?վH1*ȁϹѷzoYIe!=rW @[m *O  7ؖ ;O~u G:襗f= c|90׃7SZ@3t;?^js] JU{Uwv\bs-,#4ycn% <&e .{yoz :Eh/j^\OԠibLӡ(7{.ȫ}A |F7K;h@<@ +2ѥyYj3a,Wf+QPXB0<ҒxT4u] y?>>ye^*ݻxQ[kFb3 4dC{6-Bo,l0 {=^QmX]lWS C1B qYMw Pn_o<WEbuuf&~.=ȴbё>o6m@ G&ȼ&@hUz"v9J>w S* AO*7zq?MhMㆠ\Wٍ"H[Ҧ&3& !̫Vj(z _BhChsE߬[՜{# +Y7k ,C/.^SZ3C$Zd#B{erD /č'/ý*(]ml|f'^ݞX@ byGܼEnnlt;xDeDq1Z#ַ?~\n]qSǕʫ i 9d̫M[WK7%6,ז\3* =;KlŻ3m¹hI=feɱmx/.^YLKj8t!S(¾/wO ,2KUIрy6Ggn{:] }oۙe?Jٕ,.^lIM"nΚtn(PD٫8V[κiԶ&_>s\@  ^(ǽ+uQ.椼`k">b:$Rk`(UfLeyAC`<>z:> weN]ݢ^Cv AJYʬHlZ]f:Emv;n.y}Y*! }y#S )ͺh!{xxD NňYK9O|Imo/ @)u78@j$zq/,32J$݈nϧ+Ļ{ H}Gp@E8PGDo~DŹRŽ2󞬱|ج$lMMxy632λϵ?*1{[9H!ɻ*˘΁kӵfxU:xųI.Xr4荷HK 7QF= oljWswuƭq؍@ kM \6i(iبTm;ȕk˨ 6>=#`otH!`y6SJ߅I+icGXK˫02qȼox2oiVFzk k~B#)s,cmI[)͇,wfsַ{Kti]߁,qkt+sP OI3pXU?8Tw)dzún6uNa@cET.dPV<R3':]Ṱ清|^1,3 !B|{6M<6_*>X@9nQ:Q1'9]ę9lV{X9=QBNy3_ s4$-E <}ZpPJ1//38jg+$ sf!VMH4#Qo7ImdVx݄Z+E>Kjۭ^/ň\*XlS_]XƟ<OȘrǹ̾~2"B!2b73n\SU] Ᏼbxy՛ӵܼ' ao-| 03~Icr!Ç1&7:Q(9żA=gbs5xFE^]Il3yu+5\^pSxc"IV:6 DWH8LēkR/3ͥ/ݭfw`X{~qWb+ۘT'  8%YiWXRz4b$g~5L!rbHඬs?bŽ:/R^]U;šHs}(-쭥j|=/G/XEooyG V?9-!B{/gbȍҍvfͬ*0t~ zmJ!'}TS^>t>|<ާ/Vw*#d1ոD(qmRܵu3$-,Eg$ӷ;-ޕE1 v.ͧ62/<w?ן߈˷/9|ŹPH+;#⥬p[-Tv 1(ۜ$MJ sUA"ȸ#AhNoij(u# gI0ゎ _ !BH]oFEkUUۢ/i2Џ􈒼yʣ,x싐 9!9ZxyA2?\%X0d?%a 06BStdC;C{;%ŅRx"V}?-iD{x /]"޺]jv)e)aŖ,zr'/ڱ^漌Sg) 3 !B_%uU@cq)1}s?'E XBL5~V{ xx2n_|^oqn,M`cҖqX8^^d0^^ؓ-&qKWyyep't!$5WHWF·Xx<݄ЁCU\/R.V7O8BT`X @[<|IB![#}2hzG!r_EYYJ<يcu0%7M;N&D ]2W%;Mq!oR3(4:ƙ?pݰ3xNF%a\o̹`D_.Q̫-y$i9e`A`- < ;4U[$X[O]NBU[MgnQx"2Y ޏKMORR F;vkYO{=FŴ-V5CW#j4=<5 B!$#ٯ\q~K<^/+]TI lyI zuku}(ت: xZ[28!Kq!>'OƹL͙ёm-Wc6A[l`o3 5c^Jۖ5L8.?S;E$t%KM5 ^U8|k<Z̋s7zK,/"DWJʛex'e5nqN!rT՛wZ(%R:OрCڤ몶6.r^uB.5?|E<$8(*?&.;Gsyd5ʘ' 7*O%G(o24mLk4zYRۖ7ڤ'8m$vb^](K,ٍtNEsY,;KxSqnf%^V@UƓ=[O/$v)#%i%p5-Sœ/f !B ߛ\m;wy;rSEiAhDj~5;IN颒\!^923<uXXfw~wb ЈGӖc^ __;jpIʋ Vt16U[˕G`Ǚ e>eўL)Rp7nB!~ɶ-w۬ucZ٥mFk븆c9Ž9C[#ę2B#{Bt9zn/Ӵ}g=NPi<&b^ ^ F=ei1Ja9WM:01 U<6 8:SF{ń>Zz/G?k'qDu,NOi\ks).Y"^x æSY=xq7 eiQ(E +YWl_r5c=B!ݾr}aՊi)۹b+% 8Ymߑ!5c>/>ͯcsR5P>F$ڜ3ݿډ4b+ eOkwbm( DqӬ$U\+[7w ؅:VNS[5z"p .qyRSPOJJFx: eGUH˗ {b޴STI+> !BJSVm<#s?Ao=^u"qmfxk!$٬ yzك{qXvi}07@4.6[34V1$g)݆ļ^z@W۔g<*0'M[hk MZs\`ffkEWQdČ]v6'x;WL\Uk{JᢩCe~qUT T,i?RU\+BfvB!}l|b]+:/XMw~1Չk99fcvP_U93H9ҵBރуCbSifPS(q93,ٖ!慤l-Ŷb,3oɒb:JڼğkڴRC-= IܾMwޱ_h[4J%%5K} i#yDbV&^/O;Mu\ZD<؛K͵1Ti]jjݕJxp5pqs:_ !BK7vj8 w<,3ഄ1 !fZL!<Ӽr)ȴbm/iE:43[;1Pl\gY#I=@2uaW1ϡ٥ygaLjȰk{(}FYT1F߱PK7E^lVȷ;ZqW:&_ YvK-$ixx6KMZ1I \5O(4(o"eC%#>~0;)6.@O!r_8:-B}#MS*iwJxa~]HK`U y.b0k!ۥوvbf 036.6;Um9&4 Oc)& w|c.>f7ϔ6y<>6IXˏߵgDTx'-DE+Di YI*۫kfAk_|tU $XJR\gF/icSB!䐾㈭ F)ddg}1lR[%UB&}"Yj1Of $Yt}BvfYo[:`㢳} ·n{/]N}Y/5kMZS*_|*iIMjP`8 qwh}rq%Rn똛7Iē"pى'ܸD*U[x]\<)7>UͮqYN ]%f(:XMSkbVuug[xJ =}wx.mܬ7%z9h"ql47wFW~X6ю7]pt^]@ܣObu"Ͳ?).&BE {z)گ~[Jݫܕv/5^Ɗ=zEu[+%m$poKp2z-V`NB!'~$gFj[L6 =VrWxU};#̟QZN$9(M;/n !B}'ߥu^NVUH4JZ#e5E|Rsf \ƛB!g켔w"ƎA9c {B&LF x-A!1{0"BiBRPD Mڲnm1X,hc{-owV@(U<1=Voz$[$%v^PЋl {B\XC,3K';8}6>Ѿ8Y.uײT;$[rxuqك.mW{>;R~Wb^ԪtxrneLl92ѸHe]\ӕ&!B]cy)]Ыi?!/$ IDATiD< =>eIsBCo`߸qGfdԂ_++Yk#׈~ Cݍe%fP̋}< fm>jv(Q*=l~DnWox$>ocEU-}_"\؈ZS{b H6sO"^D61XI C5LnS?IVu˄B!1:o;wv,Uakİ|~1.!TiZBՈ*/@o j5ThMw_9~ RubI;VfkZuf1xԢ]`d͸Kςn6?i\Z7/&eTn:VkNCu> {R=jG6[//:Ae6(mj+'chj M6G>sd5{U4EOa?<;LJU} Vx͟EE<D%Ο?uLal _i@Kܒ&1[[ZWgw~x̰Z,wV `{Mc4R+w`mżZbm=VADzf;k*\?oLЮwVUk:' ⥶_)-V+BIq.5π>F$#)^P)Jzm+>^x\?XB!9q0T\OiΈBNlpNlyf 3pTM=1/>&J7Fq@#X( ;? \^\Ϟ64l\kw[ƍ(6ښyVT ^/'3s$[O2ej,8 G<"ܬ dD5Ԋx}œ@xxO5E5VK1Ku/[S^3.{wE5B!ż"J zjiVꕖ1 !Ǭ'~Mhg >"9}ݴXn2LåqD5%.^?a\Y1Fu~oT<LX pxB!. z 'N:{R#׋5nnfէۅ ^^ӷ= ZīBmSb"ul~`"㇐ODO=!#5vO]7V;iJ-_]B!B&x{$"`hwc^ G9!lo?R3`>s>hR1qX[yAij1Z;K۔UYOP #<9y}KXk 댺1BHKE74.q[ɬP6nՅZ_tϞtIj-[xoQxXvJAl}p3K2|cW5J;L9`enW+Ʀ B!{_{ڢ~l&WI6|Ɵr:libv0ty4{Zp^o?,q&OrG5ӍfRzvox'"beӧxz"^33 eWV4.5ǩFPcvuL!B1sڅv]OD=9Rr̓-xy*-2.!*\"SгGYPqVa/ef3]WdNFE:8r.k+W+'pOγwoSx܁zWc%'H.C=5^*PE;G9YK}ٽ#B!TهIՈDŽ'!11/܁i.=cOL*CY7+#n^OZBsس3}F A/mRZ ўdT*Ed]."B!Ώn4=k`D׳"$̋n"^u*gJxicG2i8`^ b'9 eD us.En- NvE<B!eƸߛJ)BA0嘋BW6:V3D/`JlF 3xxKZוw[JoB!ިg z+ػ(B!1-㖱Ai)cg% 2R;K(:bn5,[#B!$VI)lz`R*~'!cYmϖxyq1/xŬzy<)0yyuQQ%\Nݲ@ y7]wZ4Y/x6aX%^BV+-e"IƗ(oWP xUѻ(mY"eU,vF*1ts BoNq6z҈']?h',>uP·;SB!BJ|7{s}a +׾ŦWkEX:U_,!^I+1Rb0, ڌT+Tm$yv ?;NQnzz1l"*9^.^):^eHRQ@Kl"_'ImG'=|*9-״"*^]I E_ 0b-zW]rGJ!Bh2몉jg5"!,yZ1ke^?F Ee]mYW}FSٶ^7p iAO .QS=xm[drZvu\))}x!V#pgD<!igW;?~%q)^.bv+tAPHh %pMC?(}~c6B!B%5,&(:>B2w>0\[!)%v[Of'2/'A<'W t>Cyѐv&v?pMTreRsFo e/) Ŀo˜%Lfl^+͔iN@hktˋx+|0fDD V}8 :eu mpxi_Oiz~dʎon(n2Y.Z> IDATE; f4txN B*μ}_G!B9|=U ۑQ.!ul3 eyj,9*0b"b^dt3)nr|H(xG^=|,+nڨsf&TRtۥ5ߤxG8xWqYUfT&T[ ^'GN;ˉx1~C"%bV*.^|a ߿&'L 4ThtYDz$B!I:YQ#`]yryʶY@ I|J|1Psmcw?%?zR,`KM@X"bB!BȑshVz?! y:1ϲhK<ye^@<6M+'Lz]1aЂTh0'q9~V^Ln(GGּ[!qF/K\bU/$A">JdOzkE<+>FІu`7L([ŝ'D"\[OtC(-!B!tH2JDSEZ"nNV?:GT4t+A/vKz2ӶO#DJծ@ ozۘz;{H 49$^EUInbYCLjx`Γćo_ hVB==ytq̧uIҊShu~nB!S&*G!G|+m%-|mm*P_~(Ib-(.m<n uW~qqN)~%z3sym|7bxd~uS yG \I.#D!Mw|J:\whE3%4Zb%4WñK源R3s-TuB!rwȲSgצQ_t!vV̋YVD%y[Ae^{ֈYuy yM+7#=`vSĚ6D=uM&?^L\lX5Ɣ*uϡL# "R;>пxϟtE<_*Q ֩=i&=bxb.5CxW7 , !B!(BQ3cż4y 1/P_ 1ozNv ]mJOr4*:47+P>"Wa)]ZF"3l7b3 m#p76J9=4Ο;Rn۔~ ϗP :CSX Q;Kbca`I;i #B!$'0GB=JZD53ku=s$Y'DDAOF_š4iㄭ4+ &.o472MKwβGQVf/}^°Dʼnxgϻd4 K4Yk$$\"vX1!B!{ eD=xr2%?|@O>BbKdn/8,}9/Cv7}]NsRnW@ē[U7$̯[1.ɸE7\?]yB!2 y/ԨBI1OܖyCeN/99,ϭ@"b0sЩyL6eڝbxm:7g[3.Zew+hJt~ xuyTxsJ x@o%Iet {4]i:*&Dݿ""1WX"c2DE<{@ӈxuP$֧#%RwB!B!ycg<70+e^tNTkTҔm<ْ]m2'25~; 'کs씯)W7iR[#%[/‚|^OqOڝ-R\>FE3 xBRQHyvS))+`M'ϻҿ6X %pyB!B!B|xH,G b0̳-\RZ-ƥx1m=B!B!dy7(L,MTݍNRu^kݥqct?VzHQoStWbiƟWW{TܚmMNI1i*BYxçʓ-ǒp"ޣ/J]ڸv:> $=.^>5WόG!B!Bx-"ys<\yUc6<@eOZA~mE]nAX6)_kN}}xvFR;os_뵫^+-ڧ5O'3"yn-$8ac"o±q\n5+_3!B!B9hjךq A1/n7,&,,GA[M2zxN)c2LzK.2Oxi,l&B!B!wy-|o Vt_̫^;ǻtZA$C7شcc^7Nb?Fv*QmPg҅1oĚݟS.~o]zʤˌ,rqV\Vӏ*jӯ] M^1 O]~,<7@Q+%B!B!'ͼ!ѡ\mHeP. AQ#\mn ;wM;oign0kmn{%A/pJlM; @}]@u퇈gtCRTxuS^T%SW('׋ ۽o哀xS xݦv)πo^xK|}"^ %-4"n"7 mmG!B!B3if2Yb*o{Lf[F̳iqNlM;>{ <~gO4 ut}%^t*r~jAv*$y!B!B!ļчbbOڪCo<K0׳ Y']lnib'`c4[ަTcYK#ޙ_kj}1/ddv-ꅄ~.8WMEK[8*grZoIr7Qe,2a'7gWURsD-Z+VIm|xQy3D=?E/I;M\,.h@Kc? xu \\sWG!B!BNOC ZYr^1/<k~vusX~7` ;l8wظՄuwMZ1M}9k7tb4A=#@J<ͭ Bw L v |25(~Wi)ݟwװ^ndd/spXvdK+͖G_R'Ex'n@ D%^ -D1j/j5B!B!R[ļEw3݋7HjyM5/!7b:m`XvMV;3=]hf5I`*`ki45ȚI" k7juci/P>+Ӿy\݄I"Yev DxV^sgrXt4IDs3j4|Z')U%^OX.xxB!B!ٺ Img%yXt]1iEgSۊzi[L *|w})ZNiTř}v,}]"LןWku~Vx>oxE/$w-s4 Q  \^. !B!B9q#~Ċ*sGFPN$hݰN ;4F׫xc_Vq7]S1 @fM>3ۈx2k6T`4TYUm/yvjq}6"9og%VëlW{ {T\?]bbpȤɮښ+ IDATDr}_j񦍅7L 3*,Dw!â.ɰs+ix/_g~xxxB!B!0wmť61 Z,'^A>Ӷs%7v^ b uuFЛof+mcd#`.P \lͦB7**+=!\Z1wWŪ!?@vQήf ,!<1{]s]˩\o>zԍ䕄,ʉ& =/b4>\ !B!B+.)2b^_rյnݐ63 זU^.2;r|0qY[a6ļMjg0"ucD+ˑheYb+=iQ Pb QU)1.9rjTmsqJr`d-$۲\5"wExtP2gxHs*& ŝ'Ǫէx=B!B!BkFryX lnqcmya9k-wc33@nlT[SS9H,?4m2qev6vKHtwBnqIAJ}toȞEM^ ow{I Ҥ `|O׷"4}gsЊx<["AuLХ&ϗOWܭB!B!;K/FKswV:b/yOŗ9Xn)&l-ˣ z۔+LiŴڅ^'^yfjKV;l"[?fjTffXlܷIWRVz_K+.S,Hb/{9Pep||[#Ԗxb}׿or Ks=)ٞ^㦌ޟ@{"*L$7a__9w[O$Ϯ7 !B!Bl J?[6]zg O[K<4"g瀙Cf2o3*`VA` 3fҚAa0uJϗED=G8<;P|dzv9= EQDߓ%2%:4}0H+8)%^ke+ga;w2Baj_QKG!B!BH#}E.l;,W+'c~-bVLo4:@^6ټfͱM{e^S!%ٺWd bUd\vX!Pz3TkBD(xY_UV%RA_a@i;wsXubW xcYEߓXu⦶/~|?JM-≺0pby|j9QZy/\s~ΌB!B!kMQ|b@rӸll&u6]_\y8x1Z繋bxvRI*w+3͛&M3ϴ ߫`+/Ph}S[8gr'E.QYכvyvcnyNcT3Sú~| SUh<9֦XX+tr9ƍ}EZ>}<{/-22ڡ睧wZ=5*A#$yy}]!B!B!=N%.(ah73o[}`$v$Xy ;vϹيs`KZ{^<Qi've)I3ؕJ 6~+:<:Kw6*(y+tg31*]j!c;M,}tDX91 xp x}7(O2ձ׵ZB!B!BȀ@ylA/"󀘫;M+N.Xu- e 9;t\m|=L%J`zb bXmg<tN|\/Ԓ)-ڹʿ^c&䏈ƚjxOrVx߽pY+8M"^5"w3Fof[U t'cG)JHn[viq9MGs.3Vv2ĕn5u?gW]7q gUSE*͚"jFA17riJY9AXB!B!*VRW+y0 6|KiLM JJtzNkljQWn`3]X\zOp*/z7u%8YKfۏDAHG #u&x:Lk<' .Véf1s~SSSkI<[`f]ȖA5|cVj%L^O)2jҀJ}$L ȶq'c7FB!B!BX)/p <]U$nw܍BsC"^=|) ylhԫ 7G 5m:|EqUwM10rP;wr$}}+6z]u-<ޫNOsUZ̰aCēLwu(xŋX?7 B!B!cRkFQD)F).@~Y!a9Cl$Y>ovh'yq'Vui#ֿ3QxvK -PKY6B5F((/;lא 4 K@YNufv1ZR廑nk]Q8Oz?a6H.,+TI+ pskf93ߓ!B!B!dAd^/0*2lOkUH♐PUK#Tu.FFaVR2LU@%6̷ <MqgeKm2IFKfIṼr1/VGq:TerSɦ;-v{Sy7iQjR^Po%&(<$Tmn`Om$TmğOIG"B!B!# aӰsu".yXDtɻ]YSyu@k1'1Kw2ϏkO)/: Yߧh]NNM o;rEaVzy/ŃN^';ȶ;$Ve6PƯH<;뮹u|y0dR)vRRy(?.(!B!BقUT"/y5H<uj B=bMGz1:3L}fx{t5\_/^rW(-}@iZTx1WG5T*P~"ڞmDrs3Rpk"JO Kv)N^t4nTRǺ-3m 5d7VI< 7L.ӼXkJ |0/JԦҌ9%SSpE.GzJ!o4QxB!B!,*>y@p-ڌdNϙ$teJ Zh{ҥlpF9+a+ZmjYiV eЯUq MEiW5 ҍ>| ^\I܍jٜS łʎj W"6Rz |k{/2Qm*͸42ax"5J;~~i)!B!Ba8^*P-󲜊uTAqw:+ "̋(b"ސ :T+ǥ`l:E8P}zP6DP&@F56&lD^fusn%tubL䞿ҧ,Q馅yM_U^àᬨsbdbT:tb˭%^ٻ\ )fIuxt[J.N>w7s|ǤJpϳ M3%!B!B!!+fd^kҴץsSɥ|9gWX1WJ 'zͦT&:m hni6Smn5̃Y?l/@7\=mxRr*sr¤Ԭboy~ijn3+wccl)ܷgT~X+xQʥ,*ґ{-)1SHI8zDR.ߎl={lt7}zԨL@MBm;1[f;CL[7QUދW9~eb&ԶRޘRI ILI5o/DJwϔ;;TҀ6R/Dx}ˠL'F]7Ƣu٥缯=]D5YpHu HdlO8NīBR]_F%)!B!B VbDU1.j3MH RLV̧1%2Ḧ́!̧ENtg b뎸?%'GY>LFvJ(K?D͑TcrryqdѴ/IsJίN;1z尻B&J8ع7gP'{5V\-^i pY@:k1yr}:"uWgZ0cTNL_R<~{]3!B!B!:KKĕn=tBN7R4%!}+_G<S z0 QmL`2:^Z/RǃAU7$^]]|{&uQ~;֖oME(VMD[x<7! F uxv>̙B!B!rXLM'R@/T"Mxt^J\"ࢎĮGU-#z~[%s5ro_w\bߟDiOˏƦƤ,_x4vIHX4`.ݩslZvtʂ%u*ei&$^Ve O&mTeKk'~|1N!B!B+?.+l,:/_Na8^*Mh`>v;wCaeB!B!r1ytR-󺣙R1l)jkՉJKԧKԒz"v^"x}OV~v~ F[2ZW'Qpco6澐i[O*:Fs+j')7l/HJxA-I QVxjY6oЯJ^rZ}%ȗ]`òi>3ߜ%ɷԉP$dUD>ffJ("|} <;RB!B!rSY^%%|$y,)4q=qMy,f=H@'Ԡq:DMWnEe%Ե%S+ś.V{uk^_%n.aV6-+9oIπ{QΓ9>NT'~<0FyC6>^B!B!K7tx霙R9F 0яc< 'ȔdMBs&vW]̯_C9O0-PwjIE qj8H8par(/ڽJnԀOV23ZШFk~5}f$78\#>l%%1n+/SB!B!r j M^W s ڢyJJƊtqaFjҍQz&PG%NZ%^qJ'#禌Kf{{J-#j""DU߿-e&C}I>}|XؚԬ]~|a>}ϦkOtr6/}#B!B!]aruT<%ނt^JgǭzwSr ɤ7_;r"2A$79:/=fUޖBI+w1]Eʹ.wFN̈́;)xg޿HIEg]nf<'#K8\iIOeapP)}/fGH<#B!B!e+f;d$xQ@!pla5.W2zB[*Ed Yw+j^_ 7?~ ]^éyO^?53f.'b.Ll_%w$v\ox[dF[,AJ%0*7jd[J<) 3]K6Ɨ#B!B!¦e2Apy(:td76 f!!)_'ˮb_.}~=W}f uw#q2rˍ?UQx^JMq)>4- 7)u/u]GspER'w^RO\q~|zxB!B!g eS*dDrrNԜ?aT^|)qKPd H=שׂ*`hCor'224f %$1w^ђWHe^$kLJЭ[#v7{0!䝣IɹM˥b}<_N} M'[ FJoSWCxB!B!150QtDYTڷ$Tc {uV秸^Ts)731Y'e{[`V7~VQ^0վ-#F{ѦT.x"V%Ğ̻2f5ZGiܾ0e >O,=KS=O3#JsJ35gzxB!B!ֲ2# eK/E}6ZQaM?y9a < .'V+SQL`\*Ey5ު~]CG)gu7|D+^EHf1W#$}_榢8\lRzw}we7NE*^;ӟY;wf}b$^R_ <;=M V?D&B!B!\ȳǁ١1 J0Rjv} "a1_̹$F0t&I$Ť j RDdPus*df7|YP_2ϗ. щHӡR BSA@FWծ'c!wMCfnOMy<"אVWUqgݚ3_LmDL:@ɰsbw"P07PRbiWS$xu#B!B!gl.(,N}D5 y\wL@cdhsJ_K!`$S $I W)TyCsk 4M~[?D20Rd:d':W{o47Kr`m^Tf+(Ī']Aɱ۴F^3%Fjē-Pw1_| <Mu.3w9!J=&zR,WxqJx{qޭtˊ(e// ʑ{B!B!U_E9*9B~hS&,z %hۉ6۴tN+xQռb}pP/y*5Q Ҏ&C)+DO{ ? L}PX7JT[6)7'}O#W6!^먉4˽)2g[_ p*W0H#Tܕ^4Ο;DKoTv^d{6!5kπ_ !B!B!CLD^_9%杍W/G9X)儕D(-Ac_bj@;I&vxyOٌȼ} ,~t[Qi'^w>U/ҔJ3DE ^+(46mjIT\zޘOf MdM/ei:ubI5yiLOx5'vbj)a U+\d$)igV<*wr=kG|sLw{3ozK1 +^%uwWY[_mL:J7zr>r4 /ק-k)G!B!Bc5 @\(4k1_~_~|/O V0)MtlzF{3ŎmE;/e2sN.-2$rB$x.jH;t"Tޢcƫ>N⹲{]RW{ϓy~hH4Zo =_q1`=;[] =E; 3`$f s%T|']z*gN7O/ZxB!B!1lD^"o\d<|1><|x;5~<_:OcXq4}ttؠtT$2}C۴xV`^.J,ųjwcSuD+^֢xͺCw3mۛeݙUHA\lY퀽Em4d}ݐowoKIOZd>K|4f"c|z<=#B!B!T "PpB 6bL5 |1Z-0^V;xg8PQXoV+ΓއIjZHit-k'1^AOypή5}-FV5M';y?ihLQFB)MM~DoHAk%PDH<-&պۊ/:<%MklL4{E:M٘?iMN%0(@ҘIRzyH-9=Yr 7/F-%nv"/7fq.ztn5CL2JmZ?9[4!B!B!L YT$4w?IXgO8_5pds'ۖ _?j$ޮ^?“X#O_?>B!B! K Ӏb*jW[dxԔR]vNR {lA,u܋\OyayBO`SY$hXcw IDATWwj8S ԁI<+n@zQXo4h52O4 Vm[zN.-|WRWjwuB bNK/wvvdtJ!_ o^yz7"{7yb#ZyM/䠁Z5'{܉;y!zux܋Bj N'P䉶;<$K.ݣ(䍗o=)f97Ew[d6;+vr:jq]Н֟xpo8œ#uFnS;rm%OsB!B!BȒg:ׄǜ%h#~'X5F(hQn{;x/^FپEwmt):TU\g׺NU< 4g4?o8]jƤռ3ȧW8[CM+;Y>dRx)Ux&-lxn^n(3^nU-iM4`lXovut)gwgS¬$&t<<>/LzM<ٔb#\bEBFeSk͔)I` i63{+6mՠUA(7D_N%[7HS0|_֗ej<.ƚr#9kCd/p!inԴ3gLwV͌]K4TLuk$edт~xkJ|B!B!B^0y)׌a$\ٯZ@۟7 ÷y/?۳&FPOdkyi5sHQfby8Kcjp`G.Q %tg. oߺMPUbߦ;\s4 y.kSq Jd^sݴ]+y= q dVOxcxc{.a'OG!B!B+J}?S?ţjI៮.+uWxJT47dl>ʵW7/q%>!4@ _?Ή[եTᵦ:` :yz _6j`B\Pj{`(^TSfRP*Dkٛ/բfew7NmZ#"Ze -]GM4ޔq#GD߅d469<!B!B!{gDlȏkaex2|}be/K(xP[JTq+Rc#g:QqD9OTIQ:az. RoARx{ݽU%Yct^}UWHob< 45+gQ:A7_KVRܥܲ6ߣ'A:}Kdj%*h @!B!BljMF2!'ԗmK/NEEQ$P0rBE&ę ;D`U;ޜ\S>t釐/uD!ʝQVanK?hP~{tuyghmk/3(P[&_S5Wěpy~*K榯B2W\nC(\QvPɼ̾2fjJI͉AVc=.W/O2TQzFM}!Rd)>](!pK}DݔO>Mm|rQFjFh[#62OkmkSh[.za%ӛ 8F]& QY<@寖+tdI-= ˗Iikkh_LB!B!BBVɣ%uj$OPl(s\#H7`R2ϟrVFƯ;YϳҬi-֝:+?=pki[!F'J7'[+/H ͟A=h[Vڄ5tJ4hH?K) U};ޜDۜRfx9bW;S;8PlzK`G/*"ۇ+63vt u@!B!B"o^oR55OKo}| 8BS6ŦR^w?^E r- FQN芹,$r+*]l.?wC'0$7'SJs*Ʉ28^!\_O_B!B!k0"oLmxPC{|wD!a1/]ʔ, d_jiz~B=&&a]WΦ_|缤y$7gPҺVldIEiD.wE_k`V䣔$e6p/rn*kw6e{\ԟ){͟mgC9~<,sj^VI3MQ9B!B!BdĚ,N5jٵ[Um{I;r@ʥ n7a>dH N)>;!q́9R-Ot}dOޞ4~=<[EA4KD{O14PZokVf; ɻxį6vv[mḫs|L72j xzh" !B!B!a# 2/ޥ.R;hcy_*3G0儭JˬL~l?/!ۄj/A`\2ghM.'崍"zlm<(y~|uoZ aݤ?(ףSB!B!BRx5yA 7gO L%D)(x[H:ɫz0JKp }|\l_ p!r>(s;"\;ДN)qD :8E:5uk':)nj||#/uxήēydGSTmol;6jI%66pR_B!B!BȵfUݲV?'e\tl.L3k} /#HD?Jd<%M@rt O!׶.SOSjxӝSN4*IŞ[9pr\ QWWpr]}9 n7I1W(۷=N5!DY[-ZdOW׏[Ox-ޔKF@!B!BD"o^^Eڰ h4, T̚ʧݒYd0*j57* "~ >9( rvv2FL^MO K폧{C[Kޗ(%rWr.6nwҵ,Wgwca⻑w.Xt%W)2co$#x_oπ_!B!B!FIs%EB钼Ƴe1REefD *S~x"jTsy@lhr3M ԟ ҥtۗ⤞H*KwH@ooZ÷^v~Wf( S-?tPXf~JΠlC%y|<gns8<_|B!B!BȍP#oDpgʏBA"`jbT}ZY3U;T% EEw~)N)Z#^j' 2QxbƙK@xxhuEMUʽ.RWPhJ1(2a7ۻG7̨)WGSkO!B!Bʧ 2/t?Nc215~撊ROB棔'Z]ĝԚP ޾~6Qx"]8NUD>F)M{FtJg沌TWUN $Rh.7 cW*dR37272$K\O/XB!B!rYUXi|S z9u%xN͒yщd\5 OFyR"1N6NLHTY,/"}a> >Nc(:WYn y_h]F"4!&s{|$=v~s^)r'C#B!B!xLD^QeNDcqM<=%؟I8:oW2[O?Py&E k}תsS~*`f_JJ`O؛'O8>OZ):}RF)7GDԍqE"ƺe% pJݾB!B!wU]rF{րP)I)|X$"IKA'eĽ%B!B!B)z72S/ϏjT(mOVBEzAi<rՊ"ʼĩNʼ^0L;&]R2ӏl^RGة+q(<E؍bxr*mĞS ɲң*Emew2r4nm :npM5jowoGAq2}2u(l`<[ !B!B!aDK'I>2k񹦢&Fߍp~ B!B!BI5kdjB'<;zEE2OlHo2.(P5#i7yZn}6KWKoL襢^Mr ""YTj=^5%&=I|R!?v.a#}¿g|(?#ʅ񪆪o>giqB=_(!B!B!,*<#T7t"u >_)b^#F޹<֯SQF ev5d$v^K5?Ra_2ם^ƅ/[OՉJk`Kr/iAZL{]li.nr7;?֏̝w"NFZI*B!B!BiLD;GA2o{u,AوYNf9'|]ND6E ΕyRZh+E!KZt"ZqRov|\DJ4n_0 [ZM2l)f+5u~E5yҏ@Kj#0kXOm>.&y9QxB!B!γ*U dM εk;0"ĻFs5qyK)2Yݼ\WfɆY'/.^{#i;w⿶|#3Eu{g0P%ۺB[ˎw?~Q}}W8(KW}XB!B!BC/Ƅxʫ[P1?xxxF)Ze~V\kz݋)2Y2Fť0J/aP 2VaOJbOTEn.C2s:_N[`%K&^M[]WM.~~)pKU +SϒFnDŽy{!B!B!aD^6/T/v2ؤ{o 8jy}9 D)WVi3KiiT8}<:f>dF&L%IPLk=X=?|hw3rB#fВEVǀָf#D[];˕-B!B!B35Ǘa*L vH1pt EHy63}SoFKk@x^fZ4Ȍ7[*7&k;^EIl6f'ۭiu'9R-2?+(s_x6M+G!B!B!!@~9|Mir`W6=0/=:Ċ:7NQ&C;7ď/a("r+6f(H=ǰꄗ~D[Uui%ܛ0yf9Y{t~JU_{aކ lm-ﶚxzzxB!B!Ut"u^:0)5:xNu5|!p.pDȸ"mݚ';x`i#152fF /'$"j^aqJK,oTyr/5$"xUC1ueދ`+IYMěUd--xv !B!B!ȋTX sT(~ ܹee`6s> }N/L]Ew`j)]%D4 iGuk.'uj.1ꕪ8bu3G拃L/>ـ%9_ߓ,rAMkxm2>R?7&&RZj;%2B!B!B% ĝa_.&ºwNR5^ s5uJ:O@MS P-РxNjmkIFS" }G ;(hrbz<pG{qrj|QꒉD]\?';xfjv(R P͒wZ9B!B!BH0Du^yk>"T3Nw;+1pvX(@Z࠱oғ)o%KPQ?2&L:OM+`7uD7{9۴uGn0-gIe?EvΚdV_}zxB!B!`U픜SNYS?6眨exĝ܏oy/>EOyLM'~œwrЬWjDQ~̈́]8:myb}>7 Q{ i luJ*GU$ Ҋb7X -\X|p/”AGB!B!B?Ir&uJEz)^W1ֿ\jMv:'4\R7+nys DK"Mx^<+ȥ͐{2~?oʨS%^v;N7Ci7v>YIWzh&."B!B!Rb5.UILƫ;ON %L[g e_|}oQϵay^_Kw>]o{ y3):69TI`z7;īv }dNf:1L$C`݂B!B!B86"/%ldW +|djڹSjy8}a秿L<_v%> |g鴜Hʾl֘c*SĻ'}DsV[6 3135íefĻbi2A3aL=_މgB!B!BEZӽ@:Q?xa$qN{sW{Eрק};W+O?9zyj7eZm Nτh9ٲ]a6aw{mMF8eֺ]G[jV*} ܿ;?dWf1!B!B!Lb.>yܟT Դ "2 a"|v :ۋ̙ډ=& E''f%qPTw=G%L[V*|507!\QZUq<ޝWQ啗֗B!B!BVC^=.?0i(/Nٔbv}u8Y8_9YFqH2#ZO/yFەڜ9_xy<.&qw|c3Kw?&@T}auŻؚi1uQzj>O>Q/nK!B!B!W˪xl̋|O \9lnʡ]b}Lx]~89@.JSYɚiwNtu' 54KbE޴/|0$WܣVz6o/{Ϥ[j|OR^RisT=4]Y@q`Ȼًxod0szx_y5u.<y'>J?N ˤI,1}(>kx{ZRLü:3 LX.at(2Y)üJ@I> xayie$6 Bx1̋}Ε[xQ Sw{FѐNt%[Tn!l5n#dj@g-+4oɊ\V|#?)vv 6x.SMCEOLTEM[ bwiʯ:ru4Z󼽗c6@܊ldqw);98d"=nCӇtpfK$aY|_=JI_IoNT؅&U!I@:-j7]>Ø k+Aa+JIw5բzj`$~}0|nuq]9ʹt$!1w q[ٵ^7[ޯu.liC7}>y ?fHLF:y0k5mi$q.E޹Pux6‹804HZTǹtիw>;|mW=U 0Mg'aR!_Z7t["oagy5tN+_I2y8&9#]5buWkC_li0A^t4.xMeaVWխReuNP>ib'4۽/g۬GҾ kMϝJv_K؁&ab`&28gRoׄ:~<@.اM[\.igmv9ajͺ-'M&mZ/p\l'ۓfnnp؇x1)`Gk$0SVTeII?޽p_So8iv)zTLLeaMuKxacz=5η} |U'^:f=<رÙ-۴tƷ״q{.;S/ҟU[f*+t^ ϘrNW=VRP6pi^̣`/Tg0y$x҇n۽/zҗGoHIENDB`distributor_logo.png000066400000000000000000000356701437064675400354300ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/debian_template/boot/grub/themes/debianPNG  IHDR 4r\xWzTXtRaw profile type exifxڭWQ({D {1;v퇱.?>_8.jǨ8wc>Vs}߭ ў.n>>6$xX_{|w ׋0_ӪMگ {a}bwWF&ܸJLxp̷#LޙTɪd+_;&<22=~wNZ9gf7=l't\NO8@vF]8y#rZof;m?h;mR܉i3VrZ~vH9X^F+}Ҟq.alI ڕB@\ɷvl/]"ܽi#y?+Ay`6Lp uuь,; ؂dPXhU5hg"wZP"Kݱ񍶧/ Y Bx0 MpalЏvtb2>dT֮CUM]ufܦ3Q|t"\x%k,]|̀| +1>#5-=nR}[m@jO?rcb8:k=Lݎ>UhNrƨ70.gZT.uӄ\r]`ߍ/7Joa\ ʥO#oa-j- jeaΔ{Dꩴ0KGoA(8s@? +rdMX*O=ǀVz_M65`n'TCF'&JQmFhD?}j+Ҍ&i^Y w;jrq;3r@21,e\!Խi,ꁣ9tw()z4>w*` c5 @iuYTl70<+bvr A2aQ[wIK_i(* Q}|4d ~C3 ,u;}|vcʃW@MGxWACrKȉ,$e[kA ,յaM>%%]H11O4<Յ( ;?#Z[~veBIC$ljf SCY Ph5:3W(-F)r:ݥiOM/Oֆa@(E;;1@\5T,pm[ `<;+@ om 5_& B{\(Qiv۩.#0-W9t>C#Z0%mn% 4F@mXPWU' X_-?02lus{^ 3\tFiCCPICC profilex}=H@_S;(dNDE EjVL.& IZpc⬫ ~89:)HK -b<8ǻ{wP/346SɮW eIRx?Ggaӛy8ŠJ|NF.Ш94N3p:0IzEm⺥){0dȦJAB>7e[{뭹 M]%oC`@>j3~axrr'iTXtXML:com.adobe.xmp 3gbKGDo pHYs  tIME d IDATxwTW6zޕJTbDD55jkM4ccb,vP,T }[?]ٕ"&sNٹ3{{{9A!B!N!B!B!B B!B!B!r|B! @ȫ0 |LG'B!/st02nl\CCCdeeC o_ 8z$O1xH $t&G` ! ~H'B!d  ooOhii)+**!Hqq\/X椴k'tňCC$&6h{`waaaCC\v@Qph QB!B-!]0o,vxggG BXx9ܢ']*|VGakkZv&-4:hIB!2%\o׉|rd2D"TWWC$A&S>;z#*j IH^nKWg*ߧ- wմrdCG|B!- e-J8w.ǏBiY_km ??OkN:];53>^IIJLT*eRE$"$d,|fg-cR7! {B!s=||3+#[+֭fma1. ?!_|(qss/Vbu3*sr}RoeA&5@U#h<܄|B! u _ >ΟODwG*[aРq%FWғ"G*/ږcDȣ{B!F)^WVVǕpLS`~=Qa_WҲJ@ENFEEU8c=$A!Bd>Q)r$1}\&LfF &NyyʮN<cƌ~ B!ר"!V ovcʔPjK~: \.o7kZw*oǬuoܸS,;](((¥>||ގMe\}}mmI]o(BJ=%5ѿwL4S|\!ݽ5㭷:@GGO]ĥK)8s }"U0ޘHa8y^ OO<~ kw^C>NnnnI'B!on"QXXɓǁ㢰| VHL * *|066VD\Y {ѳgWhh&Oql"bbaJ8 쇱>@X|9vu(/) 1;t$[AWiSS#,[##m zpskܡx?C\\<ݏ#" ;tt `dd__oX --<:#$dzxkZ.^5k6'Y7KNQЪU+@qqlh0l~r:By3޽1ehC&c͈}45yXc( Caa!***QRR}{cJ1Ο߃bѹ7NPe|GXX I7SLtծ z[aΜdGff6k}-[ڲ\CC_%x<.}:,v qD(wu5n}_(_MRRn{EFN"!B^+jӆ) !!1nYpppap*/+ fBebµMVCCQ{6m[gFX,ALa={Z1`66 Bπnr?,ؘa%eV% < Ay3pH$)Ά]}о{ꦭ3c_it}; bZZѵNɩW 9rnFee \ZcAhʀm5nQ:vbYZZ RGAAK:9N}}>}/r9""1ӳ"{X=TvC雍B!gOp\H$Xٳ籂D"vNPXDOOO5ggVFa%HG34444|Dn[cV2D G8Eپ؋8~n$ {{[E-ڷK*߯;ew{|lwf899 k~!TZy9~FJK6ybb"V֒AYPȾNZZ!_OeoddGGF\ Dn݅If9? > r:qqgLr#,lʀ¸Yn-["#**Zt9N &N T:bqn!z E?FzaTT^Cy9Rn@&OE3jD<w{7}e66MQǟK B&OE-ZC&OTv 6emaOB&Oŭ}mL!PHT \  |p\WP%++ys$˫U$蹹 ÇaP;,nff6#xrTLVmBlEFyrcL"}|:6"4ts2r?U*O"KtQ{;hp8drp4@r^?mڴ&1ixDDW팍p`aa3s-\H؎Yf޾ 4n† ߳޳6)Du gk ]]Ws&iiXnڮb>=b9|EU/Apv6.g_з1!B[m@nn^744@C\f/bCQ+#H2eؾ=twwD6ֆ,ܹvcΎ,KPX~[!0#kmv<\LT_sܸnqtlRo7SPP{޼{xxC&a߾8>p2A]^^>mێ:x^刍݌N|qI53f|{K0e X1 Q7yO{#GDEUiPP¼wBWiX[[a ߿;`Ӧ_k B j5l^X zֽ-ڷo(t99!˛\.]Xex|>﹁uaa1 JԮ_X]@P?ɽ{yooe7jQFzD"՟4F@/Wطo?7&cg1h2~zvpp_glAa׮l ?o?vQ TW<|>oC,r##ȳF+-UŋV)a+SppY|6w}B!!l$ޣGѺ\.>aݺHEY`;O ص+MJEUՒ&fU]PLgg61<p@=22>/X,G듙NNׂ3ld2̟,IB! `` ӫIwrrĉq1}cC+GÔunFFFծHey֊WNN9>QFSŴi`hhǏs23&B(ΟO”)1ir_o> <1tX#j9,ZQw+Wnm x***_YX^:w媟 N(,D~ZǢc<@幪eii"nIuYojKKZa>k)/BkT§ /^GNC̝; NFe޽l.oJnm'>+AÚ ЩSGFWjspz*NR&ls|^MVaժ&OC.^LŽߙC*'ܽ;FöTSq\33e+@RRτZ#Gc KW?_[weœ}նNԫ6%B(!q\UK9h&{1thBvvMR-z)S&٘?/ wi~^xS#<| **,%$$717jN܏|Wشiәޘz˯?2++]mW"\ÇϩwjKKKU>WUU!{ɀ^przXuB!x8y"씣ݹV'Vgff6*CPJ?|˗EB Ϸ7 Voێ%!y6ڴi `̩}. kkKhkk;|/{*(p8\x\H& ǎcZB>yRШ,ʪCl!,XQ|x=[#"00z(..իЫWO\|1GF-SSCv8[S5eee* ;Ч˿_ܺr?B5ҥEٳgʕw/ #F C۶n@[ ! @ϯݻiiܹ$Dmd29/ڌA]ر#l44C@rb$^o_k@WWI|(y< Ѫ+h6NduGRX|Vd͑*J0||FYį ruv%zlsb G``uA;3#hhhر*[m(--CNNy@&3_zYZg/Dyy%mmmL! BP>|Ӈ`{رSovb/aX'ğK@PDD51/%GI0P0Ԍ.~Ǐp803{eMGGuJ.##C Kkgg >df>xn[v6/v*nUw8gg2kеJ^uSgwX,Gd&!|vꖪW/2 ?gϤG8wG oo/ߓ ! @o(/ҥSOO//ᣏV`HJ|<|8UmڨV5ya]&{)cc(dEly6c \ʽ{w{cnGK;aDTV]С= 1r591#lmu?aggHźu`YAbbsN6@59$Gx\.ӦMoaB!wN)Grvvf˯@RbބZ]vP2}ӂ7ѣ\\BmFF*7m}}=rB&rc*n_4/e1fDFF&֭ۀǠ}g0f;wV<AŶ;)y~fe147ŪUk TǟDŽAee%d2IRlס044k3mGŤu[+j&'dOB Kh{Rzϭ[C$㏧71!Mޔ ׬3BpeL䵭_dwaopeTUa`iFg~P醇k~E>qr+zd0;vm$n\Q{<[1.]JG0QX+"L ㏍_ #>te+.(F0չ`\ 33H15ŴptQƘ8q8>|<;wvk?^5--x<2y*uII)BC`[F]Ç޶ʉ넨sb[#- 96r!S]7j&6,}ׯ]p..Nزe5^ሏ'LSss3g.g|>&LxvmDH0[ t0n\0 =i\N[|"*G*+?3_ȟ[3Q^ X]]kfۊQ? MM =Ԡ㴴4i@N(,U;xmAYYvkwHJ/\B!B3MݰZ3c׮-4!#}2RSvp.V#,lb΂Z&&F011,==vv֬luoWn<_EaIT>x<9ceXiiِHO6/BWGCzk?ς\^gbc F T*DZ#X9,ӧ¾ZVF\.Mannخ15`bNSqm<"\.ޡUBvpssű/'>1`@?xzzDe a$!zO} B!:{[V.~X9g]C$ (G#>me;,zz(,>;졼IDATU.HPVVGgk|_UU5~u-\ v.:+_r# vMb&y<Mt*FȹN*ՁiqX KK zTycba„oO!^xMbႯQVV>}n;_J$R1fUSǁq*w"|4'"5.J ןRSPP't@nnE CEE%`2̞Baݠn]tu̥O@EE+jv&cc#tLp88Ȯ 3rannk|Ctt ! !J!cŝFHL>B!-;YnDbk8p`;Ý;UeKJ1dp(Ǝ__88O#%믳03k-DU5 ::3ʣС2+<=aei/owd?DVV63qv6Μw236B{[0j/-bc ;y3 k2d5*蠤ZZ(/?ݯNCȔC dgelGE("O䮺Jjԩx\t p8ЁL&GqO*a_P]]\mmdg6߼ϚB!>< *~ocС7o!<7:xt !B!@_c`aakג1rTdf&B! @j `x |7n$8OIDAThA 6j `I% ?0 ?QɏIENDB`menu_bkg_se.png000066400000000000000000000004651437064675400343060ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/debian_template/boot/grub/themes/debianPNG  IHDR sRGBbKGD pHYs  tIME 'tEXtCommentCreated with GIMPWIDATu!0'J!Ee&&%f~I~H~`ONА†8R&v5hUJp"ڶwP3˜ #,sE JkX|2@nF`s<7IENDB`menu_bkg_sw.png000066400000000000000000000005041437064675400343220ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/debian_template/boot/grub/themes/debianPNG  IHDR sRGBbKGD pHYs  tIME +PtEXtCommentCreated with GIMPWIDAT}!NF_0Kf&j1&=Ò&;4+ @ 5C ` %'>i:!+ypP?,jVL~4M*v{|B۶braǟᅥaNqv y # Adapted again for Debian by Jonathan Carter # Global Property title-text: "" title-font: "Cantarell 24" title-color: "#333333" message-font: "Fixed Regular 10" message-color: "#333333" message-bg-color: "#f2f2f2" desktop-image: "background.png" desktop-color: "#101010" terminal-box: "term_bkg_*.png" terminal-font: "Fixed Regular 10" # Distributor logo + image { top = 13% left = 11% width = 320 height = 26 file = "distributor_logo.png" } # Show the boot menu + boot_menu { left = 10% width = 80% top = 20% height = 45% item_font = "Cantarell 24" selected_item_font = "Cantarell 24" item_color = "#656668" selected_item_color = "#fdfeff" item_height = 30 item_padding = 0 item_spacing = 0 icon_width = 18 icon_height = 18 item_icon_space = 10 selected_item_pixmap_style= "select_bkg_*.png" menu_pixmap_style = "menu_bkg_*.png" } #progress bar + progress_bar { id = "__timeout__" left = 10% width = 80% top = 71% height = 20 font = "DejaVu Sans Regular 12" text_color = "#fff" fg_color = "#2a2871" bg_color = "#120f3a" border_color = "#120f3a" text = "@TIMEOUT_NOTIFICATION_LONG@" selected_item_pixmap_style= "select_bkg_*.png" menu_pixmap_style = "menu_bkg_*.png" } + label { top = 100%-140 left = 10% width = 70% height = 40 text = "Use the arrow keys on your keyboard and Enter to select an option." align = "center" color = "#fff" } distribution-management-modules-0.1.1/examples/debian_template/boot/grub/x86_64-efi/000077500000000000000000000000001437064675400303645ustar00rootroot00000000000000distribution-management-modules-0.1.1/examples/debian_template/boot/grub/x86_64-efi/grub.cfg000066400000000000000000000003271437064675400320060ustar00rootroot00000000000000insmod part_acorn insmod part_amiga insmod part_apple insmod part_bsd insmod part_dfly insmod part_dvh insmod part_gpt insmod part_msdos insmod part_plan insmod part_sun insmod part_sunpc source /boot/grub/grub.cfg distribution-management-modules-0.1.1/examples/vm.dmm000077500000000000000000000064351437064675400230020ustar00rootroot00000000000000#!/usr/bin/env dmm-perform-recipe module_path: - local global_settings: chroot: &chroot /tmp/disk diskimg: &diskimg /tmp/disk.img loopdev: &loopdev loop7 apt_depends: util-linux adduser parted mkfs debootstrap recipe: create_disk: module: fallocate destination: *diskimg size: "10G" create_partitions: module: parted destination: *diskimg static-layout: - mklabel gpt - "mkpart primary 1M 50MB" - set 1 esp on - "mkpart primary 50MB 100%" loop_disk: module: losetup function: setup diskimg: *diskimg mountpoint: *chroot loopdev: *loopdev format_partitions: module: mkfs partitions: - esp: fstype: vfat partition: /dev/loop7p1 options: '' - rootfs: fstype: ext4 partition: /dev/loop7p2 options: -b 4096 mount_partitions: module: mountfs partitions: - rootfs: source: /dev/loop7p2 mountpoint: *chroot mountopts: -o noatime,delalloc,barrier=0 fstype: ext4 - esp: source: /dev/loop7p1 mountpoint: /tmp/disk/boot/efi mountopts: '' fstype: vfat install_debian: module: debootstrap destination: /tmp/disk release: testing mirror: http://deb.debian.org/debian debootstrapopts: '' setup_network: module: networking chroot: *chroot hostname: buildvm add_users: module: users users: - username: buildvm password: buildvm sudo: True apt_setup: module: aptsetup chroot: *chroot sources-list: | deb http://deb.debian.org/debian unstable main sources-file: /etc/apt/sources.list update-sources: True install_linux: module: linux action: run chroot: *chroot package: linux-image-amd64 setup_bind_mounts: module: mountfs partitions: - dev: source: /dev mountpoint: /tmp/disk/dev mountopts: '' fstype: devtmpfs - proc: source: /proc mountpoint: /tmp/disk/proc mountopts: '' fstype: proc - sys: source: /sys mountpoint: /tmp/disk/sys mountopts: '' fstype: sysfs setup_grub: module: grub action: install chroot: *chroot platform: efi setup_virtual_machine: module: vmsetup root-during-build: loop7p2 root-dev-in-vm: vda2 chroot: *chroot unmount_filesystems: module: umountfs mounts: - proc: mountpoint: /tmp/disk/proc - sys: mountpoint: /tmp/disk/sys - dev: mountpoint: /tmp/disk/dev - efi: mountpoint: /tmp/disk/boot/efi - rootfs: mountpoint: *chroot loop_disk_unmount: module: losetup function: unmount loopdev: *loopdev distribution-management-modules-0.1.1/setup.py000066400000000000000000000012631437064675400215440ustar00rootroot00000000000000import setuptools with open("README.md", "r") as fh: long_description = fh.read() setuptools.setup( name="dmm-highvoltage", version="0.0.2", author="Jonathan Carter", author_email="jcc@debian.org", description="Distribution management modules", long_description=long_description, long_description_content_type="text/markdown", url="https://salsa.debian.org/highvoltage/dmm", packages=setuptools.find_packages(), include_package_data=True, classifiers=[ "Programming Language :: Python :: 3", "License :: OSI Approved :: ISC License (ISC)", "Operating System :: POSIX :: Linux", ], python_requires='>=3.6', ) distribution-management-modules-0.1.1/tests/000077500000000000000000000000001437064675400211725ustar00rootroot00000000000000distribution-management-modules-0.1.1/tests/test_dd.py000077500000000000000000000004451437064675400232000ustar00rootroot00000000000000#!/usr/bin/python3 from dmm.modules.dd import dd import os def test_dd_create_file(): dd.create_file("/dev/zero", "/tmp/test.img", "2M", "2", 2) file_stats = os.stat("/tmp/test.img") # 2M * 2 should equal 4M assert file_stats.st_size == 4194304 os.remove("/tmp/test.img") distribution-management-modules-0.1.1/tests/test_fallocate.py000077500000000000000000000004431437064675400245410ustar00rootroot00000000000000#!/usr/bin/python3 from dmm.modules.fallocate import fallocate import os def test_fallocate_create_file(): fallocate.create_file("4M", "/tmp/test.img") file_stats = os.stat("/tmp/test.img") # 4M is 4194304 assert file_stats.st_size == 4194304 os.remove("/tmp/test.img") distribution-management-modules-0.1.1/utils/000077500000000000000000000000001437064675400211705ustar00rootroot00000000000000distribution-management-modules-0.1.1/utils/dmm-perform-recipe000077500000000000000000000010411437064675400246040ustar00rootroot00000000000000#!/usr/bin/python3 """ Perform a recipe using DMM. """ from loguru import logger logger.info("Initialized logging") import dmm.dmm as dmm import os import sys logger.add(sys.stderr, format="{time} {level} {message}", filter="perform_recipe", level="INFO") if len(sys.argv) < 2: logger.error("E: You need to specify the full path to the recipe") sys.exit(4041) else: if os.path.isfile(sys.argv[1]): dmm.perform_recipe(sys.argv[1]) else: print("E: The specified file could not be found.") sys.exit(404)