pax_global_header00006660000000000000000000000064122613263460014517gustar00rootroot0000000000000052 comment=4f0eb9504cc786d5a57a43814427e8eb35407a4c vagrant-1.4.3/000077500000000000000000000000001226132634600131665ustar00rootroot00000000000000vagrant-1.4.3/.gitignore000066400000000000000000000007721226132634600151640ustar00rootroot00000000000000# OS-specific .DS_Store # Vagrant stuff acceptance_config.yml boxes/* /Vagrantfile /.vagrant /vagrant-spec.config.rb # Bundler/Rubygems *.gem .bundle pkg/* tags /Gemfile.lock test/tmp/ # Documentation _site/* .yardoc/ doc/ # Python *.pyc # Rubinius *.rbc # IDE junk .idea/* *.iml # Ruby Managers .rbenv .ruby-gemset .ruby-version .rvmrc # Website: docs website/docs/.sass-cache website/docs/build website/docs/Rakefile # Website: www website/www/.sass-cache website/www/build website/www/Rakefile vagrant-1.4.3/.travis.yml000066400000000000000000000002101226132634600152700ustar00rootroot00000000000000language: ruby before_install: - sudo apt-get update -qq - sudo apt-get install -qq -y bsdtar rvm: - 2.0.0 script: rake test:unit vagrant-1.4.3/.vimrc000066400000000000000000000001221226132634600143020ustar00rootroot00000000000000" tabstop settings set tabstop=2 set softtabstop=2 set shiftwidth=2 set expandtab vagrant-1.4.3/.yardopts000066400000000000000000000000141226132634600150270ustar00rootroot00000000000000-m markdown vagrant-1.4.3/CHANGELOG.md000066400000000000000000002360521226132634600150070ustar00rootroot00000000000000## 1.4.3 (January 2, 2014) BUG FIXES: - providers/virtualbox: `vagrant package` works properly again. [GH-2739] ## 1.4.2 (December 31, 2013) IMPROVEMENTS: - guests/linux: emit upstart event when NFS folders are mounted. [GH-2705] - provisioners/chef-solo: Encrypted data bag secret is removed from the machine after provisioning. [GH-2712] BUG FIXES: - core: Ctrl-C no longer raises "trap context" exception. - core: The version for `Vagrant.configure` can now be an int. [GH-2689] - core: `Vagrant.has_plugin?` tries to use plugin's gem name before registered plugin name [GH-2617] - core: Fix exception if an EOFError was somehow raised by Ruby while checking a box checksum. [GH-2716] - core: Better error message if your plugin state file becomes corrupt somehow. [GH-2694] - core: Box add will fail early if the box already exists. [GH-2621] - hosts/bsd: Only run `nfsd checkexports` if there is an exports file. [GH-2714] - commands/plugin: Fix exception that could happen rarely when installing a plugin. - providers/virtualbox: Error when packaging if the package already exists _before_ the export is done. [GH-2380] - providers/virtualbox: NFS with static IP works even if VirtualBox guest additions aren't installed (regression). [GH-2674] - synced\_folders/nfs: sudo will only ask for password one at a time when using a parallel provider [GH-2680] ## 1.4.1 (December 18, 2013) IMPROVEMENTS: - hosts/bsd: check NFS exports file for issues prior to exporting - provisioners/ansible: Add ability to use Ansible groups in generated inventory [GH-2606] - provisioners/docker: Add support for using the provisioner with RedHat based guests [GH-2649] - provisioners/docker: Remove "Docker" prefix from Client and Installer classes [GH-2641] BUG FIXES: - core: box removal of a V1 box works - core: `vagrant ssh -c` commands are now executed in the context of a login shell (regression). [GH-2636] - core: specifying `-t` or `-T` to `vagrant ssh -c` as extra args will properly enable/disable a TTY for OpenSSH. [GH-2618] - commands/init: Error if can't write Vagrantfile to directory. [GH-2660] - guests/debian: fix `use_dhcp_assigned_default_route` to work properly. [GH-2648] - guests/debian,ubuntu: fix change\_host\_name for FQDNs with trailing dots [GH-2610] - guests/freebsd: configuring networks in the guest works properly [GH-2620] - guests/redhat: fix configure networks bringing down interfaces that don't exist. [GH-2614] - providers/virtualbox: Don't override NFS exports for all VMs when coming up. [GH-2645] - provisioners/ansible: Array arguments work for raw options [GH-2667] - provisioners/chef-client: Fix node/client deletion when node\_name is not set. [GH-2345] - provisioners/chef-solo: Force remove files to avoid cases where a prompt would be shown to users. [GH-2669] - provisioners/puppet: Don't prepend default module path for Puppet in case Puppet is managing its own paths. [GH-2677] ## 1.4.0 (December 9, 2013) FEATURES: - New provisioner: Docker. Install Docker, pull containers, and run containers easier than ever. - Machine readable output. Vagrant now has machine-friendly output by using the `--machine-readable` flag. - New plugin type: synced folder implementation. This allows new ways of syncing folders to be added as plugins to Vagrant. - The `Vagrant.require_version` function can be used at the top of a Vagrantfile to enforce a minimum/maximum Vagrant version. - Adding boxes via `vagrant box add` and the Vagrantfile both support providing checksums of the box files. - The `--debug` flag can be specified on any command now to get debug-level log output to ease reporting bugs. - You can now specify a memory using `vb.memory` setting with VirtualBox. - Plugin developers can now hook into `environment_plugins_loaded`, which is executed after plugins are loaded but before Vagrantfiles are parsed. - VirtualBox internal networks are now supported. [GH-2020] IMPROVEMENTS: - core: Support resumable downloads [GH-57] - core: owner/group of shared folders can be specified by integers. [GH-2390] - core: the VAGRANT\_NO\_COLOR environmental variable may be used to enable `--no-color` mode globally. [GH-2261] - core: box URL and add date is tracked and shown if `-i` flag is specified for `vagrant box list` [GH-2327] - core: Multiple SSH keys can be specified with `config.ssh.private_key_path` [GH-907] - core: `config.vm.box_url` can be an array of URLs. [GH-1958] - commands/box/add: Can now specify a custom CA cert for verifying certs from a custom CA. [GH-2337] - commands/box/add: Can now specify a client cert when downloading a box. [GH-1889] - commands/init: Add `--output` option for specifing output path, or "-" for stdin. [GH-1364] - commands/provision: Add `--no-parallel` option to disable provider parallelization if the provider supports it. [GH-2404] - commands/ssh: SSH compression is enabled by default. [GH-2456] - commands/ssh: Inline commands specified with "-c" are now executed using OpenSSH rather than pure-Ruby SSH. It is MUCH faster, and stdin works! - communicators/ssh: new configuration `config.ssh.pty` is a boolean for whether you want ot use a PTY for provisioning. - guests/linux: emit upstart event `vagrant-mounted` if upstart is available. [GH-2502] - guests/pld: support changing hostname [GH-2543] - providers/virtualbox: Enable symlinks for VirtualBox 4.1. [GH-2414] - providers/virtualbox: default VM name now includes milliseconds with a random number to try to avoid conflicts in CI environments. [GH-2482] - providers/virtualbox: customizations via VBoxManage are retried, avoiding VirtualBox flakiness [GH-2483] - providers/virtualbox: NFS works with DHCP host-only networks now. [GH-2560] - provisioners/ansible: allow files for extra vars [GH-2366] - provisioners/puppet: client cert and private key can now be specified for the puppet server provisioner. [GH-902] - provisioners/puppet: the manifests path can be in the VM. [GH-1805] - provisioners/shell: Added `keep_color` option to not automatically color output based on stdout/stderr. [GH-2505] - provisioners/shell: Arguments can now be an array of args. [GH-1949] - synced\_folders/nfs: Specify `nfs_udp` to false to disable UDP based NFS folders. [GH-2304] BUG FIXES: - core: Make sure machine IDs are always strings. [GH-2434] - core: 100% CPU spike when waiting for SSH is fixed. [GH-2401] - core: Command lookup works on systems where PATH is not valid UTF-8 [GH-2514] - core: Human-friendly error if box metadata.json becomes corrupted. [GH-2305] - core: Don't load Vagrantfile on `vagrant plugin` commands, allowing Vagrantfiles that use plugins to work. [GH-2388] - core: global flags are ignored past the "--" on the CLI. [GH-2491] - core: provisoining will properly happen if `up` failed. [GH-2488] - guests/freebsd: Mounting NFS folders works. [GH-2400] - guests/freebsd: Uses `sh` by default for shell. [GH-2485] - guests/linux: upstart events listening for `vagrant-mounted` won't wait for jobs to complete, fixing issues with blocking during vagrant up [GH-2564] - guests/redhat: `DHCP_HOSTNAME` is set to the hostname, not the FQDN. [GH-2441] - guests/redhat: Down interface before messing up configuration file for networking. [GH-1577] - guests/ubuntu: "localhost" is preserved when changing hostnames. [GH-2383] - hosts/bsd: Don't set mapall if maproot is set in NFS. [GH-2448] - hosts/gentoo: Support systemd for NFS startup. [GH-2382] - providers/virtualbox: Don't start new VM if VirtualBox has transient failure during `up` from suspended. [GH-2479] - provisioners/chef: Chef client encrypted data bag secrets are now uploaded to the provisioning path to avoid perm issues. [GH-1246] - provisioners/chef: Create/chown the cache and backup folders. [GH-2281] - provisioners/chef: Verify environment paths exist in config validation step. [GH-2381] - provisioners/puppet: Multiple puppet definitions in a Vagrantfile work correctly. - provisioners/salt: Bootstrap on FreeBSD systems work. [GH-2525] - provisioners/salt: Extra args for bootstrap are put in the proper location. [GH-2558] ## 1.3.5 (October 15, 2013) FEATURES: - VirtualBox 4.3 is now supported. [GH-2374] - ESXi is now a supported guest OS. [GH-2347] IMPROVEMENTS: - guests/redhat: Oracle Linux is now supported. [GH-2329] - provisioners/salt: Support running overstate. [GH-2313] BUG FIXES: - core: Fix some places where "no error message" errors were being reported when in fact there were errors. [GH-2328] - core: Disallow hyphens or periods for starting hostnames. [GH-2358] - guests/ubuntu: Setting hostname works properly. [GH-2334] - providers/virtualbox: Retryable VBoxManage commands are properly retried. [GH-2365] - provisioners/ansible: Verbosity won't be blank by default. [GH-2320] - provisioners/chef: Fix exception raised during Chef client node cleanup. [GH-2345] - provisioners/salt: Correct master seed file name. [GH-2359] ## 1.3.4 (October 2, 2013) FEATURES: - provisioners/shell: Specify the `binary` option as true and Vagrant won't automatically replace Windows line endings with Unix ones. [GH-2235] IMPROVEMENTS: - guests/suse: Support installing CFEngine. [GH-2273] BUG FIXES: - core: Don't output `\e[0K` anymore on Windows. [GH-2246] - core: Only modify `DYLD_LIBRARY_PATH` on Mac when executing commands in the installer context. [GH-2231] - core: Clear `DYLD_LIBRARY_PATH` on Mac if the subprocess is executing a setuid or setgid script. [GH-2243] - core: Defined action hook names can be strings now. They are converted to symbols internally. - guests/debian: FQDN is properly set when setting the hostname. [GH-2254] - guests/linux: Fix poor chown command for mounting VirtualBox folders. - guests/linux: Don't raise exception right away if mounting fails, allow retries. [GH-2234] - guests/redhat: Changing hostname changes DHCP_HOSTNAME. [GH-2267] - hosts/arch: Vagrant won't crash on Arch anymore. [GH-2233] - provisioners/ansible: Extra vars are converted to strings. [GH-2244] - provisioners/ansible: Output will show up on a task-by-task basis. [GH-2194] - provisioners/chef: Propagate disabling color if Vagrant has no color enabled. [GH-2246] - provisioners/chef: Delete from chef server exception fixed. [GH-2300] - provisioners/puppet: Work with restrictive umask. [GH-2241] - provisioners/salt: Remove bootstrap definition file on each run in order to avoid permissions issues. [GH-2290] ## 1.3.3 (September 18, 2013) BUG FIXES: - core: Fix issues with dynamic linker not finding symbols on OS X. [GH-2219] - core: Properly clean up machine directories on destroy. [GH-2223] - core: Add a timeout to waiting for SSH connection and server headers on SSH. [GH-2226] ## 1.3.2 (September 17, 2013) IMPROVEMENTS: - provisioners/ansible: Support more verbosity levels, better documentation. [GH-2153] - provisioners/ansible: Add `host_key_checking` configuration. [GH-2203] BUG FIXES: - core: Report the proper invalid state when waiting for the guest machine to be ready - core: `Guest#capability?` now works with strings as well - core: Fix NoMethodError in the new `Vagrant.has_plugin?` method [GH-2189] - core: Convert forwarded port parameters to integers. [GH-2173] - core: Don't spike CPU to 100% while waiting for machine to boot. [GH-2163] - core: Increase timeout for individual SSH connection to 60 seconds. [GH-2163] - core: Call realpath after creating directory so NFS directory creation works. [GH-2196] - core: Don't try to be clever about deleting the machine state directory anymore. Manually done in destroy actions. [GH-2201] - core: Find the root Vagrantfile only if Vagrantfile is a file, not a directory. [GH-2216] - guests/linux: Try `id -g` in addition to `getent` for mounting VirtualBox shared folders [GH-2197] - hosts/arch: NFS exporting works properly, no exceptions. [GH-2161] - hosts/bsd: Use only `sudo` for writing NFS exports. This lets NFS exports work if you have sudo privs but not `su`. [GH-2191] - hosts/fedora: Fix host detection encoding issues. [GH-1977] - hosts/linux: Fix NFS export problems with `no_subtree_check`. [GH-2156] - installer/mac: Vagrant works properly when a library conflicts from homebrew. [GH-2188] - installer/mac: deb/rpm packages now have an epoch of 1 so that new installers don't appear older. [GH-2179] - provisioners/ansible: Default output level is now verbose again. [GH-2194] - providers/virtualbox: Fix an issue where destroy middlewares weren't being properly called. [GH-2200] ## 1.3.1 (September 6, 2013) BUG FIXES: - core: Fix various issues where using the same options hash in a Vagrantfile can cause errors. - core: `VAGRANT_VAGRANTFILE` env var only applies to the project Vagrantfile name. [GH-2130] - core: Fix an issue where the data directory would be deleted too quickly in a multi-VM environment. - core: Handle the case where we get an EACCES cleaning up the .vagrant directory. - core: Fix exception on upgrade warnings from V1 to V2. [GH-2142] - guests/coreos: Proper IP detection. [GH-2146] - hosts/linux: NFS exporting works properly again. [GH-2137] - provisioners/chef: Work even with restrictive umask on user. [GH-2121] - provisioners/chef: Fix environment validation to be less restrictive. - provisioners/puppet: No more "shared folders cannot be found" error. [GH-2134] - provisioners/puppet: Work with restrictive umask on user by testing for folders with sudo. [GH-2121] ## 1.3.0 (September 5, 2013) BACKWARDS INCOMPATIBILITY: - `config.ssh.max_tries` is gone. Instead of maximum tries, Vagrant now uses a simple overall timeout value `config.vm.boot_timeout` to wait for the machine to boot up. - `config.vm.graceful_halt_retry_*` settings are gone. Instead, a single timeout is now used to wait for a graceful halt to work, specified by `config.vm.graceful_halt_timeout`. - The ':extra' flag to shared folders for specifying arbitrary mount options has been replaced with the `:mount_options` flag, which is now an array of mount options. - `vagrant up` will now only run provisioning by default the first time it is run. Subsequent `reload` or `up` will need to explicitly specify the `--provision` flag to provision. [GH-1776] FEATURES: - New command: `vagrant plugin update` to update specific installed plugins. - New provisioner: File provisioner. [GH-2112] - New provisioner: Salt provisioner. [GH-1626] - New guest: Mac OS X guest support. [GH-1914] - New guest: CoreOS guest support. Change host names and configure networks on CoreOS. [GH-2022] - New guest: Solaris 11 guest support. [GH-2052] - Support for environments in the Chef-solo provisioner. [GH-1915] - Provisioners can now define "cleanup" tasks that are executed on `vagrant destroy`. [GH-1302] - Chef Client provisioner will now clean up the node/client using `knife` if configured to do so. - `vagrant up` has a `--no-destroy-on-error` flag that will not destroy the VM if a fatal error occurs. [GH-2011] - NFS: Arbitrary mount options can be specified using the `mount_options` option on synced folders. [GH-1029] - NFS: Arbitrary export options can be specified using `bsd__nfs_options` and `linux__nfs_options`. [GH-1029] - Static IP can now be set on public networks. [GH-1745] - Add `Vagrant.has_plugin?` method for use in Vagrantfile to check if a plugin is installed. [GH-1736] - Support for remote shell provisioning scripts [GH-1787] IMPROVEMENTS: - core: add `--color` to any Vagrant command to FORCE color output. [GH-2027] - core: "config.vm.host_name" works again, just an alias to hostname. - core: Reboots via SSH are now handled gracefully (without exception). - core: Mark `disabled` as true on forwarded port to disable. [GH-1922] - core: NFS exports are now namespaced by user ID, so pruning NFS won't remove exports from other users. [GH-1511] - core: "vagrant -v" no longer loads the Vagrantfile - commands/box/remove: Fix stack trace that happens if no provider is specified. [GH-2100] - commands/plugin/install: Post install message of a plugin will be shown if available. [GH-1986] - commands/status: cosmetic improvement to better align names and statuses [GH-2016] - communicators/ssh: Support a proxy_command. [GH-1537] - guests/openbsd: support configuring networks, changing host name, and mounting NFS. [GH-2086] - guests/suse: Supports private/public networks. [GH-1689] - hosts/fedora: Support RHEL as a host. [GH-2088] - providers/virtualbox: "post-boot" customizations will run directly after boot, and before waiting for SSH. [GH-2048] - provisioners/ansible: Many more configuration options. [GH-1697] - provisioners/ansible: Ansible `inventory_path` can be a directory now. [GH-2035] - provisioners/ansible: Extra verbose option by setting `config.verbose` to `extra`. [GH-1979] - provisioners/ansible: `inventory_path` will be auto-generated if not specified. [GH-1907] - provisioners/puppet: Add `nfs` option to puppet provisioner. [GH-1308] - provisioners/shell: Set the `privileged` option to false to run without sudo. [GH-1370] BUG FIXES: - core: Clean up ".vagrant" folder more effectively. - core: strip newlines off of ID file values [GH-2024] - core: Multiple forwarded ports with different protocols but the same host port can be specified. [GH-2059] - core: `:nic_type` option for private networks is respected. [GH-1704] - commands/up: provision-with validates the provisioners given. [GH-1957] - guests/arch: use systemd way of setting host names. [GH-2041] - guests/debian: Force bring up eth0. Fixes hangs on setting hostname. [GH-2026] - guests/ubuntu: upstart events are properly emitted again. [GH-1717] - hosts/bsd: Nicer error if can't read NFS exports. [GH-2038] - hosts/fedora: properly detect later CentOS versions. [GH-2008] - providers/virtualbox: VirtualBox 4.2 now supports up to 36 network adapters. [GH-1886] - provisioners/ansible: Execute ansible with a cwd equal to the path where the Vagrantfile is. [GH-2051] - provisioners/all: invalid config keys will be properly reported. [GH-2117] - provisioners/ansible: No longer report failure on every run. [GH-2007] - provisioners/ansible: Properly handle extra vars with spaces. [GH-1984] - provisioners/chef: Formatter option works properly. [GH-2058] - provisioners/chef: Create/chown the provisioning folder before reading contents. [GH-2121] - provisioners/puppet: mount synced folders as root to avoid weirdness - provisioners/puppet: Run from the correct working directory. [GH-1967] with Puppet. [GH-2015] - providers/virtualbox: Use `getent` to get the group ID instead of `id` in case the name doesn't have a user. [GH-1801] - providers/virtualbox: Will only set the default name of the VM on initial `up`. [GH-1817] ## 1.2.7 (July 28, 2013) BUG FIXES: - On Windows, properly convert synced folder host path to a string so that separator replacement works properly. - Use `--color=false` for no color in Puppet to support older versions properly. [GH-2000] - Make sure the hostname configuration is a string. [GH-1999] - cURL downloads now contain a user agent which fixes some issues with downloading Vagrant through proxies. [GH-2003] - `vagrant plugin install` will now always properly show the actual installed gem name. [GH-1834] ## 1.2.6 (July 26, 2013) BUG FIXES: - Box collections with multiple formats work properly by converting the supported formats to symbols. [GH-1990] ## 1.2.5 (July 26, 2013) FEATURES: - `vagrant help ` now works. [GH-1578] - Added `config.vm.box_download_insecure` to allow the box_url setting to point to an https site that won't be validated. [GH-1712] - VirtualBox VBoxManage customizations can now be specified to run pre-boot (the default and existing functionality, pre-import, or post-boot. [GH-1247] - VirtualBox no longer destroys unused network interfaces by default. This didn't work across multi-user systems and required admin privileges on Windows, so it has been disabled by default. It can be enabled using the VirtualBox provider-specific `destroy_unused_network_interfaces` configuration by setting it to true. [GH-1324] IMPROVEMENTS: - Remote commands that fail will now show the stdout/stderr of the command that failed. [GH-1203] - Puppet will run without color if the UI is not colored. [GH-1344] - Chef supports the "formatter" configuration for setting the formatter. [GH-1250] - VAGRANT_DOTFILE_PATH environmental variable reintroduces the functionality removed in 1.1 from "config.dotfile_name" [GH-1524] - Vagrant will show an error if VirtualBox 4.2.14 is running. - Added provider to BoxNotFound error message. [GH-1692] - If Ansible fails to run properly, show an error message. [GH-1699] - Adding a box with the `--provider` flag will now allow a box for any of that provider's supported formats. - NFS mounts enable UDP by default, resulting in higher performance. (Because mount is over local network, packet loss is not an issue) [GH-1706] BUG FIXES: - `box_url` now handles the case where the provider doesn't perfectly match the provider in use, but the provider supports it. [GH-1752] - Fix uninitialized constant error when configuring Arch Linux network. [GH-1734] - Debian/Ubuntu change hostname works properly if eth0 is configured with hot-plugging. [GH-1929] - NFS exports with improper casing on Mac OS X work properly. [GH-1202] - Shared folders overriding '/vagrant' in multi-VM environments no longer all just use the last value. [GH-1935] - NFS export fsid's are now 32-bit integers, rather than UUIDs. This lets NFS exports work with Linux kernels older than 2.6.20. [GH-1127] - NFS export allows access from all private networks on the VM. [GH-1204] - Default VirtualBox VM name now contains the machine name as defined in the Vagrantfile, helping differentiate multi-VM. [GH-1281] - NFS works properly on CentOS hosts. [GH-1394] - Solaris guests actually shut down properly. [GH-1506] - All provisioners only output newlines when the provisioner sends a newline. This results in the output looking a lot nicer. - Sharing folders works properly if ".profile" contains an echo. [GH-1677] - `vagrant ssh-config` IdentityFile is only wrapped in quotes if it contains a space. [GH-1682] - Shared folder target path can be a Windows path. [GH-1688] - Forwarded ports don't auto-correct by default, and will raise an error properly if they collide. [GH-1701] - Retry SSH on ENETUNREACH error. [GH-1732] - NFS is silently ignored on Windows. [GH-1748] - Validation so that private network static IP does not end in ".1" [GH-1750] - With forward agent enabled and sudo being used, Vagrant will automatically discover and set `SSH_AUTH_SOCK` remotely so that forward agent works properly despite misconfigured sudoers. [GH-1307] - Synced folder paths on Windows containing '\' are replaced with '/' internally so that they work properly. - Unused config objects are finalized properly. [GH-1877] - Private networks work with Fedora guests once again. [GH-1738] - Default internal encoding of strings in Vagrant is now UTF-8, allowing detection of Fedora to work again (which contained a UTF-8 string). [GH-1977] ## 1.2.4 (July 16, 2013) FEATURES: - Chef solo and client provisioning now support a `custom_config_path` setting that accepts a path to a Ruby file to load as part of Chef configuration, allowing you to override any setting available. [GH-876] - CFEngine provisioner: you can now specify the package name to install, so CFEngine enterprise is supported. [GH-1920] IMPROVEMENTS: - `vagrant box remove` works with only the name of the box if that box exists only backed by one provider. [GH-1032] - `vagrant destroy` returns exit status 1 if any of the confirmations are declined. [GH-923] - Forwarded ports can specify a host IP and guest IP to bind to. [GH-1121] - You can now set the "ip" of a private network that uses DHCP. This will change the subnet and such that the DHCP server uses. - Add `file_cache_path` support for chef_solo. [GH-1897] BUG FIXES: - VBoxManage or any other executable missing from PATH properly reported. Regression from 1.2.2. [GH-1928] - Boxes downloaded as part of `vagrant up` are now done so _prior_ to config validation. This allows Vagrantfiles to references files that may be in the box itself. [GH-1061] - Chef removes dna.json and encrypted data bag secret file prior to uploading. [GH-1111] - NFS synced folders exporting sub-directories of other exported folders now works properly. [GH-785] - NFS shared folders properly dereference symlinks so that the real path is used, avoiding mount errors [GH-1101] - SSH channel is closed after the exit status is received, potentially eliminating any SSH hangs. [GH-603] - Fix regression where VirtualBox detection wasn't working anymore. [GH-1918] - NFS shared folders with single quotes in their name now work properly. [GH-1166] - Debian/Ubuntu request DHCP renewal when hostname changes, which will fix issues with FQDN detecting. [GH-1929] - SSH adds the "DSAAuthentication=yes" option in case that is disabled on the user's system. [GH-1900] ## 1.2.3 (July 9, 2013) FEATURES: - Puppet provisioner now supports Hiera by specifying a `hiera_config_path`. - Added a `working_directory` configuration option to the Puppet apply provisioner so you can specify the working directory when `puppet` is called, making it friendly to Hiera data and such. [GH-1670] - Ability to specify the host IP to bind forwarded ports to. [GH-1785] IMPROVEMENTS: - Setting hostnames works properly on OmniOS. [GH-1672] - Better VBoxManage error detection on Windows systems. This avoids some major issues where Vagrant would sometimes "lose" your VM. [GH-1669] - Better detection of missing VirtualBox kernel drivers on Linux systems. [GH-1671] - More precise detection of Ubuntu/Debian guests so that running Vagrant within an LXC container works properly now. - Allow strings in addition to symbols to more places in V1 configuration as well as V2 configuration. - Add `ARPCHECK=0` to RedHat OS family network configuration. [GH-1815] - Add SSH agent forwarding sample to initial Vagrantfile. [GH-1808] - VirtualBox: Only configure networks if there are any to configure. This allows linux's that don't implement this capability to work with Vagrant. [GH-1796] - Default SSH forwarded port now binds to 127.0.0.1 so only local connections are allowed. [GH-1785] - Use `netctl` for Arch Linux network configuration. [GH-1760] - Improve fedora host detection regular expression. [GH-1913] - SSH shows a proper error on EHOSTUNREACH. [GH-1911] BUG FIXES: - Ignore "guest not ready" errors when attempting to graceful halt and carry on checks whether the halt succeeded. [GH-1679] - Handle the case where a roles path for Chef solo isn't properly defined. [GH-1665] - Finding V1 boxes now works properly again to avoid "box not found" errors. [GH-1691] - Setting hostname on SLES 11 works again. [GH-1781] - `config.vm.guest` properly forces guests again. [GH-1800] - The `read_ip_address` capability for linux properly reads the IP of only the first network interface. [GH-1799] - Validate that an IP is given for a private network. [GH-1788] - Fix uninitialized constant error for Gentoo plugin. [GH-1698] ## 1.2.2 (April 23, 2013) FEATURES: - New `DestroyConfirm` built-in middleware for providers so they can more easily conform to the `destroy` action. IMPROVEMENTS: - No longer an error if the Chef run list is empty. It is now a warning. [GH-1620] - Better locking around handling the `box_url` parameter for parallel providers. - Solaris guest is now properly detected on SmartOS, OmniOS, etc. [GH-1639] - Guest addition version detection is more robust, attempting other routes to get the version, and also retrying a few times. [GH-1575] BUG FIXES: - `vagrant package --base` works again. [GH-1615] - Box overrides specified in provider config overrides no longer fail to detect the box. [GH-1617] - In a multi-machine environment, a box not found won't be downloaded multiple times. [GH-1467] - `vagrant box add` with a file path now works correctly on Windows when a drive letter is specified. - DOS line endings are converted to Unix line endings for the shell provisioner automatically. [GH-1495] ## 1.2.1 (April 17, 2013) FEATURES: - Add a `--[no-]parallel` flag to `vagrant up` to enable/disable parallelism. Vagrant will parallelize by default. IMPROVEMENTS: - Get rid of arbitrary 4 second sleep when connecting via SSH. The issue it was attempting to work around may be gone now. BUG FIXES: - Chef solo run list properly set. [GH-1608] - Follow 30x redirects when downloading boxes. [GH-1607] - Chef client config defaults are done properly. [GH-1609] - VirtualBox mounts shared folders with the proper owner/group. [GH-1611] - Use the Mozilla CA cert bundle for cURL so SSL validation works properly. ## 1.2.0 (April 16, 2013) BACKWARDS INCOMPATIBILITIES: - WINDOWS USERS: Vagrant now defaults to using the 'USERPROFILE' environmental variable for the home directory if it is set. This means that the default location for the Vagrant home directory is now `%USERPROFILE%/.vagrant.d`. On Cygwin, this will cause existing Cygwin users to "lose" their boxes. To work around this, either set `VAGRANT_HOME` to your Cygwin ".vagrant.d" folder or move your ".vagrant.d" folder to `USERPROFILE`. The latter is recommended for long-term support. - The constant `Vagrant::Environment::VAGRANT_HOME` was removed in favor of `Vagrant::Environment#default_vagrant_home`. FEATURES: - Providers can now parallelize! If they explicitly support it, Vagrant will run "up" and other commands in parallel. For providers such AWS, this means that your instances will come up in parallel. VirtualBox does not support this mode. - Box downloads are now done via `curl` rather than Ruby's built-in HTTP library. This results in massive speedups, support for SSL verification, FTP downloads, and more. - `config.vm.provider` now takes an optional second parameter to the block, allowing you to override any configuration value. These overrides are applied last, and therefore override any other configuration value. Note that while this feature is available, the "Vagrant way" is instead to use box manifests to ensure that the "box" for every provider matches, so these sorts of overrides are unnecessary. - A new "guest capabilities" system to replace the old "guest" system. This new abstraction allows plugins to define "capabilities" that certain guest operating systems can implement. This allows greater flexibility in doing guest-specific behavior. - Ansible provisioner support. [GH-1465] - Providers can now support multiple box formats by specifying the `box_format:` option. - CFEngine provisioner support. - `config.ssh.default` settings introduced to set SSH defaults that providers can still override. [GH-1479] IMPROVEMENTS: - Full Windows support in cmd.exe, PowerShell, Cygwin, and MingW based environments. - By adding the "disabled" boolean flag to synced folders you can disable them altogether. [GH-1004] - Specify the default provider with the `VAGRANT_DEFAULT_PROVIDER` environmental variable. [GH-1478] - Invalid settings are now caught and shown in a user-friendly way. [GH-1484] - Detect PuTTY Link SSH client on Windows and show an error. [GH-1518] - `vagrant ssh` in Cygwin won't output DOS path file warnings. - Add `--rtcuseutc on` as a sane default for VirtualBox. [GH-912] - SSH will send keep-alive packets every 5 seconds by default to keep connections alive. Can be disabled with `config.ssh.keep_alive`. [GH-516] - Show a message on `vagrant up` if the machine is already running. [GH-1558] - "Running provisioner" output now shoes the provisioner shortcut name, rather than the less-than-helpful class name. - Shared folders with the same guest path will overwrite each other. No more shared folder IDs. - Shell provisioner outputs script it is running. [GH-1568] - Automatically merge forwarded ports that share the same host port. BUG FIXES: - The `:mac` option for host-only networks is respected. [GH-1536] - Don't preserve modified time when untarring boxes. [GH-1539] - Forwarded port auto-correct will not auto-correct to a port that is also in use. - Cygwin will always output color by default. Specify `--no-color` to override this. - Assume Cygwin has a TTY for asking for input. [GH-1430] - Expand Cygwin paths to Windows paths for calls to VBoxManage and for VirtualBox shared folders. - Output the proper clear line text for shells in Cygwin when reporting dynamic progress. - When using `Builder` instances for hooks, the builders will be merged for the proper before/after chain. [GH-1555] - Use the Vagrant temporary directory again for temporary files since they can be quite large and were messing with tmpfs. [GH-1442] - Fix issue parsing extra SSH args in `vagrant ssh` in multi-machine environments. [GH-1545] - Networks come back up properly on RedHat systems after reboot. [GH-921] - `config.ssh` settings override all detected SSH settings (regression). [GH-1479] - `ssh-config` won't raise an exception if the VirtualBox machine is not created. [GH-1562] - Multiple machines defined in the same Vagrantfile with the same name will properly merge. - More robust hostname checking for RedHat. [GH-1566] - Cookbook path existence for Chef is no longer an error, so that things like librarian and berkshelf plugins work properly. [GH-1570] - Chef solo provisioner uses proper SSH username instead of hardcoded config. [GH-1576] - Shell provisioner takes ownership of uploaded files properly so that they can also be manually executed later. [GH-1576] ## 1.1.6 (April 3, 2013) BUG FIXES: - Fix SSH re-use connection logic to drop connection if an error occurs. ## 1.1.5 (April 2, 2013) IMPROVEMENTS: - More robust SSH connection close detection. - Don't load `vagrant plugin` installed plugins when in a Bundler environment. This happens during plugin development. This will make Vagrant errors much quieter when developing plugins. - Vagrant will detect Bundler environments, make assumptions that you're developing plugins, and will quiet its error output a bit. - More comprehensive synced folder configuration validation. - VBoxManage errors now show the output from the command so that users can potentially know what is wrong. BUG FIXES: - Proper error message if invalid provisioner is used. [GH-1515] - Don't error on graceful halt if machine just shut down very quickly. [GH-1505] - Error message if private key for SSH isn't owned by the proper user. [GH-1503] - Don't error too early when `config.vm.box` is not properly set. - Show a human-friendly error if VBoxManage is not found (exit status 126). [GH-934] - Action hook prepend/append will only prepend or append once. - Retry SSH on Errno::EACCES. - Show an error if an invalid network type is used. - Don't share Chef solo folder if it doesn't exist on host. ## 1.1.4 (March 25, 2013) BUG FIXES: - Default forwarded port adapter for VirtualBox should be 1. ## 1.1.3 (March 25, 2013) IMPROVEMENTS: - Puppet apply provisioner now retains the default module path even while specifying custom module paths. [GH-1207] - Re-added DHCP support for host-only networks. [GH-1466] - Ability to specify a plugin version, plugin sources, and pre-release versions using `--plugin-version`, `--plugin-source`, and `--plugin-prerelease`. [GH-1461] - Move VirtualBox guest addition checks to after the machine boots. [GH-1179] - Removed `Vagrant::TestHelpers` because it doesn't really work anymore. - Add PLX linux guest support. [GH-1490] BUG FIXES: - Attempt to re-establish SSH connection on `Net::SSH::Disconnect` - Allow any value that can convert to a string for `Vagrant.plugin` - Chef solo `recipe_url` works properly again. [GH-1467] - Port collision detection works properly in VirtualBox with auto-corrected ports. [GH-1472] - Fix obscure error when temp directory is world writable when adding boxes. - Improved error handling around network interface detection for VirtualBox [GH-1480] ## 1.1.2 (March 18, 2013) BUG FIXES: - When not specifying a cookbooks_path for chef-solo, an error won't be shown if "cookbooks" folder is missing. - Fix typo for exception when no host-only network with NFS. [GH-1448] - Use UNSET_VALUE/nil with args on shell provisioner by default since `[]` was too truthy. [GH-1447] ## 1.1.1 (March 18, 2013) IMPROVEMENTS: - Don't load plugins on any `vagrant plugin` command, so that errors are avoided. [GH-1418] - An error will be shown if you forward a port to the same host port multiple times. - Automatically convert network, provider, and provisioner names to symbols internally in case people define them as strings. - Using newer versions of net-ssh and net-scp. [GH-1436] BUG FIXES: - Quote keys to StringBlockEditor so keys with spaces, parens, and so on work properly. - When there is no route to host for SSH, re-establish a new connection. - `vagrant package` once again works, no more nil error. [GH-1423] - Human friendly error when "metadata.json" is missing in a box. - Don't use the full path to the manifest file with the Puppet provisioner because it exposes a bug with Puppet path lookup on VMware. - Fix bug in VirtualBox provider where port forwarding just didn't work if you attempted to forward to a port under 1024. [GH-1421] - Fix cross-device box adds for Windows. [GH-1424] - Fix minor issues with defaults of configuration of the shell provisioner. - Fix Puppet server using "host_name" instead of "hostname" [GH-1444] - Raise a proper error if no hostonly network is found for NFS with VirtualBox. [GH-1437] ## 1.1.0 (March 14, 2013) BACKWARDS INCOMPATIBILITIES: - Vagrantfiles from 1.0.x that _do not use_ any plugins are fully backwards compatible. If plugins are used, they must be removed prior to upgrading. The new plugin system in place will avoid this issue in the future. - Lots of changes introduced in the form of a new configuration version and format, but this is _opt-in_. Old Vagrantfile format continues to be supported, as promised. To use the new features that will be introduced throughout the 1.x series, you'll have to upgrade at some point. FEATURES: - Groundwork for **providers**, alternate backends for Vagrant that allow Vagrant to power systems other than VirtualBox. Much improvement and change will come to this throughout the 1.x lifecycle. The API will continue to change, features will be added, and more. Specifically, a revamped system for handling shared folders gracefully across providers will be introduced in a future release. - New plugin system which adds much more structure and stability to the overall API. The goal of this system is to make it easier to write powerful plugins for Vagrant while providing a backwards-compatible API so that plugins will always _load_ (though they will almost certainly not be _functional_ in future versions of Vagrant). - Plugins are now installed and managed using the `vagrant plugin` interface. - Allow "file://" URLs for box URLs. [GH-1087] - Emit "vagrant-mount" upstart event when NFS shares are mounted. [GH-1118] - Add a VirtualBox provider config `auto_nat_dns_proxy` which when set to false will not attempt to automatically manage NAT DNS proxy settings with VirtualBox. [GH-1313] - `vagrant provision` accepts the `--provision-with` flag [GH-1167] - Set the name of VirtualBox machines with `virtualbox.name` in the VirtualBox provider config. [GH-1126] - `vagrant ssh` will execute an `ssh` binary on Windows if it is on your PATH. [GH-933] - The environmental variable `VAGRANT_VAGRANTFILE` can be used to specify an alternate Vagrantfile filename. IMPROVEMENTS / BUG FIXES: - Vagrant works much better in Cygwin environments on Windows by properly resolving Cygwin paths. [GH-1366] - Improve the SSH "ready?" check by more gracefully handling timeouts. [GH-841] - Human friendly error if connection times out for HTTP downloads. [GH-849] - Detect when the VirtualBox installation is incomplete and error. [GH-846] - Detect when kernel modules for VirtualBox need to be installed on Gentoo systems and report a user-friendly error. [GH-710] - All `vagrant` commands that can take a target VM name can take one even if you're not in a multi-VM environment. [GH-894] - Hostname is set before networks are setup to avoid very slow `sudo` speeds on CentOS. [GH-922] - `config.ssh.shell` now includes the flags to pass to it, such as `-l` [GH-917] - The check for whether a port is open or not is more complete by catching ENETUNREACH errors. [GH-948] - SSH uses LogLevel FATAL so that errors are still shown. - Sending a SIGINT (Ctrl-C) very early on when executing `vagrant` no longer results in an ugly stack trace. - Chef JSON configuration output is now pretty-printed to be human readable. [GH-1146] that SSHing succeeds when booting a machine. - VMs in the "guru meditation" state can be destroyed now using `vagrant destroy`. - Fix issue where changing SSH key permissions didn't properly work. [GH-911] - Fix issue where Vagrant didn't properly detect VBoxManage on Windows if VBOX_INSTALL_PATH contained multiple paths. [GH-885] - Fix typo in setting host name for Gentoo guests. [GH-931] - Files that are included with `vagrant package --include` now properly preserve file attributes on earlier versions of Ruby. [GH-951] - Multiple interfaces now work with Arch linux guests. [GH-957] - Fix issue where subprocess execution would always spin CPU of Ruby process to 100%. [GH-832] - Fix issue where shell provisioner would sometimes never end. [GH-968] - Fix issue where puppet would reorder module paths. [GH-964] - When console input is asked for (destroying a VM, bridged interfaces, etc.), keystrokes such as ctrl-D and ctrl-C are more gracefully handled. [GH-1017] - Fixed bug where port check would use "localhost" on systems where "localhost" is not available. [GH-1057] - Add missing translation for "saving" state on VirtualBox. [GH-1110] - Proper error message if the remote end unexpectedly resets the connection while downloading a box over HTTP. [GH-1090] - Human-friendly error is raised if there are permission issues when using SCP to upload files. [GH-924] - Box adding doesn't use `/tmp` anymore which can avoid some cross-device copy issues. [GH-1199] - Vagrant works properly in folders with strange characters. [GH-1223] - Vagrant properly handles "paused" VirtualBox machines. [GH-1184] - Better behavior around permissions issues when copying insecure private key. [GH-580] ## 1.0.7 (March 13, 2013) - Detect if a newer version of Vagrant ran and error if it did, because we're not forward-compatible. - Check for guest additions version AFTER booting. [GH-1179] - Quote IdentityFile in `ssh-config` so private keys with spaces in the path work. [GH-1322] - Fix issue where multiple Puppet module paths can be re-ordered [GH-964] - Shell provisioner won't hang on Windows anymore due to unclosed tempfile. [GH-1040] - Retry setting default VM name, since it sometimes fails first time. [GH-1368] - Support setting hostname on Suse [GH-1063] ## 1.0.6 (December 21, 2012) - Shell provisioner outputs proper line endings on Windows [GH-1164] - SSH upload opens file to stream which fixes strange upload issues. - Check for proper exit codes for Puppet, since multiple exit codes can mean success. [GH-1180] - Fix issue where DNS doesn't resolve properly for 12.10. [GH-1176] - Allow hostname to be a substring of the box name for Ubuntu [GH-1163] - Use `puppet agent` instead of `puppetd` to be Puppet 3.x compatible. [GH-1169] - Work around bug in VirtualBox exposed by bug in OS X 10.8 where VirtualBox executables couldn't handle garbage being injected into stdout by OS X. ## 1.0.5 (September 18, 2012) - Work around a critical bug in VirtualBox 4.2.0 on Windows that causes Vagrant to not work. [GH-1130] - Plugin loading works better on Windows by using the proper file path separator. - NFS works on Fedora 16+. [GH-1140] - NFS works with newer versions of Arch hosts that use systemd. [GH-1142] ## 1.0.4 (September 13, 2012) - VirtualBox 4.2 driver. [GH-1120] - Correct `ssh-config` help to use `--host`, not `-h`. - Use "127.0.0.1" instead of "localhost" for port checking to fix problem where "localhost" is not properly setup. [GH-1057] - Disable read timeout on Net::HTTP to avoid `rbuf_fill` error. [GH-1072] - Retry SSH on `EHOSTUNREACH` errors. - Add missing translation for "saving" state. [GH-1110] ## 1.0.3 (May 1, 2012) - Don't enable NAT DNS proxy on machines where resolv.conf already points to localhost. This allows Vagrant to work once again with Ubuntu 12.04. [GH-909] ## 1.0.2 (March 25, 2012) - Provisioners will still mount folders and such if `--no-provision` is used, so that `vagrant provision` works. [GH-803] - Nicer error message if an unsupported SSH key type is used. [GH-805] - Gentoo guests can now have their host names changed. [GH-796] - Relative paths can be used for the `config.ssh.private_key_path` setting. [GH-808] - `vagrant ssh` now works on Solaris, where `IdentitiesOnly` was not an available option. [GH-820] - Output works properly in the face of broken pipes. [GH-819] - Enable Host IO Cache on the SATA controller by default. - Chef-solo provisioner now supports encrypted data bags. [GH-816] - Enable the NAT DNS proxy by default, allowing your DNS to continue working when you switch networks. [GH-834] - Checking for port forwarding collisions also checks for other applications that are potentially listening on that port as well. [GH-821] - Multiple VM names can be specified for the various commands now. For example: `vagrant up web db service`. [GH-795] - More robust error handling if a VM fails to boot. The error message is much clearer now. [GH-825] ## 1.0.1 (March 11, 2012) - Installers are now bundled with Ruby 1.9.3p125. Previously they were bundled with 1.9.3p0. This actually fixes some IO issues with Windows. - Windows installer now outputs a `vagrant` binary that will work in msys or Cygwin environments. - Fix crashing issue which manifested itself in multi-VM environments. - Add missing `rubygems` require in `environment.rb` to avoid possible load errors. [GH-781] - `vagrant destroy` shows a nice error when called without a TTY (and hence can't confirm). [GH-779] - Fix an issue with the `:vagrantfile_name` option to `Vagrant::Environment` not working properly. [GH-778] - `VAGRANT_CWD` environmental variable can be used to set the CWD to something other than the current directory. - Downloading boxes from servers that don't send a content-length now works properly. [GH-788] - The `:facter` option now works for puppet server. [GH-790] - The `--no-provision` and `--provision-with` flags are available to `vagrant reload` now. - `:openbsd` guest which supports only halting at the moment. [GH-773] - `ssh-config -h` now shows help, instead of assuming a host is being specified. For host, you can still use `--host`. [GH-793] ## 1.0.0 (March 6, 2012) - `vagrant gem` should now be used to install Vagrant plugins that are gems. This installs the gems to a private gem folder that Vagrant adds to its own load path. This isolates Vagrant-related gems from system gems. - Plugin loading no longer happens right when Vagrant is loaded, but when a Vagrant environment is loaded. I don't anticipate this causing any problems but it is a backwards incompatible change should a plugin depend on this (but I don't see any reason why they would). - `vagrant destroy` now asks for confirmation by default. This can be overridden with the `--force` flag. [GH-699] - Fix issue with Puppet config inheritance. [GH-722] - Fix issue where starting a VM on some systems was incorrectly treated as failing. [GH-720] - It is now an error to specify the packaging `output` as a directory. [GH-730] - Unix-style line endings are used properly for guest OS. [GH-727] - Retry certain VirtualBox operations, since they intermittently fail. [GH-726] - Fix issue where Vagrant would sometimes "lose" a VM if an exception occurred. [GH-725] - `vagrant destroy` destroys virtual machines in reverse order. [GH-739] - Add an `fsid` option to Linux NFS exports. [GH-736] - Fix edge case where an exception could be raised in networking code. [GH-742] - Add missing translation for the "guru meditation" state. [GH-745] - Check that VirtualBox exists before certain commands. [GH-746] - NIC type can be defined for host-only network adapters. [GH-750] - Fix issue where re-running chef-client would sometimes cause problems due to file permissions. [GH-748] - FreeBSD guests can now have their hostnames changed. [GH-757] - FreeBSD guests now support host only networking and bridged networking. [GH-762] - `VM#run_action` is now public so plugin-devs can hook into it. - Fix crashing bug when attempting to run commands on the "primary" VM in a multi-VM environment. [GH-761] - With puppet you can now specify `:facter` as a dictionary of facts to override what is generated by Puppet. [GH-753] - Automatically convert all arguments to `customize` to strings. - openSUSE host system. [GH-766] - Fix subprocess IO deadlock which would occur on Windows. [GH-765] - Fedora 16 guest support. [GH-772] ## 0.9.7 (February 9, 2012) - Fix regression where all subprocess IO simply didn't work with Windows. [GH-721] ## 0.9.6 (February 7, 2012) - Fix strange issue with inconsistent childprocess reads on JRuby. [GH-711] - `vagrant ssh` does a direct `exec()` syscall now instead of going through the shell. This makes it so things like shell expansion oddities no longer cause problems. [GH-715] - Fix crashing case if there are no ports to forward. - Fix issue surrounding improper configuration of host only networks on RedHat guests. [GH-719] - NFS should work properly on Gentoo. [GH-706] ## 0.9.5 (February 5, 2012) - Fix crashing case when all network options are `:auto_config false`. [GH-689] - Type of network adapter can be specified with `:nic_type`. [GH-690] - The NFS version can be specified with the `:nfs_version` option on shared folders. [GH-557] - Greatly improved FreeBSD guest and host support. [GH-695] - Fix instability with RedHat guests and host only and bridged networks. [GH-698] - When using bridged networking, only list the network interfaces that are up as choices. [GH-701] - More intelligent handling of the `certname` option for puppet server. [GH-702] - You may now explicitly set the network to bridge to in the Vagrantfile using the `:bridge` parameter. [GH-655] ## 0.9.4 (January 28, 2012) - Important internal changes to middlewares that make plugin developer's lives much easier. [GH-684] - Match VM names that have parens, brackets, etc. - Detect when the VirtualBox kernel module is not loaded and error. [GH-677] - Set `:auto_config` to false on any networking option to not automatically configure it on the guest. [GH-663] - NFS shared folder guest paths can now contain shell expansion characters such as `~`. - NFS shared folders with a `:create` flag will have their host folders properly created if they don't exist. [GH-667] - Fix the precedence for Arch, Ubuntu, and FreeBSD host classes so they are properly detected. [GH-683] - Fix issue where VM import sometimes made strange VirtualBox folder layouts. [GH-669] - Call proper `id` command on Solaris. [GH-679] - More accurate VBoxManage error detection. - Shared folders can now be marked as transient using the `:transient` flag. [GH-688] ## 0.9.3 (January 24, 2012) - Proper error handling for not enough arguments to `box` commands. - Fix issue causing crashes with bridged networking. [GH-673] - Ignore host only network interfaces that are "down." [GH-675] - Use "printf" instead of "echo" to determine shell expanded files paths which is more generally POSIX compliant. [GH-676] ## 0.9.2 (January 20, 2012) - Support shell expansions in shared folder guest paths again. [GH-656] - Fix issue where Chef solo always expected the host to have a "cookbooks" folder in their directory. [GH-638] - Fix `forward_agent` not working when outside of blocks. [GH-651] - Fix issue causing custom guest implementations to not load properly. - Filter clear screen character out of output on SSH. - Log output now goes on `stderr`, since it is utility information. - Get rid of case where a `NoMethodError` could be raised while determining VirtualBox version. [GH-658] - Debian/Ubuntu uses `ifdown` again, instead of `ifconfig xxx down`, since the behavior seems different/wrong. - Give a nice error if `:vagrant` is used as a JSON key, since Vagrant uses this. [GH-661] - If there is only one bridgable interface, use that without asking the user. [GH-655] - The shell will have color output if ANSICON is installed on Windows. [GH-666] ## 0.9.1 (January 18, 2012) - Use `ifconfig device down` instead of `ifdown`. [GH-649] - Clearer invalid log level error. [GH-645] - Fix exception raised with NFS `recover` method. - Fix `ui` `NoMethodError` exception in puppet server. - Fix `vagrant box help` on Ruby 1.8.7. [GH-647] ## 0.9.0 (January 17, 2012) - VirtualBox 4.0 support backported in addition to supporting VirtualBox 4.1. - `config.vm.network` syntax changed so that the first argument is now the type of argument. Previously where you had `config.vm.network "33.33.33.10"` you should now put `config.vm.network :hostonly, "33.33.33.10"`. This is in order to support bridged networking, as well. - `config.vm.forward_port` no longer requires a name parameter. - Bridged networking. `config.vm.network` with `:bridged` as the option will setup a bridged network. - Host only networks can be configured with DHCP now. Specify `:dhcp` as the IP and it will be done. - `config.vm.customize` now takes a command to send to `VBoxManage`, so any arbitrary command can be sent. The older style of passing a block no longer works and Vagrant will give a proper error message if it notices this old-style being used. - `config.ssh.forwarded_port_key` is gone. Vagrant no longer cares about forwarded port names for any reason. Please use `config.ssh.guest_port` (more below). - `config.ssh.forwarded_port_destination` has been replaced by `config.ssh.guest_port` which more accurately reflects what it is used for. Vagrant will automatically scan forwarded ports that match the guest port to find the SSH port. - Logging. The entire Vagrant source has had logging sprinkled throughout to make debugging issues easier. To enable logging, set the VAGRANT_LOG environmental variable to the log level you wish to see. By default, logging is silent. - `system` renamed to `guest` throughout the source. Any `config.vm.system` configurations must be changed to `config.vm.guest` - Puppet provisioner no longer defaults manifest to "box.pp." Instead, it is now "default.pp" - All Vagrant commands that take a VM name in a Multi-VM environment can now be given a regular expression. If the name starts and ends with a "/" then it is assumed to be a regular expression. [GH-573] - Added a "--plain" flag to `vagrant ssh` which will cause Vagrant to not perform any authentication. It will simply `ssh` into the proper IP and port of the virtual machine. - If a shared folder now has a `:create` flag set to `true`, the path on the host will be created if it doesn't exist. - Added `--force` flag to `box add`, which will overwite any existing boxes if they exist. [GH-631] - Added `--provision-with` to `up` which configures what provisioners run, by shortcut. [GH-367] - Arbitrary mount options can be passed with `:extra` to any shared folders. [GH-551] - Options passed after a `--` to `vagrant ssh` are now passed directly to `ssh`. [GH-554] - Ubuntu guests will now emit a `vagrant-mounted` upstart event after shared folders are mounted. - `attempts` is a new option on chef client and chef solo provisioners. This will run the provisioner multiple times until erroring about failing convergence. [GH-282] - Removed Thor as a dependency for the command line interfaces. This resulted in general speed increases across all command line commands. - Linux uses `shutdown -h` instead of `halt` to hopefully more consistently power off the system. [GH-575] - Tweaks to SSH to hopefully be more reliable in coming up. - Helpful error message when SCP is unavailable in the guest. [GH-568] - Error message for improperly packaged box files. [GH-198] - Copy insecure private key to user-owned directory so even `sudo` installed Vagrant installations work. [GH-580] - Provisioner stdout/stderr is now color coded based on stdout/stderr. stdout is green, stderr is red. [GH-595] - Chef solo now prompts users to run a `reload` if shared folders are not found on the VM. [GH-253] - "--no-provision" once again works for certain commands. [GH-591] - Resuming a VM from a saved state will show an error message if there would be port collisions. [GH-602] - `vagrant ssh -c` will now exit with the same exit code as the command run. [GH-598] - `vagrant ssh -c` will now send stderr to stderr and stdout to stdout on the host machine, instead of all output to stdout. - `vagrant box add` path now accepts unexpanded shell paths such as `~/foo` and will properly expand them. [GH-633] - Vagrant can now be interrupted during the "importing" step. - NFS exports will no longer be cleared when an expected error occurs. [GH-577] ## 0.8.10 (December 10, 2011) - Revert the SSH tweaks made in 0.8.8. It affected stability ## 0.8.8 (December 1, 2011) - Mount shared folders shortest to longest to avoid mounting subfolders first. [GH-525] - Support for basic HTTP auth in the URL for boxes. - Solaris support for host only networks. [GH-533] - `vagrant init` respects `Vagrant::Environment` cwd. [GH-528] - `vagrant` commands will not output color when stdout is not a TTY. - Fix issue where `box_url` set with multiple VMs could cause issues. [GH-564] - Chef provisioners no longer depend on a "v-root" share being available. [GH-556] - NFS should work for FreeBSD hosts now. [GH-510] - SSH executed methods respect `config.ssh.max_tries`. [GH-508] - `vagrant box add` now respects the "no_proxy" environmental variable. [GH-502] - Tweaks that should make "Waiting for VM to boot" slightly more reliable. - Add comments to Vagrantfile to make it detected as Ruby file for `vi` and `emacs`. [GH-515] - More correct guest addition version checking. [GH-514] - Chef solo support on Windows is improved. [GH-542] - Put encrypted data bag secret into `/tmp` by default so that permissions are almost certainly guaranteed. [GH-512] ## 0.8.7 (September 13, 2011) - Fix regression with remote paths from chef-solo. [GH-431] - Fix issue where Vagrant crashes if `.vagrant` file becomes invalid. [GH-496] - Issue a warning instead of an error for attempting to forward a port <= 1024. [GH-487] ## 0.8.6 (August 28, 2011) - Fix issue with download progress not properly clearing the line. [GH-476] - NFS should work properly on Fedora. [GH-450] - Arguments can be specified to the `shell` provisioner via the `args` option. [GH-475] - Vagrant behaves much better when there are "inaccessible" VMs. [GH-453] ## 0.8.5 (August 15, 2011) Note: 0.8.3 and 0.8.4 was yanked due to RubyGems encoding issue. - Fix SSH `exec!` to inherit proper `$PATH`. [GH-426] - Chef client now accepts an empty (`nil`) run list again. [GH-429] - Fix incorrect error message when running `provision` on halted VM. [GH-447] - Checking guest addition versions now ignores OSE. [GH-438] - Chef solo from a remote URL fixed. [GH-431] - Arch linux support: host only networks and changing the host name. [GH-439] [GH-448] - Chef solo `roles_path` and `data_bags_path` can only be be single paths. [GH-446] - Fix `virtualbox_not_detected` error message to require 4.1.x. [GH-458] - Add shortname (`hostname -s`) for hostname setting on RHEL systems. [GH-456] - `vagrant ssh -c` output no longer has a prefix and respects newlines from the output. [GH-462] ## 0.8.2 (July 22, 2011) - Fix issue with SSH disconnects not reconnecting. - Fix chef solo simply not working with roles/data bags. [GH-425] - Multiple chef solo provisioners now work together. - Update Puppet provisioner so no deprecation warning is shown. [GH-421] - Removed error on "provisioner=" in config, as this has not existed for some time now. - Add better validation for networking. ## 0.8.1 (July 20, 2011) - Repush of 0.8.0 to fix a Ruby 1.9.2 RubyGems issue. ## 0.8.0 (July 20, 2011) - VirtualBox 4.1 support _only_. Previous versions of VirtualBox are supported by earlier versions of Vagrant. - Performance optimizations in `virtualbox` gem. Huge speed gains. - `:chef_server` provisioner is now `:chef_client`. [GH-359] - SSH connection is now cached after first access internally, speeding up `vagrant up`, `reload`, etc. quite a bit. - Actions which modify the VM now occur much more quickly, greatly speeding up `vagrant up`, `reload`, etc. - SUSE host only networking support. [GH-369] - Show nice error message for invalid HTTP responses for HTTP downloader. [GH-403] - New `:inline` option for shell provisioner to provide inline scripts as a string. [GH-395] - Host only network now properly works on multiple adapters. [GH-365] - Can now specify owner/group for regular shared folders. [GH-350] - `ssh_config` host name will use VM name if given. [GH-332] - `ssh` `-e` flag changed to `-c` to align with `ssh` standard behavior. [GH-323] - Forward agent and forward X11 settings properly appear in `ssh_config` output. [GH-105] - Chef JSON can now be set with `chef.json =` instead of the old `merge` technique. [GH-314] - Provisioner configuration is no longer cleared when the box needs to be downloaded during an `up`. [GH-308] - Multiple Chef provisioners no longer overwrite cookbook folders. [GH-407] - `package` won't delete previously existing file. [GH-408] - Vagrantfile can be lowercase now. [GH-399] - Only one copy of Vagrant may be running at any given time. [GH-364] - Default home directory for Vagrant moved to `~/.vagrant.d` [GH-333] - Specify a `forwarded_port_destination` for SSH configuration and SSH port searching will fall back to that if it can't find any other port. [GH-375] ## 0.7.8 (July 19, 2011) - Make sure VirtualBox version check verifies that it is 4.0.x. ## 0.7.7 (July 12, 2011) - Fix crashing bug with Psych and Ruby 1.9.2. [GH-411] ## 0.7.6 (July 2, 2011) - Run Chef commands in a single command. [GH-390] - Add `nfs` option for Chef to mount Chef folders via NFS. [GH-378] - Add translation for `aborted` state in VM. [GH-371] - Use full paths with the Chef provisioner so that restart cookbook will work. [GH-374] - Add "--no-color" as an argument and no colorized output will be used. [GH-379] - Added DEVICE option to the RedHat host only networking entry, which allows host only networking to work even if the VM has multiple NICs. [GH-382] - Touch the network configuration file for RedHat so that the `sed` works with host only networking. [GH-381] - Load prerelease versions of plugins if available. - Do not load a plugin if it depends on an invalid version of Vagrant. - Encrypted data bag support in Chef server provisioner. [GH-398] - Use the `-H` flag to set the proper home directory for `sudo`. [GH-370] ## 0.7.5 (May 16, 2011) - `config.ssh.port` can be specified and takes highest precedence if specified. Otherwise, Vagrant will still attempt to auto-detect the port. [GH-363] - Get rid of RubyGems deprecations introduced with RubyGems 1.8.x - Search in pre-release gems for plugins as well as release gems. - Support for Chef-solo `data_bags_path` [GH-362] - Can specify path to Chef binary using `binary_path` [GH-342] - Can specify additional environment data for Chef using `binary_env` [GH-342] ## 0.7.4 (May 12, 2011) - Chef environments support (for Chef 0.10) [GH-358] - Suppress the "added to known hosts" message for SSH [GH-354] - Ruby 1.8.6 support [GH-352] - Chef proxy settings now work for chef server [GH-335] ## 0.7.3 (April 19, 2011) - Retry all SSH on Net::SSH::Disconnect in case SSH is just restarting. [GH-313] - Add NFS shared folder support for Arch linux. [GH-346] - Fix issue with unknown terminal type output for sudo commands. - Forwarded port protocol can now be set as UDP. [GH-311] - Chef server file cache path and file backup path can be configured. [GH-310] - Setting hostname should work on Debian now. [GH-307] ## 0.7.2 (February 8, 2011) - Update JSON dependency to 1.5.1, which works with Ruby 1.9 on Windows. - Fix sudo issues on sudo < 1.7.0 (again). - Fix race condition in SSH, which specifically manifested itself in the chef server provisioner. [GH-295] - Change sudo shell to use `bash` (configurable). [GH-301] - Can now set mac address of host only network. [GH-294] - NFS shared folders with spaces now work properly. [GH-293] - Failed SSH commands now show output in error message. [GH-285] ## 0.7.1 (January 28, 2011) - Change error output with references to VirtualBox 3.2 to 4.0. - Internal SSH through net-ssh now uses `IdentitiesOnly` thanks to upstream net-ssh fix. - Fix issue causing warnings to show with `forwardx11` enabled for SSH. [GH-279] - FreeBSD support for host only networks, NFS, halting, etc. [GH-275] - Make SSH commands which use sudo compatible with sudo < 1.7.0. [GH-278] - Fix broken puppet server provisioner which called a nonexistent method. - Default SSH host changed from `localhost` to `127.0.0.1` since `localhost` is not always loopback. - New `shell` provisioner which simply uploads and executes a script as root on the VM. - Gentoo host only networking no longer fails if alrady setup. [GH-286] - Set the host name of your guest OS with `config.vm.host_name` [GH-273] - `vagrant ssh-config` now outputs the configured `config.ssh.host` ## 0.7.0 (January 19, 2011) - VirtualBox 4.0 support. Support for VirtualBox 3.2 is _dropped_, since the API is so different. Stay with the 0.6.x series if you have VirtualBox 3.2.x. - Puppet server provisioner. [GH-262] - Use numeric uid/gid in mounting shared folders to increase portability. [GH-252] - HTTP downloading follows redirects. [GH-163] - Downloaders have clearer output to note what they're doing. - Shared folders with no guest path are not automounted. [GH-184] - Boxes downloaded during `vagrant up` reload the Vagrantfile config, which fixes a problem with box settings not being properly loaded. [GH-231] - `config.ssh.forward_x11` to enable the ForwardX11 SSH option. [GH-255] - Vagrant source now has a `contrib` directory where contributions of miscellaneous addons for Vagrant will be added. - Vagrantfiles are now loaded only once (instead of 4+ times) [GH-238] - Ability to move home vagrant dir (~/.vagrant) by setting VAGRANT_HOME environmental variable. - Removed check and error for the "OSE" version of VirtualBox, since with VirtualBox 4 this distinction no longer exists. - Ability to specify proxy settings for chef. [GH-169] - Helpful error message shown if NFS mounting fails. [GH-135] - Gentoo guests now support host only networks. [GH-240] - RedHat (CentOS included) guests now support host only networks. [GH-260] - New Vagrantfile syntax for enabling and configuring provisioners. This change is not backwards compatible. [GH-265] - Provisioners are now RVM-friendly, meaning if you installed chef or puppet with an RVM managed Ruby, Vagrant now finds then. [GH-254] - Changed the unused host only network destroy mechanism to check for uselessness after the VM is destroyed. This should result in more accurate checks. - Networks are no longer disabled upon halt/destroy. With the above change, its unnecessary. - Puppet supports `module_path` configuration to mount local modules directory as a shared folder and configure puppet with it. [GH-270] - `ssh-config` now outputs `127.0.0.1` as the host instead of `localhost`. ## 0.6.9 (December 21, 2010) - Puppet provisioner. [GH-223] - Solaris system configurable to use `sudo`. - Solaris system registered, so it can be set with `:solaris`. - `vagrant package` include can be a directory name, which will cause the contents to be recursively copied into the package. [GH-241] - Arbitrary options to puppet binary can be set with `config.puppet.options`. [GH-242] - BSD hosts use proper GNU sed syntax for clearing NFS shares. [GH-243] - Enumerate VMs in a multi-VM environment in order they were defined. [GH-244] - Check for VM boot changed to use `timeout` library, which works better with Windows. - Show special error if VirtualBox not detected on 64-bit Windows. - Show error to Windows users attempting to use host only networking since it doesn't work yet. ## 0.6.8 (November 30, 2010) - Network interfaces are now up/down in distinct commands instead of just restarting "networking." [GH-192] - Add missing translation for chef binary missing. [GH-203] - Fix default settings for Opscode platform and comments. [GH-213] - Blank client name for chef server now uses FQDN by default, instead of "client" [GH-214] - Run list can now be nil, which will cause it to sync with chef server (when chef server is enabled). [GH-214] - Multiple NFS folders now work on linux. [GH-215] - Add translation for state "stuck" which is very rare. [GH-218] - virtualbox gem dependency minimum raised to 0.7.6 to verify FFI < 1.0.0 is used. - Fix issue where box downloading from `vagrant up` didn't reload the box collection. [GH-229] ## 0.6.7 (November 3, 2010) - Added validation to verify that a box is specified. - Proper error message when box is not found for `config.vm.box`. [GH-195] - Fix output of `vagrant status` with multi-vm to be correct. [GH-196] ## 0.6.6 (October 14, 2010) - `vagrant status NAME` works once again. [GH-191] - Conditional validation of Vagrantfile so that some commands don't validate. [GH-188] - Fix "junk" output for ssh-config. [GH-189] - Fix port collision handling with greater than two VMs. [GH-185] - Fix potential infinite loop with root path if bad CWD is given to environment. ## 0.6.5 (October 8, 2010) - Validations on base MAC address to avoid situation described in GH-166, GH-181 from ever happening again. - Properly load sub-VM configuration on first-pass of config loading. Solves a LOT of problems with multi-VM. [GH-166] [GH-181] - Configuration now only validates on final Vagrantfile proc, so multi-VM validates correctly. - A nice error message is given if ".vagrant" is a directory and therefore can't be accessed. [GH-172] - Fix plugin loading in a Rails 2.3.x project. [GH-176] ## 0.6.4 (October 4, 2010) - Default VM name is now properly the parent folder of the working directory of the environment. - Added method to `TestHelpers` to assist with testing new downloaders. - `up --no-provision` works again. This disables provisioning during the boot process. - Action warden doesn't do recovery process on `SystemExit` exceptions, allowing the double ctrl-C to work properly again. [related to GH-166] - Initial Vagrantfile is now heavily commented with various available options. [GH-171] - Box add checks if a box already exists before the download. [GH-170] - NFS no longer attempts to clean exports file if VM is not created, which was causing a stack trace during recovery. [related to GH-166] - Basic validation added for Chef configuration (both solo and server). - Top config class is now available in all `Vagrant::Config::Base` subclasses, which is useful for config validation. - Subcommand help shows proper full command in task listing. [GH-168] - SSH gives error message if `ssh` binary is not found. [GH-161] - SSH gives proper error message if VM is not running. [GH-167] - Fix some issues with undefined constants in command errors. ## 0.6.1, 0.6.2, 0.6.3 (September 27, 2010) A lot of quick releases which all were to fix issues with Ruby 1.8.7 compatibility. ## 0.6.0 (September 27, 2010) - VM name now defaults to the name of the containing folder, plus a timestamp. This should make it easier to identify VMs in the VirtualBox GUI. - Exposed Vagrant test helpers in `Vagrant::TestHelpers` for plugins to easily test themselves against Vagrant environments. - **Plugins** have landed. Plugins are simply gems which have a `vagrant_init.rb` file somewhere in their load path. Please read the documentation on vagrantup.com before attempting to create a plugin (which is very easy) for more information on how it all works and also some guidelines. - `vagrant package` now takes a `--vagrantfile` option to specify a Vagrantfile to package. The `--include` approach for including a Vagrantfile no longer works (previously built boxes will continue to work). - `vagrant package` has new logic with regards to the `--include` option depending on if the file path is relative or absolute (they can be intermixed): * _Relative_ paths are copied directly into the box, preserving their path. So `--include lib/foo` would be in the box as "lib/foo" * _Absolute_ paths are simply copied files into the root of the box. So `--include /lib/foo` would be in the box as "foo" - "vagrant_main" is no longer the default run list. Instead, chef run list starts empty. It is up to you to specify all recipes in the Vagrantfile now. - Fixed various issues with certain action middleware not working if the VM was not created. - SSH connection is retried 5 times if there is a connection refused. Related to GH-140. - If `http_proxy` environmental variable is set, it will be used as the proxy box adding via http. - Remove `config.ssh.password`. It hasn't been used for a few versions now and was only kept around to avoid exceptions in Vagrantfiles. - Configuration is now validated so improper input can be found in Vagrantfiles. - Fixed issue with not detecting Vagrantfile at root directory ("/"). - Vagrant now gives a nice error message if there is a syntax error in any Vagrantfile. [GH-154] - The format of the ".vagrant" file which stores persisted VMs has changed. This is **backwards incompatible**. Will provide an upgrade utility prior to 0.6 launch. - Every [expected] Vagrant error now exits with a clean error message and a unique exit status, and raises a unique exception (if you're scripting Vagrant). - Added I18n gem dependency for pulling strings into clean YML files. Vagrant is now localizable as a side effect! Translations welcome. - Fixed issue with "Waiting for cleanup" message appearing twice in some cases. [GH-145] - Converted CLI to use Thor. As a tradeoff, there are some backwards incompatibilities: * `vagrant package` - The `--include` flag now separates filenames by spaces, instead of by commas. e.g. `vagrant package --include x y z` * `vagrant ssh` - If you specify a command to execute using the `--execute` flag, you may now only specify one command (before you were able to specify an arbitrary amount). e.g. `vagrant ssh -e "echo hello"` * `vagrant ssh-config` has become `vagrant ssh_config` due to a limitation in Thor. ## 0.5.4 (September 7, 2010) - Fix issue with the "exec failed" by running on Tiger as well. - Give an error when downloading a box which already exists prior to actually downloading the box. ## 0.5.3 (August 23, 2010) - Add erubis as a dependency since its rendering of `erb` is sane. - Fixed poorly formatted Vagrantfile after `vagrant init`. [GH-142] - Fixed NFS not working properly with multiple NFS folders. - Fixed chef solo provision to work on Windows. It was expanding a linux path which prepended a drive letter onto it. ## 0.5.2 (August 3, 2010) - `vagrant up` can be used as a way to resume the VM as well (same as `vagrant resume`). [GH-134] - Sudo uses "-E" flag to preserve environment for chef provisioners. This fixes issues with CentOS. [GH-133] - Added "IdentitiesOnly yes" to options when `vagrant ssh` is run to avoid "Too Many Authentication Failures" error. [GH-131] - Fix regression with `package` not working. [GH-132] - Added ability to specify box url in `init`, which populates the Vagrantfile with the proper `config.vm.box_url`. ## 0.5.1 (July 31, 2010) - Allow specifying cookbook paths which exist only on the VM in `config.chef.cookbooks_path`. This is used for specifying cookbook paths when `config.chef.recipe_url` is used. [GH-130] See updated chef solo documentation for more information on this. - No longer show "Disabling host only networks..." if no host only networks are destroyed. Quiets `destroy`, `halt`, etc output a bit. - Updated getting started guide to be more up to date and generic. [GH-125] - Fixed error with doing a `vagrant up` when no Vagrantfile existed. [GH-128] - Fixed NFS erroring when NFS wasn't even enabled if `/etc/exports` doesn't exist. [GH-126] - Fixed `vagrant resume` to properly resume a suspended VM. [GH-122] - Fixed `halt`, `destroy`, `reload` to where they failed if the VM was in a saved state. [GH-123] - Added `config.chef.recipe_url` which allows you to specify a URL to a gzipped tar file for chef solo to download cookbooks. See the [chef-solo docs](http://wiki.opscode.com/display/chef/Chef+Solo#ChefSolo-RunningfromaURL) for more information. [GH-121] - Added `vagrant box repackage` which repackages boxes which have been added. This is useful in case you want to redistribute a base box you have but may have lost the actual "box" file. [GH-120] ## Previous The changelog began with version 0.5.1 so any changes prior to that can be seen by checking the tagged releases and reading git commit messages. vagrant-1.4.3/CONTRIBUTING.md000066400000000000000000000041471226132634600154250ustar00rootroot00000000000000# How to contribute We like to encourage you to contribute to the repository. This should be as easy as possible for you but there are a few things to consider when contributing. The following guidelines for contribution should be followed if you want to submit a pull request. ## How to prepare * You need a [GitHub account](https://github.com/signup/free) * Submit an [issue ticket](https://github.com/mitchellh/vagrant/issues) for your issue if the is no one yet. * Describe the issue and include steps to reproduce when it's a bug. * Ensure to mention the earliest version that you know is affected. * If you plan on submitting a bug report, please submit debug-level logs along with the report using [gist](https://gist.github.com/) or some other paste service by prepending `VAGRANT_LOG=debug` to your `vagrant` commands. * Fork the repository on GitHub ## Make Changes * In your forked repository, create a topic branch for your upcoming patch. * Usually this is based on the master branch. * Create a branch based on master; `git branch fix/master/my_contribution master` then checkout the new branch with `git checkout fix/master/my_contribution`. Please avoid working directly on the `master` branch. * Make commits of logical units and describe them properly. * Check for unnecessary whitespace with `git diff --check` before committing. * If possible, submit tests to your patch / new feature so it can be tested easily. * Assure nothing is broken by running all the tests. ## Submit Changes * Push your changes to a topic branch in your fork of the repository. * Open a pull request to the original repository and choose the right original branch you want to patch. * If not done in commit messages (which you really should do) please reference and update your issue with the code changes. * Even if you have write access to the repository, do not directly push or merge pull-requests. Let another team member review your pull request and approve. # Additional Resources * [General GitHub documentation](http://help.github.com/) * [GitHub pull request documentation](http://help.github.com/send-pull-requests/) vagrant-1.4.3/Gemfile000066400000000000000000000003521226132634600144610ustar00rootroot00000000000000source "http://rubygems.org" gemspec if File.exist?(File.expand_path("../../vagrant-spec", __FILE__)) gem 'vagrant-spec', path: "../vagrant-spec" else gem 'vagrant-spec', git: "https://github.com/mitchellh/vagrant-spec.git" end vagrant-1.4.3/LICENSE000066400000000000000000000020741226132634600141760ustar00rootroot00000000000000The MIT License Copyright (c) 2010-2013 Mitchell Hashimoto Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. vagrant-1.4.3/README.md000066400000000000000000000055161226132634600144540ustar00rootroot00000000000000# Vagrant * Website: [http://www.vagrantup.com](http://www.vagrantup.com) * Source: [https://github.com/mitchellh/vagrant](https://github.com/mitchellh/vagrant) * IRC: `#vagrant` on Freenode * Mailing list: [Google Groups](http://groups.google.com/group/vagrant-up) Vagrant is a tool for building and distributing development environments. Vagrant provides the framework and configuration format to create and manage complete portable development environments. These development environments can live on your computer or in the cloud, and are portable between Windows, Mac OS X, and Linux. ## Quick Start First, make sure your development machine has [VirtualBox](http://www.virtualbox.org) installed. After this, [download and install the appropriate Vagrant package for your OS](http://www.vagrantup.com/downloads). If you're not on Mac OS X or Windows, you'll need to add `/opt/vagrant/bin` to your `PATH`. After this, you're ready to go! To build your first virtual environment: vagrant init precise32 http://files.vagrantup.com/precise32.box vagrant up Note: The above `vagrant up` command will also trigger Vagrant to download the `precise32` box via the specified URL. Vagrant only does this if it detects that the box doesn't already exist on your system. ## Getting Started Guide To learn how to build a fully functional rails development environment, view the [getting started guide](http://docs.vagrantup.com/v2/getting-started/index.html). ## Installing the Gem from Git If you want the bleeding edge version of Vagrant, we try to keep master pretty stable and you're welcome to give it a shot. The following is an example showing how to do this: rake install Ruby 2.0 is needed. ## Contributing to Vagrant ### Dependencies and Unit Tests To hack on vagrant, you'll need [bundler](http://github.com/carlhuda/bundler) which can be installed with a simple `gem install bundler`. Afterwords, do the following: bundle install rake This will run the unit test suite, which should come back all green! Then you're good to go! If you want to run Vagrant without having to install the gem, you may use `bundle exec`, like so: bundle exec vagrant help ### Acceptance Tests Vagrant also comes with an acceptance test suite that does black-box tests of various Vagrant components. Note that these tests are **extremely slow** because actual VMs are spun up and down. The full test suite can take hours. Instead, try to run focused component tests. To run the acceptance test suite, first copy `vagrant-spec.config.example.rb` to `vagrant-spec.config.rb` and modify it to valid values. The places you should fill in are clearly marked. Next, see the components that can be tested: ``` $ rake acceptance:components cli provider/virtualbox/basic ... ``` Then, run one of those components: ``` $ rake acceptance:run COMPONENTS="cli" ... ``` vagrant-1.4.3/Rakefile000066400000000000000000000010521226132634600146310ustar00rootroot00000000000000require 'rubygems' require 'bundler/setup' # Immediately sync all stdout so that tools like buildbot can # immediately load in the output. $stdout.sync = true $stderr.sync = true # Load all the rake tasks from the "tasks" folder. This folder # allows us to nicely separate rake tasks into individual files # based on their role, which makes development and debugging easier # than one monolithic file. task_dir = File.expand_path("../tasks", __FILE__) Dir["#{task_dir}/**/*.rake"].each do |task_file| load task_file end task :default => "test:unit" vagrant-1.4.3/bin/000077500000000000000000000000001226132634600137365ustar00rootroot00000000000000vagrant-1.4.3/bin/vagrant000077500000000000000000000103121226132634600153230ustar00rootroot00000000000000#!/usr/bin/env ruby # Trap interrupts to quit cleanly. This will be overriden at some point # by Vagrant. This is made to catch any interrupts while Vagrant is # initializing which have historically resulted in stack traces. Signal.trap("INT") { exit 1 } # Split arguments by "--" if its there, we'll recombine them later argv = ARGV.dup argv_extra = [] if idx = argv.index("--") argv_extra = argv.slice(idx+1, argv.length-2) argv = argv.slice(0, idx) end # Set logging level to `debug`. This is done before loading 'vagrant', as it # sets up the logging system. if argv.include?("--debug") argv.delete("--debug") ENV["VAGRANT_LOG"] = "debug" end require 'log4r' require 'vagrant' require 'vagrant/cli' require 'vagrant/util/platform' # Create a logger right away logger = Log4r::Logger.new("vagrant::bin::vagrant") logger.info("`vagrant` invoked: #{ARGV.inspect}") # Stdout/stderr should not buffer output $stdout.sync = true $stderr.sync = true # These will be the options that are passed to initialze the Vagrant # environment. opts = {} # Disable color in a few cases: # # * --no-color is anywhere in our arguments # * STDOUT is not a TTY # * The terminal doesn't support colors (Windows) # if argv.include?("--no-color") || ENV["VAGRANT_NO_COLOR"] # Delete the argument from the list so that it doesn't # cause any invalid arguments down the road. argv.delete("--no-color") opts[:ui_class] = Vagrant::UI::Basic elsif !Vagrant::Util::Platform.terminal_supports_colors? opts[:ui_class] = Vagrant::UI::Basic elsif !$stdout.tty? && !Vagrant::Util::Platform.cygwin? # Cygwin always reports STDOUT is not a TTY, so we only disable # colors if its not a TTY AND its not Cygwin. opts[:ui_class] = Vagrant::UI::Basic end # Also allow users to force colors. if argv.include?("--color") argv.delete("--color") opts[:ui_class] = Vagrant::UI::Colored end # Highest precedence is if we have enabled machine-readable output if argv.include?("--machine-readable") argv.delete("--machine-readable") opts[:ui_class] = Vagrant::UI::MachineReadable end # Default to colored output opts[:ui_class] ||= Vagrant::UI::Colored # This is kind of hacky, and I'd love to find a better way to do this, but # if we're accessing the plugin interface, we want to NOT load plugins # for this run, because they can actually interfere with the function # of the plugin interface. argv.each do |arg| if !arg.start_with?("-") if arg == "plugin" ENV["VAGRANT_NO_PLUGINS"] = "1" ENV["VAGRANT_VAGRANTFILE"] = "plugin_command_#{Time.now.to_i}" end break end end # Fast path the version of Vagrant if argv.include?("-v") || argv.include?("--version") puts "Vagrant #{Vagrant::VERSION}" exit 0 end # Recombine the arguments argv << "--" argv += argv_extra env = nil begin # Create the environment, which is the cwd of wherever the # `vagrant` command was invoked from logger.debug("Creating Vagrant environment") env = Vagrant::Environment.new(opts) if !Vagrant.in_installer? warned = false # If we're in a bundler environment, we assume it is for plugin # development and will let the user know that. if defined?(Bundler) require 'bundler/shared_helpers' if Bundler::SharedHelpers.in_bundle? env.ui.warn(I18n.t("vagrant.general.in_bundler")) env.ui.warn("") warned = true end end # If we're not in the installer, warn. env.ui.warn(I18n.t("vagrant.general.not_in_installer")) if !warned end begin # Execute the CLI interface, and exit with the proper error code exit_status = env.cli(argv) ensure # Unload the environment so cleanup can be done env.unload end # Exit with the exit status from our CLI command exit(exit_status) rescue Vagrant::Errors::VagrantError => e logger.error("Vagrant experienced an error! Details:") logger.error(e.inspect) logger.error(e.message) logger.error(e.backtrace.join("\n")) if env opts = { :prefix => false } env.ui.error e.message, opts if e.message else $stderr.puts "Vagrant failed to initialize at a very early stage:\n\n" $stderr.puts e.message end exit e.status_code if e.respond_to?(:status_code) exit 999 # An error occurred with no status code defined end vagrant-1.4.3/config/000077500000000000000000000000001226132634600144335ustar00rootroot00000000000000vagrant-1.4.3/config/default.rb000066400000000000000000000015161226132634600164070ustar00rootroot00000000000000Vagrant.configure("2") do |config| config.vagrant.host = :detect config.ssh.forward_agent = false config.ssh.forward_x11 = false config.ssh.guest_port = 22 config.ssh.keep_alive = true config.ssh.shell = "bash -l" config.ssh.default.username = "vagrant" config.vm.usable_port_range = (2200..2250) config.vm.box_url = nil config.vm.base_mac = nil config.vm.boot_timeout = 300 config.vm.graceful_halt_timeout = 60 # Share SSH locally by default config.vm.network :forwarded_port, guest: 22, host: 2222, host_ip: "127.0.0.1", id: "ssh", auto_correct: true # Share the root folder. This can then be overridden by # other Vagrantfiles, if they wish. config.vm.synced_folder ".", "/vagrant" config.nfs.map_uid = :auto config.nfs.map_gid = :auto config.package.name = 'package.box' end vagrant-1.4.3/contrib/000077500000000000000000000000001226132634600146265ustar00rootroot00000000000000vagrant-1.4.3/contrib/README.md000066400000000000000000000006441226132634600161110ustar00rootroot00000000000000# Vagrant Contrib Miscellaneous contributions which assist people in using Vagrant will make their way into this directory. An up-to-date list of short descriptions for each item will be kept below. ## List of Contrib Items * `emacs` - Contains a file showing how to associate `Vagrantfile` with Ruby syntax highlighting. * `vim` - Contains a `.vim` file for enabling Ruby syntax highlighting for `Vagrantfile`s. vagrant-1.4.3/contrib/bash/000077500000000000000000000000001226132634600155435ustar00rootroot00000000000000vagrant-1.4.3/contrib/bash/completion.sh000066400000000000000000000016651226132634600202600ustar00rootroot00000000000000# Autocompletion for Vagrant just put this line in your ~/.profile or link this file into it like: # source /path/to/vagrant/contrib/bash/completion.sh _vagrant() { cur="${COMP_WORDS[COMP_CWORD]}" prev="${COMP_WORDS[COMP_CWORD-1]}" preprev="${COMP_WORDS[COMP_CWORD-2]}" commands=$(vagrant --help | awk '/^ /{print $1}') if [ $COMP_CWORD == 1 ] ; then COMPREPLY=( $(compgen -W "${commands}" -- ${cur}) ) return 0 fi if [ $COMP_CWORD == 2 ] ; then local sub_commands=$(vagrant $prev --help | awk '/^ /{print $1}') COMPREPLY=( $(compgen -W "${sub_commands}" -- ${cur}) ) return 0 fi if [[ ${cur} == -* ]] ; then local command_opts=$(vagrant $preprev $prev --help | grep -E -o "((-\w{1}|--(\w|-)*=?)){1,2}") COMPREPLY=( $(compgen -W "${command_opts}" -- ${cur}) ) return 0 fi } complete -F _vagrant vagrant # /* vim: set filetype=sh : */ vagrant-1.4.3/contrib/emacs/000077500000000000000000000000001226132634600157165ustar00rootroot00000000000000vagrant-1.4.3/contrib/emacs/vagrant.el000066400000000000000000000006441226132634600177060ustar00rootroot00000000000000;;-------------------------------------------------------------------- ;; Teach emacs to syntax highlight Vagrantfile as Ruby. ;; ;; Installation: Copy the line below into your emacs configuration, ;; or drop this file anywhere in your "~/.emacs.d" directory and be ;; sure to "load" it. ;;-------------------------------------------------------------------- (add-to-list 'auto-mode-alist '("Vagrantfile$" . ruby-mode)) vagrant-1.4.3/contrib/vim/000077500000000000000000000000001226132634600154215ustar00rootroot00000000000000vagrant-1.4.3/contrib/vim/vagrantfile.vim000066400000000000000000000003431226132634600204400ustar00rootroot00000000000000" Teach vim to syntax highlight Vagrantfile as ruby " " Install: $HOME/.vim/plugin/vagrant.vim " Author: Brandon Philips augroup vagrant au! au BufRead,BufNewFile Vagrantfile set filetype=ruby augroup END vagrant-1.4.3/keys/000077500000000000000000000000001226132634600141415ustar00rootroot00000000000000vagrant-1.4.3/keys/README.md000066400000000000000000000007201226132634600154170ustar00rootroot00000000000000# Insecure Keypair These keys are the "insecure" public/private keypair we offer to [base box creators](http://docs.vagrantup.com/v1/docs/base_boxes.html) for use in their base boxes so that vagrant installations can automatically SSH into the boxes. If you're working with a team or company or with a custom box and you want more secure SSH, you should create your own keypair and configure the private key in the Vagrantfile with `config.ssh.private_key_path` vagrant-1.4.3/keys/vagrant000066400000000000000000000032131226132634600155250ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2 hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1 Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf 4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX 3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj 3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+ dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz 6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH +vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s= -----END RSA PRIVATE KEY----- vagrant-1.4.3/keys/vagrant.pub000066400000000000000000000006311226132634600163130ustar00rootroot00000000000000ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key vagrant-1.4.3/lib/000077500000000000000000000000001226132634600137345ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant.rb000066400000000000000000000262341226132634600157320ustar00rootroot00000000000000require 'log4r' require 'rubygems' # Enable logging if it is requested. We do this before # anything else so that we can setup the output before # any logging occurs. if ENV["VAGRANT_LOG"] && ENV["VAGRANT_LOG"] != "" # Require Log4r and define the levels we'll be using require 'log4r/config' Log4r.define_levels(*Log4r::Log4rConfig::LogLevels) level = nil begin level = Log4r.const_get(ENV["VAGRANT_LOG"].upcase) rescue NameError # This means that the logging constant wasn't found, # which is fine. We just keep `level` as `nil`. But # we tell the user. level = nil end # Some constants, such as "true" resolve to booleans, so the # above error checking doesn't catch it. This will check to make # sure that the log level is an integer, as Log4r requires. level = nil if !level.is_a?(Integer) if !level # We directly write to stderr here because the VagrantError system # is not setup yet. $stderr.puts "Invalid VAGRANT_LOG level is set: #{ENV["VAGRANT_LOG"]}" $stderr.puts "" $stderr.puts "Please use one of the standard log levels: debug, info, warn, or error" exit 1 end # Set the logging level on all "vagrant" namespaced # logs as long as we have a valid level. if level logger = Log4r::Logger.new("vagrant") logger.outputters = Log4r::Outputter.stderr logger.level = level logger = nil end end require 'json' require 'pathname' require 'stringio' require 'childprocess' require 'i18n' # OpenSSL must be loaded here since when it is loaded via `autoload` # there are issues with ciphers not being properly loaded. require 'openssl' # Always make the version available require 'vagrant/version' global_logger = Log4r::Logger.new("vagrant::global") global_logger.info("Vagrant version: #{Vagrant::VERSION}") global_logger.info("Ruby version: #{RUBY_VERSION}") global_logger.info("RubyGems version: #{Gem::VERSION}") ENV.each do |k, v| global_logger.info("#{k}=#{v.inspect}") if k =~ /^VAGRANT_/ end # We need these components always so instead of an autoload we # just require them explicitly here. require "vagrant/registry" module Vagrant autoload :Action, 'vagrant/action' autoload :BatchAction, 'vagrant/batch_action' autoload :Box, 'vagrant/box' autoload :BoxCollection, 'vagrant/box_collection' autoload :CLI, 'vagrant/cli' autoload :Command, 'vagrant/command' autoload :Config, 'vagrant/config' autoload :Driver, 'vagrant/driver' autoload :Environment, 'vagrant/environment' autoload :Errors, 'vagrant/errors' autoload :Guest, 'vagrant/guest' autoload :Hosts, 'vagrant/hosts' autoload :Machine, 'vagrant/machine' autoload :MachineState, 'vagrant/machine_state' autoload :Plugin, 'vagrant/plugin' autoload :UI, 'vagrant/ui' autoload :Util, 'vagrant/util' # These are the various plugin versions and their components in # a lazy loaded Hash-like structure. PLUGIN_COMPONENTS = Registry.new.tap do |c| c.register(:"1") { Plugin::V1::Plugin } c.register([:"1", :command]) { Plugin::V1::Command } c.register([:"1", :communicator]) { Plugin::V1::Communicator } c.register([:"1", :config]) { Plugin::V1::Config } c.register([:"1", :guest]) { Plugin::V1::Guest } c.register([:"1", :host]) { Plugin::V1::Host } c.register([:"1", :provider]) { Plugin::V1::Provider } c.register([:"1", :provisioner]) { Plugin::V1::Provisioner } c.register(:"2") { Plugin::V2::Plugin } c.register([:"2", :command]) { Plugin::V2::Command } c.register([:"2", :communicator]) { Plugin::V2::Communicator } c.register([:"2", :config]) { Plugin::V2::Config } c.register([:"2", :guest]) { Plugin::V2::Guest } c.register([:"2", :host]) { Plugin::V2::Host } c.register([:"2", :provider]) { Plugin::V2::Provider } c.register([:"2", :provisioner]) { Plugin::V2::Provisioner } c.register([:"2", :synced_folder]) { Plugin::V2::SyncedFolder } end # This returns a true/false showing whether we're running from the # environment setup by the Vagrant installers. # # @return [Boolean] def self.in_installer? !!ENV["VAGRANT_INSTALLER_ENV"] end # The source root is the path to the root directory of # the Vagrant gem. def self.source_root @source_root ||= Pathname.new(File.expand_path('../../', __FILE__)) end # Configure a Vagrant environment. The version specifies the version # of the configuration that is expected by the block. The block, based # on that version, configures the environment. # # Note that the block isn't run immediately. Instead, the configuration # block is stored until later, and is run when an environment is loaded. # # @param [String] version Version of the configuration def self.configure(version, &block) Config.run(version, &block) end # This checks if a plugin with the given name is installed. This can # be used from the Vagrantfile to easily branch based on plugin # availability. def self.has_plugin?(name) manager = plugin("2").manager manager.required.any? { |gem_name| gem_name == name } || manager.registered.any? { |plugin| plugin.name == name } end # Returns a superclass to use when creating a plugin for Vagrant. # Given a specific version, this returns a proper superclass to use # to register plugins for that version. # # Optionally, if you give a specific component, then it will return # the proper superclass for that component as well. # # Plugins and plugin components should subclass the classes returned by # this method. This method lets Vagrant core control these superclasses # and change them over time without affecting plugins. For example, if # the V1 superclass happens to be "Vagrant::V1," future versions of # Vagrant may move it to "Vagrant::Plugins::V1" and plugins will not be # affected. # # @param [String] version # @param [String] component # @return [Class] def self.plugin(version, component=nil) # Build up the key and return a result key = version.to_s.to_sym key = [key, component.to_s.to_sym] if component result = PLUGIN_COMPONENTS.get(key) # If we found our component then we return that return result if result # If we didn't find a result, then raise an exception, depending # on if we got a component or not. raise ArgumentError, "Plugin superclass not found for version/component: " + "#{version} #{component}" end # This should be used instead of Ruby's built-in `require` in order to # load a Vagrant plugin. This will load the given plugin by first doing # a normal `require`, giving a nice error message if things go wrong, # and second by verifying that a Vagrant plugin was actually defined in # the process. # # @param [String] name Name of the plugin to load. def self.require_plugin(name) logger = Log4r::Logger.new("vagrant::root") if ENV["VAGRANT_NO_PLUGINS"] logger.warn("VAGRANT_NO_PLUGINS is set, not loading 3rd party plugin: #{name}") return end # Redirect stdout/stderr so that we can output it in our own way. previous_stderr = $stderr previous_stdout = $stdout $stderr = StringIO.new $stdout = StringIO.new # Attempt the normal require begin require name plugin("2").manager.plugin_required(name) rescue Exception => e # Since this is a rare case, we create a one-time logger here # in order to output the error logger.error("Failed to load plugin: #{name}") logger.error(" -- Error: #{e.inspect}") logger.error(" -- Backtrace:") logger.error(e.backtrace.join("\n")) # If it is a LoadError we first try to see if it failed loading # the top-level entrypoint. If so, then we report a different error. if e.is_a?(LoadError) # Parse the message in order to get what failed to load, and # add some extra protection around if the message is different. parts = e.to_s.split(" -- ", 2) if parts.length == 2 && parts[1] == name raise Errors::PluginLoadError, :plugin => name end end # Get the string data out from the stdout/stderr captures stderr = $stderr.string stdout = $stdout.string if !stderr.empty? || !stdout.empty? raise Errors::PluginLoadFailedWithOutput, :plugin => name, :stderr => stderr, :stdout => stdout end # And raise an error itself raise Errors::PluginLoadFailed, :plugin => name end # Log plugin version gem = Gem::Specification.find { |spec| spec.name == name } version = gem ? gem.version : "" logger.info("Loaded plugin #{name}, version #{version}") ensure $stderr = previous_stderr if previous_stderr $stdout = previous_stdout if previous_stdout end # This allows a Vagrantfile to specify the version of Vagrant that is # required. You can specify a list of requirements which will all be checked # against the running Vagrant version. # # This should be specified at the _top_ of any Vagrantfile. # # Examples are shown below: # # Vagrant.require_version(">= 1.3.5") # Vagrant.require_version(">= 1.3.5", "< 1.4.0") # Vagrant.require_version("~> 1.3.5") # def self.require_version(*requirements) logger = Log4r::Logger.new("vagrant::root") logger.info("Version requirements from Vagrantfile: #{requirements.inspect}") req = Gem::Requirement.new(*requirements) if req.satisfied_by?(Gem::Version.new(VERSION)) logger.info(" - Version requirements satisfied!") return end raise Errors::VagrantVersionBad, requirements: requirements.join(", "), version: VERSION end end # Default I18n to load the en locale I18n.load_path << File.expand_path("templates/locales/en.yml", Vagrant.source_root) if I18n.config.respond_to?(:enforce_available_locales=) # Make sure only available locales are used. This will be the default in the # future but we need this to silence a deprecation warning from 0.6.9 I18n.config.enforce_available_locales = true end # A lambda that knows how to load plugins from a single directory. plugin_load_proc = lambda do |directory| # We only care about directories next false if !directory.directory? # If there is a plugin file in the top-level directory, then load # that up. plugin_file = directory.join("plugin.rb") if plugin_file.file? global_logger.debug("Loading core plugin: #{plugin_file}") load(plugin_file) next true end end # Go through the `plugins` directory and attempt to load any plugins. The # plugins are allowed to be in a directory in `plugins` or at most one # directory deep within the plugins directory. So a plugin can be at # `plugins/foo` or also at `plugins/foo/bar`, but no deeper. Vagrant.source_root.join("plugins").children(true).each do |directory| # Ignore non-directories next if !directory.directory? # Load from this directory, and exit if we successfully loaded a plugin next if plugin_load_proc.call(directory) # Otherwise, attempt to load from sub-directories directory.children(true).each(&plugin_load_proc) end vagrant-1.4.3/lib/vagrant/000077500000000000000000000000001226132634600153765ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/action.rb000066400000000000000000000047071226132634600172100ustar00rootroot00000000000000require 'vagrant/action/builder' module Vagrant module Action autoload :Runner, 'vagrant/action/runner' autoload :Warden, 'vagrant/action/warden' # Builtin contains middleware classes that are shipped with Vagrant-core # and are thus available to all plugins as a "standard library" of sorts. module Builtin autoload :BoxAdd, "vagrant/action/builtin/box_add" autoload :BoxRemove, "vagrant/action/builtin/box_remove" autoload :Call, "vagrant/action/builtin/call" autoload :Confirm, "vagrant/action/builtin/confirm" autoload :ConfigValidate, "vagrant/action/builtin/config_validate" autoload :DestroyConfirm, "vagrant/action/builtin/destroy_confirm" autoload :EnvSet, "vagrant/action/builtin/env_set" autoload :GracefulHalt, "vagrant/action/builtin/graceful_halt" autoload :HandleBoxUrl, "vagrant/action/builtin/handle_box_url" autoload :HandleForwardedPortCollisions, "vagrant/action/builtin/handle_forwarded_port_collisions" autoload :Lock, "vagrant/action/builtin/lock" autoload :Provision, "vagrant/action/builtin/provision" autoload :ProvisionerCleanup, "vagrant/action/builtin/provisioner_cleanup" autoload :SetHostname, "vagrant/action/builtin/set_hostname" autoload :SSHExec, "vagrant/action/builtin/ssh_exec" autoload :SSHRun, "vagrant/action/builtin/ssh_run" autoload :SyncedFolders, "vagrant/action/builtin/synced_folders" autoload :SyncedFolderCleanup, "vagrant/action/builtin/synced_folder_cleanup" autoload :WaitForCommunicator, "vagrant/action/builtin/wait_for_communicator" end module General autoload :Package, 'vagrant/action/general/package' end # This is the action that will add a box from a URL. This middleware # sequence is built-in to Vagrant. Plugins can hook into this like any # other middleware sequence. This is particularly useful for provider # plugins, which can hook in to do things like verification of boxes # that are downloaded. def self.action_box_add Builder.new.tap do |b| b.use Builtin::BoxAdd end end # This is the action that will remove a box given a name (and optionally # a provider). This middleware sequence is built-in to Vagrant. Plugins # can hook into this like any other middleware sequence. def self.action_box_remove Builder.new.tap do |b| b.use Builtin::BoxRemove end end end end vagrant-1.4.3/lib/vagrant/action/000077500000000000000000000000001226132634600166535ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/action/builder.rb000066400000000000000000000124621226132634600206330ustar00rootroot00000000000000module Vagrant module Action # Action builder which provides a nice DSL for building up # a middleware sequence for Vagrant actions. This code is based # heavily off of `Rack::Builder` and `ActionDispatch::MiddlewareStack` # in Rack and Rails, respectively. # # Usage # # Building an action sequence is very easy: # # app = Vagrant::Action::Builder.new.tap do |b| # b.use MiddlewareA # b.use MiddlewareB # end # # Vagrant::Action.run(app) # class Builder # This is the stack of middlewares added. This should NOT be used # directly. # # @return [Array] attr_reader :stack # This is a shortcut for a middleware sequence with only one item # in it. For a description of the arguments and the documentation, please # see {#use} instead. # # @return [Builder] def self.build(middleware, *args, &block) new.use(middleware, *args, &block) end def initialize @stack = [] end # Implement a custom copy that copies the stack variable over so that # we don't clobber that. def initialize_copy(original) super @stack = original.stack.dup end # Returns a mergeable version of the builder. If `use` is called with # the return value of this method, then the stack will merge, instead # of being treated as a separate single middleware. def flatten lambda do |env| self.call(env) end end # Adds a middleware class to the middleware stack. Any additional # args and a block, if given, are saved and passed to the initializer # of the middleware. # # @param [Class] middleware The middleware class def use(middleware, *args, &block) if middleware.kind_of?(Builder) # Merge in the other builder's stack into our own self.stack.concat(middleware.stack) else self.stack << [middleware, args, block] end self end # Inserts a middleware at the given index or directly before the # given middleware object. def insert(index, middleware, *args, &block) index = self.index(index) unless index.is_a?(Integer) raise "no such middleware to insert before: #{index.inspect}" unless index if middleware.kind_of?(Builder) middleware.stack.reverse.each do |stack_item| stack.insert(index, stack_item) end else stack.insert(index, [middleware, args, block]) end end alias_method :insert_before, :insert # Inserts a middleware after the given index or middleware object. def insert_after(index, middleware, *args, &block) index = self.index(index) unless index.is_a?(Integer) raise "no such middleware to insert after: #{index.inspect}" unless index insert(index + 1, middleware, *args, &block) end # Replaces the given middlware object or index with the new # middleware. def replace(index, middleware, *args, &block) if index.is_a?(Integer) delete(index) insert(index, middleware, *args, &block) else insert_before(index, middleware, *args, &block) delete(index) end end # Deletes the given middleware object or index def delete(index) index = self.index(index) unless index.is_a?(Integer) stack.delete_at(index) end # Runs the builder stack with the given environment. def call(env) to_app(env).call(env) end # Returns the numeric index for the given middleware object. # # @param [Object] object The item to find the index for # @return [Integer] def index(object) stack.each_with_index do |item, i| return i if item[0] == object return i if item[0].respond_to?(:name) && item[0].name == object end nil end # Converts the builder stack to a runnable action sequence. # # @param [Hash] env The action environment hash # @return [Object] A callable object def to_app(env) app_stack = nil # If we have action hooks, then we apply them if env[:action_hooks] builder = self.dup # These are the options to pass into hook application. options = {} # If we already ran through once and did append/prepends, # then don't do it again. if env[:action_hooks_already_ran] options[:no_prepend_or_append] = true end # Specify that we already ran, so in the future we don't repeat # the prepend/append hooks. env[:action_hooks_already_ran] = true # Apply all the hooks to the new builder instance env[:action_hooks].each do |hook| hook.apply(builder, options) end # The stack is now the result of the new builder app_stack = builder.stack.dup end # If we don't have a stack then default to using our own app_stack ||= stack.dup # Wrap the middleware stack with the Warden to provide a consistent # and predictable behavior upon exceptions. Warden.new(app_stack, env) end end end end vagrant-1.4.3/lib/vagrant/action/builtin/000077500000000000000000000000001226132634600203215ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/action/builtin/box_add.rb000066400000000000000000000166651226132634600222640ustar00rootroot00000000000000require "digest/sha1" require "log4r" require "vagrant/util/downloader" require "vagrant/util/file_checksum" require "vagrant/util/platform" module Vagrant module Action module Builtin # This middleware will download a remote box and add it to the # given box collection. class BoxAdd def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::box_add") end def call(env) @download_interrupted = false box_name = env[:box_name] box_formats = env[:box_provider] if box_formats # Determine the formats a box can support and allow the box to # be any of those formats. provider_plugin = Vagrant.plugin("2").manager.providers[env[:box_provider]] if provider_plugin box_formats = provider_plugin[1][:box_format] box_formats ||= env[:box_provider] end end # Determine if we already have the box before downloading # it again. We can only do this if we specify a format if box_formats begin if env[:box_collection].find(box_name, box_formats) raise Errors::BoxAlreadyExists, :name => box_name, :formats => [box_formats].flatten.join(", ") end rescue Vagrant::Errors::BoxUpgradeRequired # If the box needs to be upgraded, do it. env[:box_collection].upgrade(box_name) retry end end # Determine the checksum type to use checksum = (env[:box_checksum] || "").to_s checksum_klass = nil if env[:box_checksum_type] checksum_klass = case env[:box_checksum_type].to_sym when :md5 Digest::MD5 when :sha1 Digest::SHA1 when :sha256 Digest::SHA2 else raise Errors::BoxChecksumInvalidType, type: env[:box_checksum_type].to_s end end # Go through each URL and attempt to download it download_error = nil download_url = nil urls = env[:box_url] urls = [env[:box_url]] if !urls.is_a?(Array) urls.each do |url| begin @temp_path = download_box_url(url, env) download_error = nil download_url = url rescue Errors::DownloaderError => e env[:ui].error(I18n.t( "vagrant.actions.box.download.download_failed")) download_error = e end # If we were interrupted during this download, then just return # at this point, we don't need to try anymore. if @download_interrupted @logger.warn("Download interrupted, not trying any more box URLs.") return end end # If all the URLs failed, then raise an exception raise download_error if download_error if checksum_klass @logger.info("Validating checksum with #{checksum_klass}") @logger.info("Expected checksum: #{checksum}") env[:ui].info(I18n.t("vagrant.actions.box.add.checksumming", name: box_name)) actual = FileChecksum.new(@temp_path, checksum_klass).checksum if actual != checksum raise Errors::BoxChecksumMismatch, actual: actual, expected: checksum end end # Add the box env[:ui].info I18n.t("vagrant.actions.box.add.adding", :name => box_name) box_added = nil begin box_added = env[:box_collection].add( @temp_path, box_name, box_formats, env[:box_force]) rescue Vagrant::Errors::BoxUpgradeRequired # Upgrade the box env[:box_collection].upgrade(box_name) # Try adding it again retry end # Call the 'recover' method in all cases to clean up the # downloaded temporary file. recover(env) # Success, we added a box! env[:ui].success( I18n.t("vagrant.actions.box.add.added", name: box_added.name, provider: box_added.provider)) # Persists URL used on download and the time it was added write_extra_info(box_added, download_url) # Passes on the newly added box to the rest of the middleware chain env[:box_added] = box_added # Carry on! @app.call(env) end def recover(env) if @temp_path && File.exist?(@temp_path) && !@download_interrupted File.unlink(@temp_path) end end def download_box_url(url, env) temp_path = env[:tmp_path].join("box" + Digest::SHA1.hexdigest(url)) @logger.info("Downloading box: #{url} => #{temp_path}") if File.file?(url) || url !~ /^[a-z0-9]+:.*$/i @logger.info("URL is a file or protocol not found and assuming file.") file_path = File.expand_path(url) file_path = Util::Platform.cygwin_windows_path(file_path) url = "file:#{file_path}" end downloader_options = {} downloader_options[:ca_cert] = env[:box_download_ca_cert] downloader_options[:continue] = true downloader_options[:insecure] = env[:box_download_insecure] downloader_options[:ui] = env[:ui] downloader_options[:client_cert] = env[:box_client_cert] # If the temporary path exists, verify it is not too old. If its # too old, delete it first because the data may have changed. if temp_path.file? delete = false if env[:box_clean] @logger.info("Cleaning existing temp box file.") delete = true elsif temp_path.mtime.to_i < (Time.now.to_i - 6 * 60 * 60) @logger.info("Existing temp file is too old. Removing.") delete = true end temp_path.unlink if delete end # Download the box to a temporary path. We store the temporary # path as an instance variable so that the `#recover` method can # access it. env[:ui].info(I18n.t( "vagrant.actions.box.download.downloading", url: url)) if temp_path.file? env[:ui].info(I18n.t("vagrant.actions.box.download.resuming")) end begin downloader = Util::Downloader.new(url, temp_path, downloader_options) downloader.download! rescue Errors::DownloaderInterrupted # The downloader was interrupted, so just return, because that # means we were interrupted as well. @download_interrupted = true env[:ui].info(I18n.t("vagrant.actions.box.download.interrupted")) rescue Errors::DownloaderError # The download failed for some reason, clean out the temp path temp_path.unlink if temp_path.file? raise end temp_path end def write_extra_info(box_added, url) info = {'url' => url, 'downloaded_at' => Time.now.utc} box_added.directory.join('info.json').open("w+") do |f| f.write(JSON.dump(info)) end end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/box_remove.rb000066400000000000000000000021431226132634600230130ustar00rootroot00000000000000require "log4r" module Vagrant module Action module Builtin # This middleware will remove a box for a given provider. class BoxRemove def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::box_remove") end def call(env) box_name = env[:box_name] box_provider = env[:box_provider].to_sym box = nil begin box = env[:box_collection].find(box_name, box_provider) rescue Vagrant::Errors::BoxUpgradeRequired env[:box_collection].upgrade(box_name) retry end raise Vagrant::Errors::BoxNotFound, :name => box_name, :provider => box_provider if !box env[:ui].info(I18n.t("vagrant.commands.box.removing", :name => box_name, :provider => box_provider)) box.destroy! # Passes on the removed box to the rest of the middleware chain env[:box_removed] = box @app.call(env) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/call.rb000066400000000000000000000047611226132634600215710ustar00rootroot00000000000000module Vagrant module Action module Builtin # This middleware class allows a sort of "conditional" run within # a single middlware sequence. It takes another middleware runnable, # runs it with the same environment, then yields the resulting env to a block, # allowing that block to determine the next course of action in the # middleware sequence. # # The first argument to this middleware sequence is anywhere middleware # runnable, whether it be a class, lambda, or something else that # responds to `call`. This middleware runnable is run with the same # environment as this class. # # After running, {Call} takes the environment and yields it to a block # given to initialize the class, along with an instance of {Builder}. # The result is used to build up a new sequence on the given builder. # This builder is then run. class Call # For documentation, read the description of the {Call} class. # # @param [Object] callable A valid middleware runnable object. This # can be a class, a lambda, or an object that responds to `call`. # @yield [result, builder] This block is expected to build on `builder` # which is the next middleware sequence that will be run. def initialize(app, env, callable, *callable_args, &block) raise ArgumentError, "A block must be given to Call" if !block @app = app @callable = callable @callable_args = callable_args @block = block @child_app = nil end def call(env) runner = Runner.new # Build the callable that we'll run callable = Builder.build(@callable, *@callable_args) # Run our callable with our environment new_env = runner.run(callable, env) # Build our new builder based on the result builder = Builder.new @block.call(new_env, builder) # Run the result with our new environment @child_app = builder.to_app(new_env) final_env = runner.run(@child_app, new_env) # Merge the environment into our original environment env.merge!(final_env) # Call the next step using our final environment @app.call(env) end def recover(env) # Call back into our compiled application and recover it. @child_app.recover(env) if @child_app end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/config_validate.rb000066400000000000000000000014141226132634600237640ustar00rootroot00000000000000require "vagrant/util/template_renderer" module Vagrant module Action module Builtin # This class validates the configuration and raises an exception # if there are any validation errors. class ConfigValidate def initialize(app, env) @app = app end def call(env) if !env.has_key?(:config_validate) || env[:config_validate] errors = env[:machine].config.validate(env[:machine]) if errors && !errors.empty? raise Errors::ConfigInvalid, :errors => Util::TemplateRenderer.render( "config/validation_failed", :errors => errors) end end @app.call(env) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/confirm.rb000066400000000000000000000024511226132634600223050ustar00rootroot00000000000000module Vagrant module Action module Builtin # This class asks the user to confirm some sort of question with # a "Y/N" question. The only parameter is the text to ask the user. # The result is placed in `env[:result]` so that it can be used # with the {Call} class. class Confirm # For documentation, read the description of the {Confirm} class. # # @param [String] message The message to ask the user. # @param [Symbol] force_key The key that if present and true in # the environment hash will skip the confirmation question. def initialize(app, env, message, force_key=nil) @app = app @message = message @force_key = force_key end def call(env) choice = nil # If we have a force key set and we're forcing, then set # the result to "Y" choice = "Y" if @force_key && env[@force_key] # If we haven't chosen yes, then ask the user via TTY choice = env[:ui].ask(@message) if !choice # The result is only true if the user said "Y" env[:result] = choice && choice.upcase == "Y" env["#{@force_key}_result".to_sym] = env[:result] @app.call(env) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/destroy_confirm.rb000066400000000000000000000012331226132634600240530ustar00rootroot00000000000000require_relative "confirm" module Vagrant module Action module Builtin # This class asks the user to confirm the destruction of a machine # that Vagrant manages. This is provided as a built-in on top of # {Confirm} because it sets up the proper keys and such so that # `vagrant destroy -f` works properly. class DestroyConfirm < Confirm def initialize(app, env) force_key = :force_confirm_destroy message = I18n.t("vagrant.commands.destroy.confirmation", :name => env[:machine].name) super(app, env, message, force_key) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/env_set.rb000066400000000000000000000011301226132634600223040ustar00rootroot00000000000000module Vagrant module Action module Builtin # This middleware class allows you to modify the environment hash # in the middle of a middleware sequence. The new environmental data # will take affect at this stage in the middleware and will persist # through. class EnvSet def initialize(app, env, new_env=nil) @app = app @new_env = new_env || {} end def call(env) # Merge in the new data env.merge!(@new_env) # Carry on @app.call(env) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/graceful_halt.rb000066400000000000000000000055531226132634600234560ustar00rootroot00000000000000require "log4r" require "timeout" module Vagrant module Action module Builtin # This middleware class will attempt to perform a graceful shutdown # of the machine using the guest implementation. This middleware is # compatible with the {Call} middleware so you can branch based on # the result, which is true if the halt succeeded and false otherwise. class GracefulHalt # Note: Any of the arguments can be arrays as well. # # @param [Symbol] target_state The target state ID that means that # the machine was properly shut down. # @param [Symbol] source_state The source state ID that the machine # must be in to be shut down. def initialize(app, env, target_state, source_state=nil) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::graceful_halt") @source_state = source_state @target_state = target_state end def call(env) graceful = true graceful = !env[:force_halt] if env.has_key?(:force_halt) # By default, we didn't succeed. env[:result] = false if graceful && @source_state @logger.info("Verifying source state of machine: #{@source_state.inspect}") # If we're not in the proper source state, then we don't # attempt to halt the machine current_state = env[:machine].state.id if current_state != @source_state @logger.info("Invalid source state, not halting: #{current_state}") graceful = false end end # Only attempt to perform graceful shutdown under certain cases # checked above. if graceful env[:ui].info I18n.t("vagrant.actions.vm.halt.graceful") begin env[:machine].guest.capability(:halt) rescue Errors::MachineGuestNotReady @logger.info("Machine guest not ready while attempting to halt. Ignoring.") end @logger.debug("Waiting for target graceful halt state: #{@target_state}") begin Timeout.timeout(env[:machine].config.vm.graceful_halt_timeout) do while env[:machine].state.id != @target_state sleep 1 end end rescue Timeout::Error # Don't worry about it, we catch the case later. end # The result of this matters on whether we reached our # proper target state or not. env[:result] = env[:machine].state.id == @target_state if env[:result] @logger.info("Gracefully halted.") else @logger.info("Graceful halt failed.") end end @app.call(env) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/handle_box_url.rb000066400000000000000000000073041226132634600236370ustar00rootroot00000000000000require "thread" require "log4r" module Vagrant module Action module Builtin # This built-in middleware handles the `box_url` setting, downloading # the box if necessary. You should place this early in your middleware # sequence for a provider after configuration validation but before # you attempt to use any box. class HandleBoxUrl @@big_lock = Mutex.new @@handle_box_url_locks = Hash.new { |h,k| h[k] = Mutex.new } def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::handle_box_url") end def call(env) if !env[:machine].config.vm.box || !env[:machine].config.vm.box_url @logger.info("Skipping HandleBoxUrl because box or box_url not set.") @app.call(env) return end if env[:machine].box @logger.info("Skipping HandleBoxUrl because box is already available") @app.call(env) return end # Get a "big lock" to make sure that our more fine grained # lock access is thread safe. lock = nil @@big_lock.synchronize do lock = @@handle_box_url_locks[env[:machine].config.vm.box] end box_name = env[:machine].config.vm.box box_url = env[:machine].config.vm.box_url box_download_ca_cert = env[:machine].config.vm.box_download_ca_cert box_download_checksum = env[:machine].config.vm.box_download_checksum box_download_checksum_type = env[:machine].config.vm.box_download_checksum_type box_download_client_cert = env[:machine].config.vm.box_download_client_cert box_download_insecure = env[:machine].config.vm.box_download_insecure # Expand the CA cert file relative to the Vagrantfile path, if # there is one. if box_download_ca_cert box_download_ca_cert = File.expand_path( box_download_ca_cert, env[:machine].env.root_path) end lock.synchronize do # Check that we don't already have the box, which can happen # if we're slow to acquire the lock because of another thread box_formats = env[:machine].provider_options[:box_format] || env[:machine].provider_name if env[:box_collection].find(box_name, box_formats) break end # Add the box then reload the box collection so that it becomes # aware of it. env[:ui].info I18n.t( "vagrant.actions.vm.check_box.not_found", :name => box_name, :provider => env[:machine].provider_name) begin env[:action_runner].run(Vagrant::Action.action_box_add, { :box_checksum => box_download_checksum, :box_checksum_type => box_download_checksum_type, :box_client_cert => box_download_client_cert, :box_download_ca_cert => box_download_ca_cert, :box_download_insecure => box_download_insecure, :box_name => box_name, :box_provider => box_formats, :box_url => box_url, }) rescue Errors::BoxAlreadyExists # Just ignore this, since it means the next part will succeed! # This can happen in a multi-threaded environment. end end # Reload the environment and set the VM to be the new loaded VM. env[:machine] = env[:machine].env.machine( env[:machine].name, env[:machine].provider_name, true) @app.call(env) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/handle_forwarded_port_collisions.rb000066400000000000000000000111321226132634600274360ustar00rootroot00000000000000require "set" require "log4r" require "vagrant/util/is_port_open" module Vagrant module Action module Builtin # This middleware class will detect and handle collisions with # forwarded ports, whether that means raising an error or repairing # them automatically. # # Parameters it takes from the environment hash: # # * `:port_collision_repair` - If true, it will attempt to repair # port collisions. If false, it will raise an exception when # there is a collision. # # * `:port_collision_extra_in_use` - An array of ports that are # considered in use. # # * `:port_collision_remap` - A hash remapping certain host ports # to other host ports. # class HandleForwardedPortCollisions include Util::IsPortOpen def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::handle_port_collisions") end def call(env) @logger.info("Detecting any forwarded port collisions...") # Get the extra ports we consider in use extra_in_use = env[:port_collision_extra_in_use] || [] # Get the remap remap = env[:port_collision_remap] || {} # Determine the handler we'll use if we have any port collisions repair = !!env[:port_collision_repair] # Log out some of our parameters @logger.debug("Extra in use: #{extra_in_use.inspect}") @logger.debug("Remap: #{remap.inspect}") @logger.debug("Repair: #{repair.inspect}") # Determine a list of usable ports for repair usable_ports = Set.new(env[:machine].config.vm.usable_port_range) usable_ports.subtract(extra_in_use) # Pass one, remove all defined host ports from usable ports with_forwarded_ports(env) do |options| usable_ports.delete(options[:host]) end # Pass two, detect/handle any collisions with_forwarded_ports(env) do |options| guest_port = options[:guest] host_port = options[:host] if remap[host_port] remap_port = remap[host_port] @logger.debug("Remap port override: #{host_port} => #{remap_port}") host_port = remap_port end # If the port is open (listening for TCP connections) if extra_in_use.include?(host_port) || is_port_open?("127.0.0.1", host_port) if !repair || !options[:auto_correct] raise Errors::ForwardPortCollision, :guest_port => guest_port.to_s, :host_port => host_port.to_s end @logger.info("Attempting to repair FP collision: #{host_port}") repaired_port = nil while !usable_ports.empty? # Attempt to repair the forwarded port repaired_port = usable_ports.to_a.sort[0] usable_ports.delete(repaired_port) # If the port is in use, then we can't use this either... if extra_in_use.include?(repaired_port) || is_port_open?("127.0.0.1", repaired_port) @logger.info("Reparied port also in use: #{repaired_port}. Trying another...") next end # We have a port so break out break end # If we have no usable ports then we can't repair if !repaired_port && usable_ports.empty? raise Errors::ForwardPortAutolistEmpty, :vm_name => env[:machine].name, :guest_port => guest_port.to_s, :host_port => host_port.to_s end # Modify the args in place options[:host] = repaired_port @logger.info("Repaired FP collision: #{host_port} to #{repaired_port}") # Notify the user env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.fixed_collision", :host_port => host_port.to_s, :guest_port => guest_port.to_s, :new_port => repaired_port.to_s)) end end @app.call(env) end protected def with_forwarded_ports(env) env[:machine].config.vm.networks.each do |type, options| # Ignore anything but forwarded ports next if type != :forwarded_port yield options end end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/lock.rb000066400000000000000000000040141226132634600215750ustar00rootroot00000000000000require "log4r" module Vagrant module Action module Builtin # This class creates a multi-process lock using `flock`. The lock # is active for the remainder of the middleware stack. class Lock def initialize(app, env, options=nil) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::lock") @options ||= options || {} raise ArgumentError, "Please specify a lock path" if !@options[:path] raise ArgumentError, "Please specify an exception." if !@options[:exception] end def call(env) lock_path = @options[:path] lock_path = lock_path.call(env) if lock_path.is_a?(Proc) env_key = "has_lock_#{lock_path}" if !env[env_key] # If we already have the key in our environment we assume the # lock is held by our middleware stack already and we allow # nesting. File.open(lock_path, "w+") do |f| # The file locking fails only if it returns "false." If it # succeeds it returns a 0, so we must explicitly check for # the proper error case. @logger.info("Locking: #{lock_path}") if f.flock(File::LOCK_EX | File::LOCK_NB) === false exception = @options[:exception] exception = exception.call(env) if exception.is_a?(Proc) raise exception end # Set that we gained the lock and call deeper into the # middleware, but make sure we UNSET the lock when we leave. begin env[env_key] = true @app.call(env) ensure @logger.info("Unlocking: #{lock_path}") env[env_key] = false f.flock(File::LOCK_UN) end end else # Just call up the middleware because we already hold the lock @app.call(env) end end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/mixin_provisioners.rb000066400000000000000000000026201226132634600246140ustar00rootroot00000000000000module Vagrant module Action module Builtin module MixinProvisioners # This returns all the instances of the configured provisioners. # This is safe to call multiple times since it will cache the results. # # @return [Array] def provisioner_instances(env) return @_provisioner_instances if @_provisioner_instances # Make the mapping that'll keep track of provisioner => type @_provisioner_types = {} # Get all the configured provisioners @_provisioner_instances = env[:machine].config.vm.provisioners.map do |provisioner| # Instantiate the provisioner klass = Vagrant.plugin("2").manager.provisioners[provisioner.name] result = klass.new(env[:machine], provisioner.config) # Store in the type map so that --provision-with works properly @_provisioner_types[result] = provisioner.name # Return the result result end return @_provisioner_instances end # This will return a mapping of a provisioner instance to its # type. def provisioner_type_map(env) # Call this in order to initial the map if it hasn't been already provisioner_instances(env) # Return the type map @_provisioner_types end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/mixin_synced_folders.rb000066400000000000000000000067041226132634600250640ustar00rootroot00000000000000module Vagrant module Action module Builtin module MixinSyncedFolders # This goes over all the registered synced folder types and returns # the highest priority implementation that is usable for this machine. def default_synced_folder_type(machine, plugins) ordered = [] # First turn the plugins into an array plugins.each do |key, data| impl = data[0] priority = data[1] ordered << [priority, key, impl] end # Order the plugins by priority ordered = ordered.sort { |a, b| b[0] <=> a[0] } # Find the proper implementation ordered.each do |_, key, impl| return key if impl.new.usable?(machine) end return nil end # This finds the options in the env that are set for a given # synced folder type. def impl_opts(name, env) {}.tap do |result| env.each do |k, v| if k.to_s.start_with?("#{name}_") k = k.dup if !k.is_a?(Symbol) v = v.dup if !v.is_a?(Symbol) result[k] = v end end end end # This returns the available synced folder implementations. This # is a separate method so that it can be easily stubbed by tests. def plugins @plugins ||= Vagrant.plugin("2").manager.synced_folders end # This returns the set of shared folders that should be done for # this machine. It returns the folders in a hash keyed by the # implementation class for the synced folders. # # @return [Hash>] def synced_folders(machine) folders = {} # Determine all the synced folders as well as the implementation # they're going to use. machine.config.vm.synced_folders.each do |id, data| # Ignore disabled synced folders next if data[:disabled] impl = "" impl = data[:type].to_sym if data[:type] if impl != "" impl_class = plugins[impl] if !impl_class # This should never happen because configuration validation # should catch this case. But we put this here as an assert raise "Internal error. Report this as a bug. Invalid: #{data[:type]}" end if !impl_class[0].new.usable?(machine) # Verify that explicitly defined shared folder types are # actually usable. raise Errors::SyncedFolderUnusable, type: data[:type].to_s end end # Keep track of this shared folder by the implementation. folders[impl] ||= {} folders[impl][id] = data.dup end # If we have folders with the "default" key, then determine the # most appropriate implementation for this. if folders.has_key?("") && !folders[""].empty? default_impl = default_synced_folder_type(machine, plugins) if !default_impl types = plugins.to_hash.keys.map { |t| t.to_s }.sort.join(", ") raise Errors::NoDefaultSyncedFolderImpl, types: types end folders[default_impl] = folders[""] folders.delete("") end return folders end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/provision.rb000066400000000000000000000060141226132634600226770ustar00rootroot00000000000000require "log4r" require_relative "mixin_provisioners" module Vagrant module Action module Builtin # This class will run the configured provisioners against the # machine. # # This action should be placed BEFORE the machine is booted so it # can do some setup, and then run again (on the return path) against # a running machine. class Provision include MixinProvisioners def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::provision") end def call(env) @env = env # Check if we already provisioned, and if so, disable the rest enabled = true ignore_sentinel = true if env.has_key?(:provision_ignore_sentinel) ignore_sentinel = env[:provision_ignore_sentinel] end sentinel_path = nil if !ignore_sentinel @logger.info("Checking provisioner sentinel if we should run...") sentinel_path = env[:machine].data_dir.join("action_provision") if sentinel_path.file? @logger.info("Sentinel found! Not provisioning.") enabled = false end end # Store the value so that other actions can use it env[:provision_enabled] = enabled if !env.has_key?(:provision_enabled) # Ask the provisioners to modify the configuration if needed provisioner_instances(env).each do |p| p.configure(env[:machine].config) end # Continue, we need the VM to be booted. @app.call(env) # Write the sentinel if we have to if sentinel_path && !sentinel_path.file? @logger.info("Writing provisioning sentinel so we don't provision again") sentinel_path.open("w") do |f| f.write(Time.now.to_i.to_s) end end # Actually provision if we enabled it if env[:provision_enabled] type_map = provisioner_type_map(env) provisioner_instances(env).each do |p| type_name = type_map[p] next if env[:provision_types] && \ !env[:provision_types].include?(type_name) env[:ui].info(I18n.t( "vagrant.actions.vm.provision.beginning", provisioner: type_name)) env[:hook].call(:provisioner_run, env.merge( callable: method(:run_provisioner), provisioner: p, provisioner_name: type_name, )) end elsif !enabled env[:ui].info(I18n.t("vagrant.actions.vm.provision.disabled_by_sentinel")) end end # This is pulled out into a seperate method so that users can # subclass and implement custom behavior if they'd like to work around # this step. def run_provisioner(env) env[:provisioner].provision end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/provisioner_cleanup.rb000066400000000000000000000021361226132634600247360ustar00rootroot00000000000000require "log4r" require_relative "mixin_provisioners" module Vagrant module Action module Builtin # This action will run the cleanup methods on provisioners and should # be used as part of any Destroy action. class ProvisionerCleanup include MixinProvisioners def initialize(app, env, place=nil) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::provision_cleanup") @place ||= :after @place = @place.to_sym end def call(env) do_cleanup(env) if @place == :before # Continue, we need the VM to be booted. @app.call(env) do_cleanup(env) if @place == :after end def do_cleanup(env) type_map = provisioner_type_map(env) # Ask the provisioners to modify the configuration if needed provisioner_instances(env).each do |p| env[:ui].info(I18n.t( "vagrant.provisioner_cleanup", name: type_map[p].to_s)) p.cleanup end end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/set_hostname.rb000066400000000000000000000014171226132634600233420ustar00rootroot00000000000000require "log4r" module Vagrant module Action module Builtin # This middleware sets the hostname of the guest according to the # "vm.hostname" configuration parameter if it is set. This middleware # should be placed such that the after the @app.call, a booted machine # is available (this generally means BEFORE the boot middleware). class SetHostname def initialize(app, env) @app = app end def call(env) @app.call(env) hostname = env[:machine].config.vm.hostname if !hostname.nil? env[:ui].info I18n.t("vagrant.actions.vm.hostname.setting") env[:machine].guest.capability(:change_host_name, hostname) end end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/ssh_exec.rb000066400000000000000000000024161226132634600224520ustar00rootroot00000000000000require "pathname" require "vagrant/util/ssh" module Vagrant module Action module Builtin # This class will exec into a full fledged SSH console into the # remote machine. This middleware assumes that the VM is running and # ready for SSH, and uses the {Machine#ssh_info} method to retrieve # SSH information necessary to connect. # # Note: If there are any middleware after `SSHExec`, they will **not** # run, since exec replaces the currently running process. class SSHExec # For quick access to the `SSH` class. include Vagrant::Util def initialize(app, env) @app = app end def call(env) # Grab the SSH info from the machine info = env[:machine].ssh_info # If the result is nil, then the machine is telling us that it is # not yet ready for SSH, so we raise this exception. raise Errors::SSHNotReady if info.nil? if info[:private_key_path] # Check SSH key permissions info[:private_key_path].each do |path| SSH.check_key_permissions(Pathname.new(path)) end end # Exec! SSH.exec(info, env[:ssh_opts]) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/ssh_run.rb000066400000000000000000000035201226132634600223270ustar00rootroot00000000000000require "log4r" require "vagrant/util/ssh" require "vagrant/util/shell_quote" module Vagrant module Action module Builtin # This class will run a single command on the remote machine and will # mirror the output to the UI. The resulting exit status of the command # will exist in the `:ssh_run_exit_status` key in the environment. class SSHRun # For quick access to the `SSH` class. include Vagrant::Util def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::ssh_run") end def call(env) # Grab the SSH info from the machine info = env[:machine].ssh_info # If the result is nil, then the machine is telling us that it is # not yet ready for SSH, so we raise this exception. raise Errors::SSHNotReady if info.nil? if info[:private_key_path] # Check SSH key permissions info[:private_key_path].each do |path| SSH.check_key_permissions(Pathname.new(path)) end end # Get the command and wrap it in a login shell command = ShellQuote.escape(env[:ssh_run_command], "'") command = "#{env[:machine].config.ssh.shell} -c '#{command}'" # Execute! opts = env[:ssh_opts] || {} # Allow the user to specify a tty or non-tty manually, but if they # don't then we default to a TTY if !opts[:extra_args].include?("-t") && !opts[:extra_args].include?("-T") opts[:extra_args] << "-t" end opts[:extra_args] << command opts[:subprocess] = true env[:ssh_run_exit_status] = Util::SSH.exec(info, opts) # Call the next middleware @app.call(env) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/synced_folder_cleanup.rb000066400000000000000000000015651226132634600252040ustar00rootroot00000000000000require "log4r" require_relative "mixin_synced_folders" module Vagrant module Action module Builtin # This middleware will run cleanup tasks for synced folders using # the appropriate synced folder plugin. class SyncedFolderCleanup include MixinSyncedFolders def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::synced_folder_cleanup") end def call(env) folders = synced_folders(env[:machine]) # Go through each folder and do cleanup folders.each_key do |impl_name| @logger.info("Invoking synced folder cleanup for: #{impl_name}") plugins[impl_name.to_sym][0].new.cleanup( env[:machine], impl_opts(impl_name, env)) end @app.call(env) end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/synced_folders.rb000066400000000000000000000052751226132634600236620ustar00rootroot00000000000000require "log4r" require 'vagrant/util/platform' require 'vagrant/util/scoped_hash_override' require_relative "mixin_synced_folders" module Vagrant module Action module Builtin # This middleware will setup the synced folders for the machine using # the appropriate synced folder plugin. class SyncedFolders include MixinSyncedFolders include Vagrant::Util::ScopedHashOverride def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::builtin::synced_folders") end def call(env) folders = synced_folders(env[:machine]) folders.each do |impl_name, fs| @logger.info("Synced Folder Implementation: #{impl_name}") fs.each do |id, data| # Log every implementation and their paths @logger.info(" - #{id}: #{data[:hostpath]} => #{data[:guestpath]}") # Scope hash override fs[id] = scoped_hash_override(data, impl_name) end end # Go through each folder and make sure to create it if # it does not exist on host folders.each do |_, fs| fs.each do |id, data| data[:hostpath] = File.expand_path(data[:hostpath], env[:root_path]) # Create the hostpath if it doesn't exist and we've been told to if !File.directory?(data[:hostpath]) && data[:create] @logger.info("Creating shared folder host directory: #{data[:hostpath]}") begin Pathname.new(data[:hostpath]).mkpath rescue Errno::EACCES raise Vagrant::Errors::SharedFolderCreateFailed, path: data[:hostpath] end end if File.directory?(data[:hostpath]) data[:hostpath] = File.realpath(data[:hostpath]) data[:hostpath] = Util::Platform.fs_real_path(data[:hostpath]).to_s end end end # Go through each folder and prepare the folders folders.each do |impl_name, fs| @logger.info("Invoking synced folder prepare for: #{impl_name}") plugins[impl_name.to_sym][0].new.prepare(env[:machine], fs, impl_opts(impl_name, env)) end # Continue, we need the VM to be booted. @app.call(env) # Once booted, setup the folder contents folders.each do |impl_name, fs| @logger.info("Invoking synced folder enable: #{impl_name}") plugins[impl_name.to_sym][0].new.enable(env[:machine], fs, impl_opts(impl_name, env)) end end end end end end vagrant-1.4.3/lib/vagrant/action/builtin/wait_for_communicator.rb000066400000000000000000000046501226132634600252450ustar00rootroot00000000000000module Vagrant module Action module Builtin # This waits for the communicator to be ready for a set amount of # time. class WaitForCommunicator def initialize(app, env, states=nil) @app = app @states = states end def call(env) # Wait for ready in a thread so that we can continually check # for interrupts. ready_thr = Thread.new do Thread.current[:result] = env[:machine].communicate.wait_for_ready( env[:machine].config.vm.boot_timeout) end # Start a thread that verifies the VM stays in a good state. states_thr = Thread.new do Thread.current[:result] = true # If we aren't caring about states, just basically put this # thread to sleep because it'll get killed later. if !@states while true sleep 300 end next end # Otherwise, periodically verify the VM isn't in a bad state. while true state = env[:machine].provider.state.id # Used to report invalid states Thread.current[:last_known_state] = state # Check if we have the proper state so we can break out if !@states.include?(state) Thread.current[:result] = false break end # Sleep a bit so we don't hit 100% CPU constantly. sleep 1 end end # Wait for a result or an interrupt env[:ui].info I18n.t("vagrant.boot_waiting") while ready_thr.alive? && states_thr.alive? sleep 1 return if env[:interrupted] end # If it went into a bad state, then raise an error if !states_thr[:result] raise Errors::VMBootBadState, valid: @states.join(", "), invalid: states_thr[:last_known_state] end # If it didn't boot, raise an error if !ready_thr[:result] raise Errors::VMBootTimeout end env[:ui].info I18n.t("vagrant.boot_completed") # Make sure our threads are all killed ready_thr.kill states_thr.kill @app.call(env) ensure ready_thr.kill states_thr.kill end end end end end vagrant-1.4.3/lib/vagrant/action/general/000077500000000000000000000000001226132634600202705ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/action/general/package.rb000066400000000000000000000067151226132634600222210ustar00rootroot00000000000000require 'fileutils' require 'vagrant/util/safe_chdir' require 'vagrant/util/subprocess' module Vagrant module Action module General # A general packaging (tar) middleware. Given the following options, # it will do the right thing: # # * package.output - The filename of the outputted package. # * package.include - An array of files to include in the package. # * package.directory - The directory which contains the contents to # compress into the package. # # This middleware always produces the final file in the current working # directory (FileUtils.pwd) class Package include Util def initialize(app, env) @app = app env["package.files"] ||= {} env["package.output"] ||= env[:global_config].package.name end def call(env) @env = env raise Errors::PackageOutputDirectory if File.directory?(tar_path) raise Errors::PackageOutputExists if File.exist?(tar_path) raise Errors::PackageRequiresDirectory if !env["package.directory"] || !File.directory?(env["package.directory"]) @app.call(env) compress end def recover(env) @env = env # There are certain exceptions that we don't delete the file for. ignore_exc = [Errors::PackageOutputDirectory, Errors::PackageOutputExists] ignore_exc.each do |exc| return if env["vagrant.error"].is_a?(exc) end # Cleanup any packaged files if the packaging failed at some point. File.delete(tar_path) if File.exist?(tar_path) end # This method copies the include files (passed in via command line) # to the temporary directory so they are included in a sub-folder within # the actual box def copy_include_files include_directory = Pathname.new(@env["package.directory"]).join("include") @env["package.files"].each do |from, dest| # We place the file in the include directory to = include_directory.join(dest) @env[:ui].info I18n.t("vagrant.actions.general.package.packaging", :file => from) FileUtils.mkdir_p(to.parent) # Copy direcotry contents recursively. if File.directory?(from) FileUtils.cp_r(Dir.glob(from), to.parent, :preserve => true) else FileUtils.cp(from, to, :preserve => true) end end end # Compress the exported file into a package def compress @env[:ui].info I18n.t("vagrant.actions.general.package.compressing", :tar_path => tar_path) # Copy over the included files copy_include_files # Get the output path. We have to do this up here so that the # pwd returns the proper thing. output_path = tar_path.to_s # Switch into that directory and package everything up Util::SafeChdir.safe_chdir(@env["package.directory"]) do # Find all the files in our current directory and tar it up! files = Dir.glob(File.join(".", "**", "*")) # Package! Util::Subprocess.execute("bsdtar", "-czf", output_path, *files) end end # Path to the final box output file def tar_path File.expand_path(@env["package.output"], FileUtils.pwd) end end end end end vagrant-1.4.3/lib/vagrant/action/hook.rb000066400000000000000000000061731226132634600201470ustar00rootroot00000000000000module Vagrant module Action # This class manages hooks into existing {Builder} stacks, and lets you # add and remove middleware classes. This is the primary method by which # plugins can hook into built-in middleware stacks. class Hook # This is a hash of the middleware to prepend to a certain # other middleware. # # @return [Hash>] attr_reader :before_hooks # This is a hash of the middleware to append to a certain other # middleware. # # @return [Hash>] attr_reader :after_hooks # This is a list of the hooks to just prepend to the beginning # # @return [Array] attr_reader :prepend_hooks # This is a list of the hooks to just append to the end # # @return [Array] attr_reader :append_hooks def initialize @before_hooks = Hash.new { |h, k| h[k] = [] } @after_hooks = Hash.new { |h, k| h[k] = [] } @prepend_hooks = [] @append_hooks = [] end # Add a middleware before an existing middleware. # # @param [Class] existing The existing middleware. # @param [Class] new The new middleware. def before(existing, new, *args, &block) @before_hooks[existing] << [new, args, block] end # Add a middleware after an existing middleware. # # @param [Class] existing The existing middleware. # @param [Class] new The new middleware. def after(existing, new, *args, &block) @after_hooks[existing] << [new, args, block] end # Append a middleware to the end of the stack. Note that if the # middleware sequence ends early, then the new middleware won't # be run. # # @param [Class] new The middleware to append. def append(new, *args, &block) @append_hooks << [new, args, block] end # Prepend a middleware to the beginning of the stack. # # @param [Class] new The new middleware to prepend. def prepend(new, *args, &block) @prepend_hooks << [new, args, block] end # This applies the given hook to a builder. This should not be # called directly. # # @param [Builder] builder def apply(builder, options=nil) options ||= {} if !options[:no_prepend_or_append] # Prepends first @prepend_hooks.each do |klass, args, block| builder.insert(0, klass, *args, &block) end # Appends @append_hooks.each do |klass, args, block| builder.use(klass, *args, &block) end end # Before hooks @before_hooks.each do |key, list| next if !builder.index(key) list.each do |klass, args, block| builder.insert_before(key, klass, *args, &block) end end # After hooks @after_hooks.each do |key, list| next if !builder.index(key) list.each do |klass, args, block| builder.insert_after(key, klass, *args, &block) end end end end end end vagrant-1.4.3/lib/vagrant/action/runner.rb000066400000000000000000000046351226132634600205210ustar00rootroot00000000000000require 'log4r' require 'vagrant/action/hook' require 'vagrant/util/busy' # TODO: # * env.lock module Vagrant module Action class Runner @@reported_interrupt = false def initialize(globals=nil, &block) @globals = globals || {} @lazy_globals = block @logger = Log4r::Logger.new("vagrant::action::runner") end def run(callable_id, options=nil) callable = callable_id if !callable.kind_of?(Builder) if callable_id.kind_of?(Class) || callable_id.respond_to?(:call) callable = Builder.build(callable_id) end end if !callable || !callable.respond_to?(:call) raise ArgumentError, "Argument to run must be a callable object or registered action." end # Create the initial environment with the options given environment = {} environment.merge!(@globals) environment.merge!(@lazy_globals.call) if @lazy_globals environment.merge!(options || {}) # Setup the action hooks hooks = Vagrant.plugin("2").manager.action_hooks(environment[:action_name]) if !hooks.empty? @logger.info("Preparing hooks for middleware sequence...") environment[:action_hooks] = hooks.map do |hook_proc| Hook.new.tap do |h| hook_proc.call(h) end end @logger.info("#{environment[:action_hooks].length} hooks defined.") end # Run the action chain in a busy block, marking the environment as # interrupted if a SIGINT occurs, and exiting cleanly once the # chain has been run. ui = environment[:ui] if environment.has_key?(:ui) int_callback = lambda do if environment[:interrupted] ui.error I18n.t("vagrant.actions.runner.exit_immediately") if ui abort end ui.warn I18n.t("vagrant.actions.runner.waiting_cleanup") if ui && !@@reported_interrupt environment[:interrupted] = true @@reported_interrupt = true end # We place a process lock around every action that is called @logger.info("Running action: #{callable_id}") Util::Busy.busy(int_callback) { callable.call(environment) } # Return the environment in case there are things in there that # the caller wants to use. environment end end end end vagrant-1.4.3/lib/vagrant/action/warden.rb000066400000000000000000000070051226132634600204620ustar00rootroot00000000000000require "log4r" module Vagrant module Action # The action warden is a middleware which injects itself between # every other middleware, watching for exceptions which are raised # and performing proper cleanup on every action by calling the `recover` # method. The warden therefore allows middlewares to not worry about # exceptional events, and by providing a simple callback, can clean up # in any erroneous case. # # Warden will "just work" behind the scenes, and is not of particular # interest except to those who are curious about the internal workings # of Vagrant. class Warden attr_accessor :actions, :stack def initialize(actions, env) @stack = [] @actions = actions.map { |m| finalize_action(m, env) } @logger = Log4r::Logger.new("vagrant::action::warden") @last_error = nil end def call(env) return if @actions.empty? begin # Call the next middleware in the sequence, appending to the stack # of "recoverable" middlewares in case something goes wrong! raise Errors::VagrantInterrupt if env[:interrupted] action = @actions.shift @logger.info("Calling IN action: #{action}") @stack.unshift(action).first.call(env) raise Errors::VagrantInterrupt if env[:interrupted] @logger.info("Calling OUT action: #{action}") rescue SystemExit # This means that an "exit" or "abort" was called. In these cases, # we just exit immediately. raise rescue Exception => e # We guard this so that the Warden only outputs this once for # an exception that bubbles up. if e != @last_error @logger.error("Error occurred: #{e}") @last_error = e end env["vagrant.error"] = e # Something went horribly wrong. Start the rescue chain then # reraise the exception to properly kick us out of limbo here. recover(env) raise end end # We implement the recover method ourselves in case a Warden is # embedded within another Warden. To recover, we just do our own # recovery process on our stack. def recover(env) @logger.info("Beginning recovery process...") @stack.each do |act| if act.respond_to?(:recover) @logger.info("Calling recover: #{act}") act.recover(env) end end @logger.info("Recovery complete.") # Clear stack so that warden down the middleware chain doesn't # rescue again. @stack.clear end # A somewhat confusing function which simply initializes each # middleware properly to call the next middleware in the sequence. def finalize_action(action, env) klass, args, block = action # Default the arguments to an empty array. Otherwise in Ruby 1.8 # a `nil` args will actually pass `nil` into the class. args ||= [] if klass.is_a?(Class) # A action klass which is to be instantiated with the # app, env, and any arguments given klass.new(self, env, *args, &block) elsif klass.respond_to?(:call) # Make it a lambda which calls the item then forwards # up the chain lambda do |e| klass.call(e) self.call(e) end else raise "Invalid action: #{action.inspect}" end end end end end vagrant-1.4.3/lib/vagrant/batch_action.rb000066400000000000000000000067031226132634600203470ustar00rootroot00000000000000require 'thread' require "log4r" module Vagrant # This class executes multiple actions as a single batch, parallelizing # the action calls if possible. class BatchAction def initialize(allow_parallel=true) @actions = [] @allow_parallel = allow_parallel @logger = Log4r::Logger.new("vagrant::batch_action") end # Add an action to the batch of actions that will be run. # # This will **not** run the action now. The action will be run # when {#run} is called. # # @param [Machine] machine The machine to run the action on # @param [Symbol] action The action to run # @param [Hash] options Any additional options to send in. def action(machine, action, options=nil) @actions << [machine, action, options] end # Run all the queued up actions, parallelizing if possible. # # This will parallelize if and only if the provider of every machine # supports parallelization and parallelization is possible from # initialization of the class. def run par = false if @allow_parallel par = true @logger.info("Enabling parallelization by default.") end if par @actions.each do |machine, _, _| if !machine.provider_options[:parallel] @logger.info("Disabling parallelization because provider doesn't support it: #{machine.provider_name}") par = false break end end end @logger.info("Batch action will parallelize: #{par.inspect}") threads = [] @actions.each do |machine, action, options| @logger.info("Starting action: #{machine} #{action} #{options}") # Create the new thread to run our action. This is basically just # calling the action but also contains some error handling in it # as well. thread = Thread.new do Thread.current[:error] = nil begin machine.send(:action, action, options) rescue Exception => e # If we're not parallelizing, then raise the error raise if !par # Store the exception that will be processed later Thread.current[:error] = e end end # Set some attributes on the thread for later thread[:machine] = machine thread.join if !par threads << thread end errors = [] threads.each do |thread| # Wait for the thread to complete thread.join # If the thread had an error, then store the error to show later if thread[:error] e = thread[:error] # If the error isn't a Vagrant error, then store the backtrace # as well. if !thread[:error].is_a?(Errors::VagrantError) e = thread[:error] message = e.message message += "\n" message += "\n#{e.backtrace.join("\n")}" errors << I18n.t("vagrant.general.batch_unexpected_error", :machine => thread[:machine].name, :message => message) else errors << I18n.t("vagrant.general.batch_vagrant_error", :machine => thread[:machine].name, :message => thread[:error].message) end end end if !errors.empty? raise Errors::BatchMultiError, :message => errors.join("\n\n") end end end end vagrant-1.4.3/lib/vagrant/box.rb000066400000000000000000000055471226132634600165260ustar00rootroot00000000000000require 'fileutils' require "json" require "log4r" require "vagrant/util/platform" require "vagrant/util/safe_chdir" require "vagrant/util/subprocess" module Vagrant # Represents a "box," which is a package Vagrant environment that is used # as a base image when creating a new guest machine. class Box include Comparable # The box name. This is the logical name used when adding the box. # # @return [String] attr_reader :name # This is the provider that this box is built for. # # @return [Symbol] attr_reader :provider # This is the directory on disk where this box exists. # # @return [Pathname] attr_reader :directory # This is the metadata for the box. This is read from the "metadata.json" # file that all boxes require. # # @return [Hash] attr_reader :metadata # This is used to initialize a box. # # @param [String] name Logical name of the box. # @param [Symbol] provider The provider that this box implements. # @param [Pathname] directory The directory where this box exists on # disk. def initialize(name, provider, directory) @name = name @provider = provider @directory = directory metadata_file = directory.join("metadata.json") raise Errors::BoxMetadataFileNotFound, :name => @name if !metadata_file.file? begin @metadata = JSON.parse(directory.join("metadata.json").read) rescue JSON::ParserError raise Errors::BoxMetadataCorrupted, name: @name end @logger = Log4r::Logger.new("vagrant::box") end # This deletes the box. This is NOT undoable. def destroy! # Delete the directory to delete the box. FileUtils.rm_r(@directory) # Just return true always true rescue Errno::ENOENT # This means the directory didn't exist. Not a problem. return true end # This repackages this box and outputs it to the given path. # # @param [Pathname] path The full path (filename included) of where # to output this box. # @return [Boolean] true if this succeeds. def repackage(path) @logger.debug("Repackaging box '#{@name}' to: #{path}") Util::SafeChdir.safe_chdir(@directory) do # Find all the files in our current directory and tar it up! files = Dir.glob(File.join(".", "**", "*")) # Package! Util::Subprocess.execute("bsdtar", "-czf", path.to_s, *files) end @logger.info("Repackaged box '#{@name}' successfully: #{path}") true end # Implemented for comparison with other boxes. Comparison is # implemented by comparing names and providers. def <=>(other) return super if !other.is_a?(self.class) # Comparison is done by composing the name and provider "#{@name}-#{@provider}" <=> "#{other.name}-#{other.provider}" end end end vagrant-1.4.3/lib/vagrant/box_collection.rb000066400000000000000000000346471226132634600207440ustar00rootroot00000000000000require "digest/sha1" require "thread" require "tmpdir" require "log4r" require "vagrant/util/subprocess" module Vagrant # Represents a collection a boxes found on disk. This provides methods # for accessing/finding individual boxes, adding new boxes, or deleting # boxes. class BoxCollection TEMP_PREFIX = "vagrant-box-add-temp-" # The directory where the boxes in this collection are stored. # # A box collection matches a very specific folder structure that Vagrant # expects in order to easily manage and modify boxes. The folder structure # is the following: # # COLLECTION_ROOT/BOX_NAME/PROVIDER/metadata.json # # Where: # # * COLLECTION_ROOT - This is the root of the box collection, and is # the directory given to the initializer. # * BOX_NAME - The name of the box. This is a logical name given by # the user of Vagrant. # * PROVIDER - The provider that the box was built for (VirtualBox, # VMWare, etc.). # * metadata.json - A simple JSON file that at the bare minimum # contains a "provider" key that matches the provider for the # box. This metadata JSON, however, can contain anything. # # @return [Pathname] attr_reader :directory # Initializes the collection. # # @param [Pathname] directory The directory that contains the collection # of boxes. def initialize(directory, options=nil) options ||= {} @directory = directory @lock = Mutex.new @temp_root = options[:temp_dir_root] @logger = Log4r::Logger.new("vagrant::box_collection") end # This adds a new box to the system. # # There are some exceptional cases: # * BoxAlreadyExists - The box you're attempting to add already exists. # * BoxProviderDoesntMatch - If the given box provider doesn't match the # actual box provider in the untarred box. # * BoxUnpackageFailure - An invalid tar file. # * BoxUpgradeRequired - You're attempting to add a box when there is a # V1 box with the same name that must first be upgraded. # # Preconditions: # * File given in `path` must exist. # # @param [Pathname] path Path to the box file on disk. # @param [String] name Logical name for the box. # @param [Symbol] provider The provider that the box should be for. This # will be verified with the `metadata.json` file in the box and is # meant as a basic check. If this isn't given, then whatever provider # the box represents will be added. # @param [Boolean] force If true, any existing box with the same name # and provider will be replaced. def add(path, name, formats=nil, force=false) formats = [formats] if formats && !formats.is_a?(Array) provider = nil with_collection_lock do # A helper to check if a box exists. We store this in a variable # since we call it multiple times. check_box_exists = lambda do |box_formats| box = find(name, box_formats) next if !box if !force @logger.error("Box already exists, can't add: #{name} #{box_formats.join(", ")}") raise Errors::BoxAlreadyExists, :name => name, :formats => box_formats.join(", ") end # We're forcing, so just delete the old box @logger.info( "Box already exists, but forcing so removing: #{name} #{box_formats.join(", ")}") box.destroy! end log_provider = formats ? formats.join(", ") : "any provider" @logger.debug("Adding box: #{name} (#{log_provider}) from #{path}") # Verify the box doesn't exist early if we're given a provider. This # can potentially speed things up considerably since we don't need # to unpack any files. check_box_exists.call(formats) if formats # Verify that a V1 box doesn't exist. If it does, then we signal # to the user that we need an upgrade. raise Errors::BoxUpgradeRequired, :name => name if v1_box?(@directory.join(name)) # Create a temporary directory since we're not sure at this point if # the box we're unpackaging already exists (if no provider was given) with_temp_dir do |temp_dir| # Extract the box into a temporary directory. @logger.debug("Unpacking box into temporary directory: #{temp_dir}") result = Util::Subprocess.execute( "bsdtar", "-v", "-x", "-m", "-C", temp_dir.to_s, "-f", path.to_s) raise Errors::BoxUnpackageFailure, :output => result.stderr.to_s if result.exit_code != 0 # If we get a V1 box, we want to update it in place if v1_box?(temp_dir) @logger.debug("Added box is a V1 box. Upgrading in place.") temp_dir = v1_upgrade(temp_dir) end # We re-wrap ourselves in the safety net in case we upgraded. # If we didn't upgrade, then this is still safe because the # helper will only delete the directory if it exists with_temp_dir(temp_dir) do |final_temp_dir| # Get an instance of the box we just added before it is finalized # in the system so we can inspect and use its metadata. box = Box.new(name, nil, final_temp_dir) # Get the provider, since we'll need that to at the least add it # to the system or check that it matches what is given to us. box_provider = box.metadata["provider"] if formats found = false formats.each do |format| # Verify that the given provider matches what the box has. if box_provider.to_sym == format.to_sym found = true break end end if !found @logger.error("Added box provider doesnt match expected: #{log_provider}") raise Errors::BoxProviderDoesntMatch, :expected => log_provider, :actual => box_provider end else # Verify the box doesn't already exist check_box_exists.call([box_provider]) end # We weren't given a provider, so store this one. provider = box_provider.to_sym # Create the directory for this box, not including the provider box_dir = @directory.join(name) box_dir.mkpath @logger.debug("Box directory: #{box_dir}") # This is the final directory we'll move it to final_dir = box_dir.join(provider.to_s) if final_dir.exist? @logger.debug("Removing existing provider directory...") final_dir.rmtree end # Move to final destination final_dir.mkpath # Go through each child and copy them one-by-one. This avoids # an issue where on Windows cross-device directory copies are # failing for some reason. [GH-1424] final_temp_dir.children(true).each do |f| destination = final_dir.join(f.basename) @logger.debug("Moving: #{f} => #{destination}") FileUtils.mv(f, destination) end end end end # Return the box find(name, provider) end # This returns an array of all the boxes on the system, given by # their name and their provider. # # @return [Array] Array of `[name, provider]` pairs of the boxes # installed on this system. An optional third element in the array # may specify `:v1` if the box is a version 1 box. def all results = [] with_collection_lock do @logger.debug("Finding all boxes in: #{@directory}") @directory.children(true).each do |child| # Ignore non-directories, since files are not interesting to # us in our folder structure. next if !child.directory? box_name = child.basename.to_s # If this is a V1 box, we still return that name, but specify # that the box is a V1 box. if v1_box?(child) @logger.debug("V1 box found: #{box_name}") results << [box_name, :virtualbox, :v1] next end # Otherwise, traverse the subdirectories and see what providers # we have. child.children(true).each do |provider| # Verify this is a potentially valid box. If it looks # correct enough then include it. if provider.directory? && provider.join("metadata.json").file? provider_name = provider.basename.to_s.to_sym @logger.debug("Box: #{box_name} (#{provider_name})") results << [box_name, provider_name] else @logger.debug("Invalid box, ignoring: #{provider}") end end end end results end # Find a box in the collection with the given name and provider. # # @param [String] name Name of the box (logical name). # @param [Array] providers Providers that the box implements. # @return [Box] The box found, or `nil` if not found. def find(name, providers) providers = [providers].flatten with_collection_lock do providers.each do |provider| # First look directly for the box we're asking for. box_directory = @directory.join(name, provider.to_s, "metadata.json") @logger.info("Searching for box: #{name} (#{provider}) in #{box_directory}") if box_directory.file? @logger.info("Box found: #{name} (#{provider})") return Box.new(name, provider, box_directory.dirname) end # If we're looking for a VirtualBox box, then we check if there is # a V1 box. if provider.to_sym == :virtualbox # Check if a V1 version of this box exists, and if so, raise an # exception notifying the caller that the box exists but needs # to be upgraded. We don't do the upgrade here because it can be # a fairly intensive activity and don't want to immediately degrade # user performance on a find. # # To determine if it is a V1 box we just do a simple heuristic # based approach. @logger.info("Searching for V1 box: #{name}") if v1_box?(@directory.join(name)) @logger.warn("V1 box found: #{name}") raise Errors::BoxUpgradeRequired, :name => name end end end end # Didn't find it, return nil @logger.info("Box not found: #{name} (#{providers.join(", ")})") nil end # Upgrades a V1 box with the given name to a V2 box. If a box with the # given name doesn't exist, then a `BoxNotFound` exception will be raised. # If the given box is found but is not a V1 box then `true` is returned # because this just works fine. # # @param [String] name Name of the box (logical name). # @return [Boolean] `true` otherwise an exception is raised. def upgrade(name) with_collection_lock do @logger.debug("Upgrade request for box: #{name}") box_dir = @directory.join(name) # If the box doesn't exist at all, raise an exception raise Errors::BoxNotFound, :name => name, :provider => "virtualbox" if !box_dir.directory? if v1_box?(box_dir) @logger.debug("V1 box #{name} found. Upgrading!") # First we actually perform the upgrade temp_dir = v1_upgrade(box_dir) # Rename the temporary directory to the provider. FileUtils.mv(temp_dir.to_s, box_dir.join("virtualbox").to_s) @logger.info("Box '#{name}' upgraded from V1 to V2.") end end # We did it! Or the v1 box didn't exist so it doesn't matter. return true end protected # This checks if the given directory represents a V1 box on the # system. # # @param [Pathname] dir Directory where the box is unpacked. # @return [Boolean] def v1_box?(dir) # We detect a V1 box given by whether there is a "box.ovf" which # is a heuristic but is pretty accurate. dir.join("box.ovf").file? end # This upgrades the V1 box contained unpacked in the given directory # and returns the directory of the upgraded version. This is # _destructive_ to the contents of the old directory. That is, the # contents of the old V1 box will be destroyed or moved. # # Preconditions: # * `dir` is a valid V1 box. Verify with {#v1_box?} # # @param [Pathname] dir Directory where the V1 box is unpacked. # @return [Pathname] Path to the unpackaged V2 box. def v1_upgrade(dir) @logger.debug("Upgrading box in directory: #{dir}") temp_dir = Pathname.new(Dir.mktmpdir(TEMP_PREFIX, @temp_root)) @logger.debug("Temporary directory for upgrading: #{temp_dir}") # Move all the things into the temporary directory dir.children(true).each do |child| # Don't move the temp_dir next if child == temp_dir # Move every other directory into the temporary directory @logger.debug("Copying to upgrade directory: #{child}") FileUtils.mv(child, temp_dir.join(child.basename)) end # If there is no metadata.json file, make one, since this is how # we determine if the box is a V2 box. metadata_file = temp_dir.join("metadata.json") if !metadata_file.file? metadata_file.open("w") do |f| f.write(JSON.generate({ :provider => "virtualbox" })) end end # Return the temporary directory temp_dir end # This locks the region given by the block with a lock on this # collection. def with_collection_lock lock = @lock begin lock.synchronize {} rescue ThreadError # If we already hold the lock, just create a new lock so # we definitely don't block and don't get an error. lock = Mutex.new end lock.synchronize do return yield end end # This is a helper that makes sure that our temporary directories # are cleaned up no matter what. # # @param [String] dir Path to a temporary directory # @return [Object] The result of whatever the yield is def with_temp_dir(dir=nil) dir ||= Dir.mktmpdir(TEMP_PREFIX, @temp_root) dir = Pathname.new(dir) yield dir ensure dir.rmtree if dir.exist? end end end vagrant-1.4.3/lib/vagrant/cli.rb000066400000000000000000000047651226132634600165060ustar00rootroot00000000000000require 'log4r' require 'optparse' module Vagrant # Manages the command line interface to Vagrant. class CLI < Vagrant.plugin("2", :command) def initialize(argv, env) super @logger = Log4r::Logger.new("vagrant::cli") @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) @logger.info("CLI: #{@main_args.inspect} #{@sub_command.inspect} #{@sub_args.inspect}") end def execute if @main_args.include?("-h") || @main_args.include?("--help") # Help is next in short-circuiting everything. Print # the help and exit. help return 0 end # If we reached this far then we must have a subcommand. If not, # then we also just print the help and exit. command_class = nil if @sub_command command_class = Vagrant.plugin("2").manager.commands[@sub_command.to_sym] end if !command_class || !@sub_command help return 1 end @logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}") # Initialize and execute the command class, returning the exit status. result = command_class.new(@sub_args, @env).execute result = 0 if !result.is_a?(Fixnum) return result end # This prints out the help for the CLI. def help # We use the optionparser for this. Its just easier. We don't use # an optionparser above because I don't think the performance hits # of creating a whole object are worth checking only a couple flags. opts = OptionParser.new do |o| o.banner = "Usage: vagrant [-v] [-h] command []" o.separator "" o.on("-v", "--version", "Print the version and exit.") o.on("-h", "--help", "Print this help.") o.separator "" o.separator "Available subcommands:" # Add the available subcommands as separators in order to print them # out as well. commands = {} longest = 0 Vagrant.plugin("2").manager.commands.each do |key, klass| key = key.to_s commands[key] = klass.synopsis longest = key.length if key.length > longest end commands.keys.sort.each do |key| o.separator " #{key.ljust(longest+2)} #{commands[key]}" @env.ui.machine("cli-command", key.dup) end o.separator "" o.separator "For help on any individual command run `vagrant COMMAND -h`" end @env.ui.info(opts.help, :prefix => false) end end end vagrant-1.4.3/lib/vagrant/config.rb000066400000000000000000000042271226132634600171750ustar00rootroot00000000000000require "vagrant/registry" module Vagrant module Config autoload :Loader, 'vagrant/config/loader' autoload :VersionBase, 'vagrant/config/version_base' autoload :V1, 'vagrant/config/v1' autoload :V2, 'vagrant/config/v2' # This is a mutex used to guarantee that only one thread can load # procs at any given time. CONFIGURE_MUTEX = Mutex.new # This is the registry which keeps track of what configuration # versions are available, mapped by the version string used in # `Vagrant.configure` calls. VERSIONS = Registry.new VERSIONS.register("1") { V1::Loader } VERSIONS.register("2") { V2::Loader } # This is the order of versions. This is used by the loader to figure out # how to "upgrade" versions up to the desired (current) version. The # current version is always considered to be the last version in this # list. VERSIONS_ORDER = ["1", "2"] CURRENT_VERSION = VERSIONS_ORDER.last # This is the method which is called by all Vagrantfiles to configure Vagrant. # This method expects a block which accepts a single argument representing # an instance of the {Config::Top} class. # # Note that the block is not run immediately. Instead, it's proc is stored # away for execution later. def self.run(version="1", &block) # Store it for later @last_procs ||= [] @last_procs << [version.to_s, block] end # This is a method which will yield to a block and will capture all # ``Vagrant.configure`` calls, returning an array of `Proc`s. # # Wrapping this around anytime you call code which loads configurations # will force a mutex so that procs never get mixed up. This keeps # the configuration loading part of Vagrant thread-safe. def self.capture_configures CONFIGURE_MUTEX.synchronize do # Reset the last procs so that we start fresh @last_procs = [] # Yield to allow the caller to do whatever loading needed yield # Return the last procs we've seen while still in the mutex, # knowing we're safe. return @last_procs end end end end vagrant-1.4.3/lib/vagrant/config/000077500000000000000000000000001226132634600166435ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/config/loader.rb000066400000000000000000000211231226132634600204350ustar00rootroot00000000000000require "pathname" require "log4r" module Vagrant module Config # This class is responsible for loading Vagrant configuration, # usually in the form of Vagrantfiles. # # Loading works by specifying the sources for the configuration # as well as the order the sources should be loaded. Configuration # set later always overrides those set earlier; this is how # configuration "scoping" is implemented. class Loader # Initializes a configuration loader. # # @param [Registry] versions A registry of the available versions and # their associated loaders. # @param [Array] version_order An array of the order of the versions # in the registry. This is used to determine if upgrades are # necessary. Additionally, the last version in this order is always # considered the "current" version. def initialize(versions, version_order) @logger = Log4r::Logger.new("vagrant::config::loader") @config_cache = {} @proc_cache = {} @sources = {} @versions = versions @version_order = version_order end # Set the configuration data for the given name. # # The `name` should be a symbol and must uniquely identify the data # being given. # # `data` can either be a path to a Ruby Vagrantfile or a `Proc` directly. # `data` can also be an array of such values. # # At this point, no configuration is actually loaded. Note that calling # `set` multiple times with the same name will override any previously # set values. In this way, the last set data for a given name wins. def set(name, sources) @logger.info("Set #{name.inspect} = #{sources.inspect}") # Sources should be an array sources = [sources] if !sources.kind_of?(Array) # Gather the procs for every source, since that is what we care about. procs = [] sources.each do |source| if !@proc_cache.has_key?(source) # Load the procs for this source and cache them. This caching # avoids the issue where a file may have side effects when loading # and loading it multiple times causes unexpected behavior. @logger.debug("Populating proc cache for #{source.inspect}") @proc_cache[source] = procs_for_source(source) end # Add on to the array of procs we're going to use procs.concat(@proc_cache[source]) end # Set this source by name. @sources[name] = procs end # This loads the configuration sources in the given order and returns # an actual configuration object that is ready to be used. # # @param [Array] order The order of configuration to load. # @return [Object] The configuration object. This is different for # each configuration version. def load(order) @logger.info("Loading configuration in order: #{order.inspect}") unknown_sources = @sources.keys - order if !unknown_sources.empty? # TODO: Raise exception here perhaps. @logger.error("Unknown config sources: #{unknown_sources.inspect}") end # Get the current version config class to use current_version = @version_order.last current_config_klass = @versions.get(current_version) # This will hold our result result = current_config_klass.init # Keep track of the warnings and errors that may come from # upgrading the Vagrantfiles warnings = [] errors = [] order.each do |key| next if !@sources.has_key?(key) @sources[key].each do |version, proc| if !@config_cache.has_key?(proc) @logger.debug("Loading from: #{key} (evaluating)") # Get the proper version loader for this version and load version_loader = @versions.get(version) version_config = version_loader.load(proc) # Store the errors/warnings associated with loading this # configuration. We'll store these for later. version_warnings = [] version_errors = [] # If this version is not the current version, then we need # to upgrade to the latest version. if version != current_version @logger.debug("Upgrading config from version #{version} to #{current_version}") version_index = @version_order.index(version) current_index = @version_order.index(current_version) (version_index + 1).upto(current_index) do |index| next_version = @version_order[index] @logger.debug("Upgrading config to version #{next_version}") # Get the loader of this version and ask it to upgrade loader = @versions.get(next_version) upgrade_result = loader.upgrade(version_config) this_warnings = upgrade_result[1] this_errors = upgrade_result[2] @logger.debug("Upgraded to version #{next_version} with " + "#{this_warnings.length} warnings and " + "#{this_errors.length} errors") # Append loading this to the version warnings and errors version_warnings += this_warnings version_errors += this_errors # Store the new upgraded version version_config = upgrade_result[0] end end # Cache the loaded configuration along with any warnings # or errors so that they can be retrieved later. @config_cache[proc] = [version_config, version_warnings, version_errors] else @logger.debug("Loading from: #{key} (cache)") end # Merge the configurations cache_data = @config_cache[proc] result = current_config_klass.merge(result, cache_data[0]) # Append the total warnings/errors warnings += cache_data[1] errors += cache_data[2] end end @logger.debug("Configuration loaded successfully, finalizing and returning") [current_config_klass.finalize(result), warnings, errors] end protected # This returns an array of `Proc` objects for the given source. # The `Proc` objects returned will expect a single argument for # the configuration object and are expected to mutate this # configuration object. def procs_for_source(source) # Convert all pathnames to strings so we just have their path source = source.to_s if source.is_a?(Pathname) if source.is_a?(Array) # An array must be formatted as [version, proc], so verify # that and then return it raise ArgumentError, "String source must have format [version, proc]" if source.length != 2 # Return it as an array since we're expected to return an array # of [version, proc] pairs, but an array source only has one. return [source] elsif source.is_a?(String) # Strings are considered paths, so load them return procs_for_path(source) else raise ArgumentError, "Unknown configuration source: #{source.inspect}" end end # This returns an array of `Proc` objects for the given path source. # # @param [String] path Path to the file which contains the proper # `Vagrant.configure` calls. # @return [Array] def procs_for_path(path) @logger.debug("Load procs for pathname: #{path}") return Config.capture_configures do begin Kernel.load path rescue SyntaxError => e # Report syntax errors in a nice way. raise Errors::VagrantfileSyntaxError, :file => e.message rescue SystemExit # Continue raising that exception... raise rescue Vagrant::Errors::VagrantError # Continue raising known Vagrant errors since they already # contain well worded error messages and context. raise rescue Exception => e @logger.error("Vagrantfile load error: #{e.message}") @logger.error(e.backtrace.join("\n")) # Report the generic exception raise Errors::VagrantfileLoadError, :path => path, :message => e.message end end end end end end vagrant-1.4.3/lib/vagrant/config/v1.rb000066400000000000000000000003411226132634600175140ustar00rootroot00000000000000module Vagrant module Config module V1 autoload :DummyConfig, "vagrant/config/v1/dummy_config" autoload :Loader, "vagrant/config/v1/loader" autoload :Root, "vagrant/config/v1/root" end end end vagrant-1.4.3/lib/vagrant/config/v1/000077500000000000000000000000001226132634600171715ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/config/v1/dummy_config.rb000066400000000000000000000004701226132634600221770ustar00rootroot00000000000000module Vagrant module Config module V1 # This is a configuration object that can have anything done # to it. Anything, and it just appears to keep working. class DummyConfig def method_missing(name, *args, &block) DummyConfig.new end end end end end vagrant-1.4.3/lib/vagrant/config/v1/loader.rb000066400000000000000000000065051226132634600207720ustar00rootroot00000000000000require "vagrant/config/v1/root" module Vagrant module Config module V1 # This is the loader that handles configuration loading for V1 # configurations. class Loader < VersionBase # Returns a bare empty configuration object. # # @return [V1::Root] def self.init new_root_object end # Finalizes the configuration by making sure there is at least # one VM defined in it. def self.finalize(config) # Call the `#finalize` method on each of the configuration keys. # They're expected to modify themselves in our case. config.finalize! # Return the object config end # Loads the configuration for the given proc and returns a configuration # object. # # @param [Proc] config_proc # @return [Object] def self.load(config_proc) # Create a root configuration object root = new_root_object # Call the proc with the root config_proc.call(root) # Return the root object, which doubles as the configuration object # we actually use for accessing as well. root end # Merges two configuration objects. # # @param [V1::Root] old The older root config. # @param [V1::Root] new The newer root config. # @return [V1::Root] def self.merge(old, new) # Grab the internal states, we use these heavily throughout the process old_state = old.__internal_state new_state = new.__internal_state # The config map for the new object is the old one merged with the # new one. config_map = old_state["config_map"].merge(new_state["config_map"]) # Merge the keys. old_keys = old_state["keys"] new_keys = new_state["keys"] keys = {} old_keys.each do |key, old_value| if new_keys.has_key?(key) # We need to do a merge, which we expect to be available # on the config class itself. keys[key] = old_value.merge(new_keys[key]) else # We just take the old value, but dup it so that we can modify. keys[key] = old_value.dup end end new_keys.each do |key, new_value| # Add in the keys that the new class has that we haven't merged. if !keys.has_key?(key) keys[key] = new_value.dup end end # Return the final root object V1::Root.new(config_map, keys) end protected def self.new_root_object # Get all the registered configuration objects and use them. If # we're currently on version 1, then we load all the config objects, # otherwise we load only the upgrade safe ones, since we're # obviously being loaded for an upgrade. config_map = nil plugin_manager = Vagrant.plugin("1").manager if Config::CURRENT_VERSION == "1" config_map = plugin_manager.config else config_map = plugin_manager.config_upgrade_safe end # Create the configuration root object V1::Root.new(config_map) end end end end end vagrant-1.4.3/lib/vagrant/config/v1/root.rb000066400000000000000000000037441226132634600205110ustar00rootroot00000000000000require "set" module Vagrant module Config module V1 # This is the root configuration class. An instance of this is what # is passed into version 1 Vagrant configuration blocks. class Root # Initializes a root object that maps the given keys to specific # configuration classes. # # @param [Hash] config_map Map of key to config class. def initialize(config_map, keys=nil) @keys = keys || {} @config_map = config_map @missing_key_calls = Set.new end # We use method_missing as a way to get the configuration that is # used for Vagrant and load the proper configuration classes for # each. def method_missing(name, *args) return @keys[name] if @keys.has_key?(name) config_klass = @config_map[name.to_sym] if config_klass # Instantiate the class and return the instance @keys[name] = config_klass.new return @keys[name] else # Record access to a missing key as an error @missing_key_calls.add(name.to_s) return DummyConfig.new end end # Called to finalize this object just prior to it being used by # the Vagrant system. The "!" signifies that this is expected to # mutate itself. def finalize! @keys.each do |_key, instance| instance.finalize! end end # Returns the internal state of the root object. This is used # by outside classes when merging, and shouldn't be called directly. # Note the strange method name is to attempt to avoid any name # clashes with potential configuration keys. def __internal_state { "config_map" => @config_map, "keys" => @keys, "missing_key_calls" => @missing_key_calls } end end end end end vagrant-1.4.3/lib/vagrant/config/v2.rb000066400000000000000000000003411226132634600175150ustar00rootroot00000000000000module Vagrant module Config module V2 autoload :DummyConfig, "vagrant/config/v2/dummy_config" autoload :Loader, "vagrant/config/v2/loader" autoload :Root, "vagrant/config/v2/root" end end end vagrant-1.4.3/lib/vagrant/config/v2/000077500000000000000000000000001226132634600171725ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/config/v2/dummy_config.rb000066400000000000000000000004701226132634600222000ustar00rootroot00000000000000module Vagrant module Config module V2 # This is a configuration object that can have anything done # to it. Anything, and it just appears to keep working. class DummyConfig def method_missing(name, *args, &block) DummyConfig.new end end end end end vagrant-1.4.3/lib/vagrant/config/v2/loader.rb000066400000000000000000000106631226132634600207730ustar00rootroot00000000000000require "vagrant/config/v2/root" module Vagrant module Config module V2 # This is the loader that handles configuration loading for V2 # configurations. class Loader < VersionBase # Returns a bare empty configuration object. # # @return [V2::Root] def self.init new_root_object end # Finalizes the configuration by making sure there is at least # one VM defined in it. def self.finalize(config) # Call the `#finalize` method on each of the configuration keys. # They're expected to modify themselves in our case. config.finalize! # Return the object config end # Loads the configuration for the given proc and returns a configuration # object. # # @param [Proc] config_proc # @return [Object] def self.load(config_proc) # Create a root configuration object root = new_root_object # Call the proc with the root config_proc.call(root) # Return the root object, which doubles as the configuration object # we actually use for accessing as well. root end # Merges two configuration objects. # # @param [V2::Root] old The older root config. # @param [V2::Root] new The newer root config. # @return [V2::Root] def self.merge(old, new) # Grab the internal states, we use these heavily throughout the process old_state = old.__internal_state new_state = new.__internal_state # The config map for the new object is the old one merged with the # new one. config_map = old_state["config_map"].merge(new_state["config_map"]) # Merge the keys. old_keys = old_state["keys"] new_keys = new_state["keys"] keys = {} old_keys.each do |key, old_value| if new_keys.has_key?(key) # We need to do a merge, which we expect to be available # on the config class itself. keys[key] = old_value.merge(new_keys[key]) else # We just take the old value, but dup it so that we can modify. keys[key] = old_value.dup end end new_keys.each do |key, new_value| # Add in the keys that the new class has that we haven't merged. if !keys.has_key?(key) keys[key] = new_value.dup end end # Merge the missing keys new_missing_key_calls = old_state["missing_key_calls"] + new_state["missing_key_calls"] # Return the final root object V2::Root.new(config_map).tap do |result| result.__set_internal_state({ "config_map" => config_map, "keys" => keys, "missing_key_calls" => new_missing_key_calls }) end end # Upgrade a V1 configuration to a V2 configuration. We do this by # creating a V2 configuration, and calling "upgrade" on each of the # V1 configurations, expecting them to set the right settings on the # new root. # # @param [V1::Root] old # @return [Array] A 3-tuple result. def self.upgrade(old) # Get a new root root = new_root_object # Store the warnings/errors warnings = [] errors = [] # Go through the old keys and upgrade them if they can be old.__internal_state["keys"].each do |_, old_value| if old_value.respond_to?(:upgrade) result = old_value.upgrade(root) # Sanity check to guard against random return values if result.is_a?(Array) warnings += result[0] errors += result[1] end end end old.__internal_state["missing_key_calls"].to_a.sort.each do |key| warnings << I18n.t("vagrant.config.loader.bad_v1_key", :key => key) end [root, warnings, errors] end protected def self.new_root_object # Get all the registered plugins for V2 config_map = Vagrant.plugin("2").manager.config # Create the configuration root object V2::Root.new(config_map) end end end end end vagrant-1.4.3/lib/vagrant/config/v2/root.rb000066400000000000000000000074331226132634600205110ustar00rootroot00000000000000require "set" require "vagrant/config/v2/util" module Vagrant module Config module V2 # This is the root configuration class. An instance of this is what # is passed into version 2 Vagrant configuration blocks. class Root # Initializes a root object that maps the given keys to specific # configuration classes. # # @param [Hash] config_map Map of key to config class. def initialize(config_map, keys=nil) @keys = keys || {} @config_map = config_map @missing_key_calls = Set.new end # We use method_missing as a way to get the configuration that is # used for Vagrant and load the proper configuration classes for # each. def method_missing(name, *args) return @keys[name] if @keys.has_key?(name) config_klass = @config_map[name.to_sym] if config_klass # Instantiate the class and return the instance @keys[name] = config_klass.new return @keys[name] else # Record access to a missing key as an error @missing_key_calls.add(name.to_s) return DummyConfig.new end end # Called to finalize this object just prior to it being used by # the Vagrant system. The "!" signifies that this is expected to # mutate itself. def finalize! @config_map.each do |key, klass| if !@keys.has_key?(key) @keys[key] = klass.new end end @keys.each do |_key, instance| instance.finalize! end end # This validates the configuration and returns a hash of error # messages by section. If there are no errors, an empty hash # is returned. # # @param [Environment] env # @return [Hash] def validate(machine) # Go through each of the configuration keys and validate errors = {} @keys.each do |_key, instance| if instance.respond_to?(:validate) # Validate this single item, and if we have errors then # we merge them into our total errors list. result = instance.validate(machine) if result && !result.empty? errors = Util.merge_errors(errors, result) end end end # Go through and delete empty keys errors.keys.each do |key| errors.delete(key) if errors[key].empty? end # If we have missing keys, record those as errors if !@missing_key_calls.empty? errors["Vagrant"] = @missing_key_calls.to_a.sort.map do |key| I18n.t("vagrant.config.root.bad_key", :key => key) end end errors end # Returns the internal state of the root object. This is used # by outside classes when merging, and shouldn't be called directly. # Note the strange method name is to attempt to avoid any name # clashes with potential configuration keys. def __internal_state { "config_map" => @config_map, "keys" => @keys, "missing_key_calls" => @missing_key_calls } end # This sets the internal state. This is used by the core to do some # merging logic and shouldn't be used by the general public. def __set_internal_state(state) @config_map = state["config_map"] if state.has_key?("config_map") @keys = state["keys"] if state.has_key?("keys") @missing_key_calls = state["missing_key_calls"] if state.has_key?("missing_key_calls") end end end end end vagrant-1.4.3/lib/vagrant/config/v2/util.rb000066400000000000000000000007551226132634600205030ustar00rootroot00000000000000module Vagrant module Config module V2 class Util # This merges two error hashes from validate methods. # # @param [Hash] first # @param [Hash] second # @return [Hash] Merged result def self.merge_errors(first, second) first.dup.tap do |result| second.each do |key, value| result[key] ||= [] result[key] += value end end end end end end end vagrant-1.4.3/lib/vagrant/config/version_base.rb000066400000000000000000000065351226132634600216600ustar00rootroot00000000000000module Vagrant module Config # This is the base class for any configuration versions, and includes # the stub methods that configuaration versions must implement. Vagrant # supports configuration versioning so that backwards compatibility can be # maintained for past Vagrantfiles while newer configurations are added. # Vagrant only introduces new configuration versions for major versions # of Vagrant. class VersionBase # Returns an empty configuration object. This can be any kind of object, # since it is treated as an opaque value on the other side, used only # for things like calling into {merge}. # # @return [Object] def self.init raise NotImplementedError end # This is called just before configuration loading is complete of # a potentially completely-merged value to perform final touch-ups # to the configuration, if required. # # This is an optional method to implement. The default implementation # will simply return the same object. # # This will ONLY be called if this is the version that is being # used. In the case that an `upgrade` is called, this will never # be called. # # @param [Object] obj Final configuration object. # @param [Object] Finalized configuration object. def self.finalize(obj) obj end # Loads the configuration for the given proc and returns a configuration # object. The return value is treated as an opaque object, so it can be # anything you'd like. The return value is the object that is passed # into methods like {merge}, so it should be something you expect. # # @param [Proc] proc The proc that is to be configured. # @return [Object] def self.load(proc) raise NotImplementedError end # Merges two configuration objects, returning the merged object. # The values of `old` and `new` are the opaque objects returned by # {load} or {init}. # # Once again, the return object is treated as an opaque value by # the Vagrant configuration loader, so it can be anything you'd like. # # @param [Object] old Old configuration object. # @param [Object] new New configuration object. # @return [Object] The merged configuration object. def self.merge(old, new) raise NotImplementedError end # This is called if a previous version of configuration needs to be # upgraded to this version. Each version of configuration should know # how to upgrade the version immediately prior to it. This should be # a best effort upgrade that makes many assumptions. The goal is for # this to work in almost every case, but perhaps with some warnings. # The return value for this is a 3-tuple: `[object, warnings, errors]`, # where `object` is the upgraded configuration object, `warnings` is # an array of warning messages, and `errors` is an array of error # messages. # # @param [Object] old The version of the configuration object just # prior to this one. # @return [Array] The 3-tuple result. Please see the above documentation # for more information on the exact structure of this object. def self.upgrade(old) raise NotImplementedError end end end end vagrant-1.4.3/lib/vagrant/environment.rb000066400000000000000000000740701226132634600202770ustar00rootroot00000000000000require 'fileutils' require 'json' require 'pathname' require 'set' require 'thread' require 'log4r' require 'vagrant/util/file_mode' require 'vagrant/util/platform' module Vagrant # Represents a single Vagrant environment. A "Vagrant environment" is # defined as basically a folder with a "Vagrantfile." This class allows # access to the VMs, CLI, etc. all in the scope of this environment. class Environment DEFAULT_LOCAL_DATA = ".vagrant" # The `cwd` that this environment represents attr_reader :cwd # The persistent data directory where global data can be stored. It # is up to the creator of the data in this directory to properly # remove it when it is no longer needed. # # @return [Pathname] attr_reader :data_dir # The valid name for a Vagrantfile for this environment. attr_reader :vagrantfile_name # The {UI} object to communicate with the outside world. attr_reader :ui # This is the UI class to use when creating new UIs. attr_reader :ui_class # The directory to the "home" folder that Vagrant will use to store # global state. attr_reader :home_path # The directory to the directory where local, environment-specific # data is stored. attr_reader :local_data_path # The directory where temporary files for Vagrant go. attr_reader :tmp_path # The directory where boxes are stored. attr_reader :boxes_path # The path where the plugins are stored (gems) attr_reader :gems_path # The path to the default private key attr_reader :default_private_key_path # Initializes a new environment with the given options. The options # is a hash where the main available key is `cwd`, which defines where # the environment represents. There are other options available but # they shouldn't be used in general. If `cwd` is nil, then it defaults # to the `Dir.pwd` (which is the cwd of the executing process). def initialize(opts=nil) opts = { :cwd => nil, :home_path => nil, :local_data_path => nil, :lock_path => nil, :ui_class => nil, :vagrantfile_name => nil, }.merge(opts || {}) # Set the default working directory to look for the vagrantfile opts[:cwd] ||= ENV["VAGRANT_CWD"] if ENV.has_key?("VAGRANT_CWD") opts[:cwd] ||= Dir.pwd opts[:cwd] = Pathname.new(opts[:cwd]) if !opts[:cwd].directory? raise Errors::EnvironmentNonExistentCWD, cwd: opts[:cwd].to_s end # Set the default ui class opts[:ui_class] ||= UI::Silent # Set the Vagrantfile name up. We append "Vagrantfile" and "vagrantfile" so that # those continue to work as well, but anything custom will take precedence. opts[:vagrantfile_name] ||= ENV["VAGRANT_VAGRANTFILE"] if \ ENV.has_key?("VAGRANT_VAGRANTFILE") opts[:vagrantfile_name] = [opts[:vagrantfile_name]] if \ opts[:vagrantfile_name] && !opts[:vagrantfile_name].is_a?(Array) # Set instance variables for all the configuration parameters. @cwd = opts[:cwd] @home_path = opts[:home_path] @lock_path = opts[:lock_path] @vagrantfile_name = opts[:vagrantfile_name] @ui = opts[:ui_class].new @ui_class = opts[:ui_class] # This is the batch lock, that enforces that only one {BatchAction} # runs at a time from {#batch}. @batch_lock = Mutex.new @lock_acquired = false @logger = Log4r::Logger.new("vagrant::environment") @logger.info("Environment initialized (#{self})") @logger.info(" - cwd: #{cwd}") # Setup the home directory setup_home_path @boxes_path = @home_path.join("boxes") @data_dir = @home_path.join("data") @gems_path = @home_path.join("gems") @tmp_path = @home_path.join("tmp") # Setup the local data directory. If a configuration path is given, # then it is expanded relative to the working directory. Otherwise, # we use the default which is expanded relative to the root path. opts[:local_data_path] ||= ENV["VAGRANT_DOTFILE_PATH"] opts[:local_data_path] ||= root_path.join(DEFAULT_LOCAL_DATA) if !root_path.nil? if opts[:local_data_path] @local_data_path = Pathname.new(File.expand_path(opts[:local_data_path], @cwd)) end setup_local_data_path # Setup the default private key @default_private_key_path = @home_path.join("insecure_private_key") copy_insecure_private_key # Load the plugins load_plugins # Call the hooks that does not require configurations to be loaded # by using a "clean" action runner hook(:environment_plugins_loaded, runner: Action::Runner.new(env: self)) # Call the environment load hooks hook(:environment_load) end # Return a human-friendly string for pretty printed or inspected # instances. # # @return [String] def inspect "#<#{self.class}: #{@cwd}>" end #--------------------------------------------------------------- # Helpers #--------------------------------------------------------------- # Returns a list of machines that this environment is currently # managing that physically have been created. # # An "active" machine is a machine that Vagrant manages that has # been created. The machine itself may be in any state such as running, # suspended, etc. but if a machine is "active" then it exists. # # Note that the machines in this array may no longer be present in # the Vagrantfile of this environment. In this case the machine can # be considered an "orphan." Determining which machines are orphan # and which aren't is not currently a supported feature, but will # be in a future version. # # @return [Array] def active_machines machine_folder = @local_data_path.join("machines") # If the machine folder is not a directory then we just return # an empty array since no active machines exist. return [] if !machine_folder.directory? # Traverse the machines folder accumulate a result result = [] machine_folder.children(true).each do |name_folder| # If this isn't a directory then it isn't a machine next if !name_folder.directory? name = name_folder.basename.to_s.to_sym name_folder.children(true).each do |provider_folder| # If this isn't a directory then it isn't a provider next if !provider_folder.directory? # If this machine doesn't have an ID, then ignore next if !provider_folder.join("id").file? provider = provider_folder.basename.to_s.to_sym result << [name, provider] end end # Return the results result end # This creates a new batch action, yielding it, and then running it # once the block is called. # # This handles the case where batch actions are disabled by the # VAGRANT_NO_PARALLEL environmental variable. def batch(parallel=true) parallel = false if ENV["VAGRANT_NO_PARALLEL"] @batch_lock.synchronize do BatchAction.new(parallel).tap do |b| # Yield it so that the caller can setup actions yield b # And run it! b.run end end end # This returns the provider name for the default provider for this # environment. The provider returned is currently hardcoded to "virtualbox" # but one day should be a detected valid, best-case provider for this # environment. # # @return [Symbol] Name of the default provider. def default_provider (ENV['VAGRANT_DEFAULT_PROVIDER'] || :virtualbox).to_sym end # Returns the collection of boxes for the environment. # # @return [BoxCollection] def boxes @_boxes ||= BoxCollection.new(boxes_path, temp_dir_root: tmp_path) end # This is the global config, comprised of loading configuration from # the default, home, and root Vagrantfiles. This configuration is only # really useful for reading the list of virtual machines, since each # individual VM can override _most_ settings. # # This is lazy-loaded upon first use. # # @return [Object] def config_global return @config_global if @config_global @logger.info("Initializing config...") home_vagrantfile = nil root_vagrantfile = nil home_vagrantfile = find_vagrantfile(home_path) if home_path root_vagrantfile = find_vagrantfile(root_path, @vagrantfile_name) if root_path # Create the configuration loader and set the sources that are global. # We use this to load the configuration, and the list of machines we are # managing. Then, the actual individual configuration is loaded for # each {#machine} call. @config_loader = Config::Loader.new(Config::VERSIONS, Config::VERSIONS_ORDER) @config_loader.set(:default, File.expand_path("config/default.rb", Vagrant.source_root)) @config_loader.set(:home, home_vagrantfile) if home_vagrantfile @config_loader.set(:root, root_vagrantfile) if root_vagrantfile # Make the initial call to get the "global" config. This is mostly # only useful to get the list of machines that we are managing. # Because of this, we ignore any warnings or errors. @config_global, _ = @config_loader.load([:default, :home, :root]) # Return the config @config_global end # This defines a hook point where plugin action hooks that are registered # against the given name will be run in the context of this environment. # # @param [Symbol] name Name of the hook. # @param [Action::Runner] action_runner A custom action runner for running hooks. def hook(name, opts=nil) @logger.info("Running hook: #{name}") opts ||= {} opts[:callable] ||= Action::Builder.new opts[:runner] ||= action_runner opts[:action_name] = name opts[:env] = self opts.delete(:runner).run(opts.delete(:callable), opts) end # This returns a machine with the proper provider for this environment. # The machine named by `name` must be in this environment. # # @param [Symbol] name Name of the machine (as configured in the # Vagrantfile). # @param [Symbol] provider The provider that this machine should be # backed by. # @param [Boolean] refresh If true, then if there is a cached version # it is reloaded. # @return [Machine] def machine(name, provider, refresh=false) @logger.info("Getting machine: #{name} (#{provider})") # Compose the cache key of the name and provider, and return from # the cache if we have that. cache_key = [name, provider] @machines ||= {} if refresh @logger.info("Refreshing machine (busting cache): #{name} (#{provider})") @machines.delete(cache_key) end if @machines.has_key?(cache_key) @logger.info("Returning cached machine: #{name} (#{provider})") return @machines[cache_key] end @logger.info("Uncached load of machine.") sub_vm = config_global.vm.defined_vms[name] if !sub_vm raise Errors::MachineNotFound, :name => name, :provider => provider end provider_plugin = Vagrant.plugin("2").manager.providers[provider] if !provider_plugin raise Errors::ProviderNotFound, :machine => name, :provider => provider end # Extra the provider class and options from the plugin data provider_cls = provider_plugin[0] provider_options = provider_plugin[1] # Build the machine configuration. This requires two passes: The first pass # loads in the machine sub-configuration. Since this can potentially # define a new box to base the machine from, we then make a second pass # with the box Vagrantfile (if it has one). vm_config_key = "vm_#{name}".to_sym @config_loader.set(vm_config_key, sub_vm.config_procs) config, config_warnings, config_errors = \ @config_loader.load([:default, :home, :root, vm_config_key]) # Determine the possible box formats for any boxes and find the box box_formats = provider_options[:box_format] || provider box = nil # Set this variable in order to keep track of if the box changes # too many times. original_box = config.vm.box box_changed = false load_box_and_overrides = lambda do box = nil if config.vm.box begin box = boxes.find(config.vm.box, box_formats) rescue Errors::BoxUpgradeRequired # Upgrade the box if we must @logger.info("Upgrading box during config load: #{config.vm.box}") boxes.upgrade(config.vm.box) retry end end # If a box was found, then we attempt to load the Vagrantfile for # that box. We don't require a box since we allow providers to download # boxes and so on. if box box_vagrantfile = find_vagrantfile(box.directory) if box_vagrantfile # The box has a custom Vagrantfile, so we load that into the config # as well. @logger.info("Box exists with Vagrantfile. Reloading machine config.") box_config_key = "box_#{box.name}_#{box.provider}".to_sym @config_loader.set(box_config_key, box_vagrantfile) config, config_warnings, config_errors = \ @config_loader.load([:default, box_config_key, :home, :root, vm_config_key]) end end # If there are provider overrides for the machine, then we run # those as well. provider_overrides = config.vm.get_provider_overrides(provider) if provider_overrides.length > 0 @logger.info("Applying #{provider_overrides.length} provider overrides. Reloading config.") provider_override_key = "vm_#{name}_#{config.vm.box}_#{provider}".to_sym @config_loader.set(provider_override_key, provider_overrides) config, config_warnings, config_errors = \ @config_loader.load([:default, box_config_key, :home, :root, vm_config_key, provider_override_key]) end if config.vm.box && original_box != config.vm.box if box_changed # We already changed boxes once, so report an error that a # box is attempting to change boxes again. raise Errors::BoxConfigChangingBox end # The box changed, probably due to the provider override. Let's # run the configuration one more time with the new box. @logger.info("Box changed to: #{config.vm.box}. Reloading configurations.") original_box = config.vm.box box_changed = true # Recurse so that we reload all the configurations load_box_and_overrides.call end end # Load the box and overrides configuration load_box_and_overrides.call # Get the provider configuration from the final loaded configuration provider_config = config.vm.get_provider_config(provider) # Determine the machine data directory and pass it to the machine. # XXX: Permissions error here. machine_data_path = @local_data_path.join("machines/#{name}/#{provider}") FileUtils.mkdir_p(machine_data_path) # If there were warnings or errors we want to output them if !config_warnings.empty? || !config_errors.empty? # The color of the output depends on whether we have warnings # or errors... level = config_errors.empty? ? :warn : :error output = Util::TemplateRenderer.render( "config/messages", :warnings => config_warnings, :errors => config_errors).chomp @ui.send(level, I18n.t("vagrant.general.config_upgrade_messages", name: name, :output => output)) # If we had errors, then we bail raise Errors::ConfigUpgradeErrors if !config_errors.empty? end # Create the machine and cache it for future calls. This will also # return the machine from this method. @machines[cache_key] = Machine.new(name, provider, provider_cls, provider_config, provider_options, config, machine_data_path, box, self) end # This returns a list of the configured machines for this environment. # Each of the names returned by this method is valid to be used with # the {#machine} method. # # @return [Array] Configured machine names. def machine_names config_global.vm.defined_vm_keys.dup end # This returns the name of the machine that is the "primary." In the # case of a single-machine environment, this is just the single machine # name. In the case of a multi-machine environment, then this can # potentially be nil if no primary machine is specified. # # @return [Symbol] def primary_machine_name # If it is a single machine environment, then return the name return machine_names.first if machine_names.length == 1 # If it is a multi-machine environment, then return the primary config_global.vm.defined_vms.each do |name, subvm| return name if subvm.options[:primary] end # If no primary was specified, nil it is nil end # Unload the environment, running completion hooks. The environment # should not be used after this (but CAN be, technically). It is # recommended to always immediately set the variable to `nil` after # running this so you can't accidentally run any more methods. Example: # # env.unload # env = nil # def unload hook(:environment_unload) end # Makes a call to the CLI with the given arguments as if they # came from the real command line (sometimes they do!). An example: # # env.cli("package", "--vagrantfile", "Vagrantfile") # def cli(*args) CLI.new(args.flatten, self).execute end # Returns the host object associated with this environment. # # @return [Class] def host return @host if defined?(@host) # Attempt to figure out the host class. Note that the order # matters here, so please don't touch. Specifically: The symbol # check is done after the detect check because the symbol check # will return nil, and we don't want to trigger a detect load. host_klass = config_global.vagrant.host if host_klass.nil? || host_klass == :detect hosts = Vagrant.plugin("2").manager.hosts.to_hash # Get the flattened list of available hosts host_klass = Hosts.detect(hosts) end # If no host class is detected, we use the base class. host_klass ||= Vagrant.plugin("2", :host) @host ||= host_klass.new(@ui) end # Action runner for executing actions in the context of this environment. # # @return [Action::Runner] def action_runner @action_runner ||= Action::Runner.new do { :action_runner => action_runner, :box_collection => boxes, :global_config => config_global, :hook => method(:hook), :host => host, :gems_path => gems_path, :home_path => home_path, :root_path => root_path, :tmp_path => tmp_path, :ui => @ui } end end # The root path is the path where the top-most (loaded last) # Vagrantfile resides. It can be considered the project root for # this environment. # # @return [String] def root_path return @root_path if defined?(@root_path) root_finder = lambda do |path| # Note: To remain compatible with Ruby 1.8, we have to use # a `find` here instead of an `each`. vf = find_vagrantfile(path, @vagrantfile_name) return path if vf return nil if path.root? || !File.exist?(path) root_finder.call(path.parent) end @root_path = root_finder.call(cwd) end # This returns the path which Vagrant uses to determine the location # of the file lock. This is specific to each operating system. def lock_path @lock_path || tmp_path.join("vagrant.lock") end # This locks Vagrant for the duration of the block passed to this # method. During this time, any other environment which attempts # to lock which points to the same lock file will fail. def lock # This allows multiple locks in the same process to be nested return yield if @lock_acquired File.open(lock_path, "w+") do |f| # The file locking fails only if it returns "false." If it # succeeds it returns a 0, so we must explicitly check for # the proper error case. raise Errors::EnvironmentLockedError if f.flock(File::LOCK_EX | File::LOCK_NB) === false begin # Mark that we have a lock @lock_acquired = true yield ensure # We need to make sure that no matter what this is always # reset to false so we don't think we have a lock when we # actually don't. @lock_acquired = false end end end #--------------------------------------------------------------- # Load Methods #--------------------------------------------------------------- # This sets the `@home_path` variable properly. # # @return [Pathname] def setup_home_path @home_path = Pathname.new(File.expand_path(@home_path || ENV["VAGRANT_HOME"] || default_home_path)) @logger.info("Home path: #{@home_path}") # Setup the list of child directories that need to be created if they # don't already exist. dirs = [@home_path] subdirs = ["boxes", "data", "gems", "rgloader", "tmp"] dirs += subdirs.collect { |subdir| @home_path.join(subdir) } # Go through each required directory, creating it if it doesn't exist dirs.each do |dir| next if File.directory?(dir) begin @logger.info("Creating: #{dir}") FileUtils.mkdir_p(dir) rescue Errno::EACCES raise Errors::HomeDirectoryNotAccessible, :home_path => @home_path.to_s end end # Create the version file to mark the version of the home directory # we're using. version_file = @home_path.join("setup_version") if !version_file.file? @logger.debug("Setting up the version file.") version_file.open("w") do |f| f.write("1.1") end end # Create the rgloader/loader file so we can use encoded files. loader_file = @home_path.join("rgloader", "loader.rb") if !loader_file.file? source_loader = Vagrant.source_root.join("templates/rgloader.rb") FileUtils.cp(source_loader.to_s, loader_file.to_s) end end # This creates the local data directory and show an error if it # couldn't properly be created. def setup_local_data_path if @local_data_path.nil? @logger.warn("No local data path is set. Local data cannot be stored.") return end @logger.info("Local data path: #{@local_data_path}") # If the local data path is a file, then we are probably seeing an # old (V1) "dotfile." In this case, we upgrade it. The upgrade process # will remove the old data file if it is successful. if @local_data_path.file? upgrade_v1_dotfile(@local_data_path) end begin @logger.debug("Creating: #{@local_data_path}") FileUtils.mkdir_p(@local_data_path) rescue Errno::EACCES raise Errors::LocalDataDirectoryNotAccessible, :local_data_path => @local_data_path.to_s end end protected # This method copies the private key into the home directory if it # doesn't already exist. # # This must be done because `ssh` requires that the key is chmod # 0600, but if Vagrant is installed as a separate user, then the # effective uid won't be able to read the key. So the key is copied # to the home directory and chmod 0600. def copy_insecure_private_key if !@default_private_key_path.exist? @logger.info("Copying private key to home directory") source = File.expand_path("keys/vagrant", Vagrant.source_root) destination = @default_private_key_path begin FileUtils.cp(source, destination) rescue Errno::EACCES raise Errors::CopyPrivateKeyFailed, :source => source, :destination => destination end end if !Util::Platform.windows? # On Windows, permissions don't matter as much, so don't worry # about doing chmod. if Util::FileMode.from_octal(@default_private_key_path.stat.mode) != "600" @logger.info("Changing permissions on private key to 0600") @default_private_key_path.chmod(0600) end end end # This returns the default home directory path for Vagrant, which # can differ depending on the system. # # @return [Pathname] def default_home_path path = "~/.vagrant.d" # On Windows, we default ot the USERPROFILE directory if it # is available. This is more compatible with Cygwin and sharing # the home directory across shells. if Util::Platform.windows? && ENV["USERPROFILE"] path = "#{ENV["USERPROFILE"]}/.vagrant.d" end Pathname.new(path) end # Finds the Vagrantfile in the given directory. # # @param [Pathname] path Path to search in. # @return [Pathname] def find_vagrantfile(search_path, filenames=nil) filenames ||= ["Vagrantfile", "vagrantfile"] filenames.each do |vagrantfile| current_path = search_path.join(vagrantfile) return current_path if current_path.file? end nil end # Loads the Vagrant plugins by properly setting up RubyGems so that # our private gem repository is on the path. def load_plugins # Add our private gem path to the gem path and reset the paths # that Rubygems knows about. ENV["GEM_PATH"] = "#{@gems_path}#{::File::PATH_SEPARATOR}#{ENV["GEM_PATH"]}" ::Gem.clear_paths # If we're in a Bundler environment, don't load plugins. This only # happens in plugin development environments. if defined?(Bundler) require 'bundler/shared_helpers' if Bundler::SharedHelpers.in_bundle? @logger.warn("In a bundler environment, not loading environment plugins!") return end end # This keeps track of the old plugins that need to be reinstalled # because they were installed with an old version of Ruby. reinstall = [] # Load the plugins plugins_json_file = @home_path.join("plugins.json") @logger.debug("Loading plugins from: #{plugins_json_file}") state = VagrantPlugins::CommandPlugin::StateFile.new(plugins_json_file) state.installed_plugins.each do |name, extra| # If the Ruby version changed, then they need to reinstall the plugin if extra["ruby_version"] != RUBY_VERSION reinstall << name next end @logger.info("Loading plugin from JSON: #{name}") begin Vagrant.require_plugin(name) rescue Errors::PluginLoadError => e @ui.error(e.message + "\n") rescue Errors::PluginLoadFailed => e @ui.error(e.message + "\n") end end if !reinstall.empty? @ui.warn(I18n.t("vagrant.plugin_needs_reinstall", names: reinstall.join(", "))) end end # This upgrades a Vagrant 1.0.x "dotfile" to the new V2 format. # # This is a destructive process. Once the upgrade is complete, the # old dotfile is removed, and the environment becomes incompatible for # Vagrant 1.0 environments. # # @param [Pathname] path The path to the dotfile def upgrade_v1_dotfile(path) @logger.info("Upgrading V1 dotfile to V2 directory structure...") # First, verify the file isn't empty. If it is an empty file, we # just delete it and go on with life. contents = path.read.strip if contents.strip == "" @logger.info("V1 dotfile was empty. Removing and moving on.") path.delete return end # Otherwise, verify there is valid JSON in here since a Vagrant # environment would always ensure valid JSON. This is a sanity check # to make sure we don't nuke a dotfile that is not ours... @logger.debug("Attempting to parse JSON of V1 file") json_data = nil begin json_data = JSON.parse(contents) @logger.debug("JSON parsed successfully. Things are okay.") rescue JSON::ParserError # The file could've been tampered with since Vagrant 1.0.x is # supposed to ensure that the contents are valid JSON. Show an error. raise Errors::DotfileUpgradeJSONError, :state_file => path.to_s end # Alright, let's upgrade this guy to the new structure. Start by # backing up the old dotfile. backup_file = path.dirname.join(".vagrant.v1.#{Time.now.to_i}") @logger.info("Renaming old dotfile to: #{backup_file}") path.rename(backup_file) # Now, we create the actual local data directory. This should succeed # this time since we renamed the old conflicting V1. setup_local_data_path if json_data["active"] @logger.debug("Upgrading to V2 style for each active VM") json_data["active"].each do |name, id| @logger.info("Upgrading dotfile: #{name} (#{id})") # Create the machine configuration directory directory = @local_data_path.join("machines/#{name}/virtualbox") FileUtils.mkdir_p(directory) # Write the ID file directory.join("id").open("w+") do |f| f.write(id) end end end # Upgrade complete! Let the user know @ui.info(I18n.t("vagrant.general.upgraded_v1_dotfile", :backup_path => backup_file.to_s)) end end end vagrant-1.4.3/lib/vagrant/errors.rb000066400000000000000000000426731226132634600172530ustar00rootroot00000000000000# This file contains all of the internal errors in Vagrant's core # commands, actions, etc. module Vagrant # This module contains all of the internal errors in Vagrant's core. # These errors are _expected_ errors and as such don't typically represent # bugs in Vagrant itself. These are meant as a way to detect errors and # display them in a user-friendly way. # # # Defining a new Error # # To define a new error, inherit from {VagrantError}, which lets Vagrant # know that this is an expected error, and also gives you some helpers for # providing exit codes and error messages. An example is shown below, then # it is explained: # # class MyError < Vagrant::Errors::VagrantError # error_key "my_error" # end # # This creates an error with an I18n error key of "my_error." {VagrantError} # uses I18n to look up error messages, in the "vagrant.errors" namespace. So # in the above, the error message would be the translation of "vagrant.errors.my_error" # # If you don't want to use I18n, you can override the {#initialize} method and # set your own error message. # # # Raising an Error # # To raise an error, it is nothing special, just raise it like any normal # exception: # # raise MyError.new # # Eventually this exception will bubble out to the `vagrant` binary which # will show a nice error message. And if it is raised in the middle of a # middleware sequence, then {Action::Warden} will catch it and begin the # recovery process prior to exiting. module Errors # Main superclass of any errors in Vagrant. This provides some # convenience methods for setting the status code and error key. # The status code is used by the `vagrant` executable as the # error code, and the error key is used as a default message from # I18n. class VagrantError < StandardError def self.error_key(key=nil, namespace=nil) define_method(:error_key) { key } error_namespace(namespace) if namespace end def self.error_message(message) define_method(:error_message) { message } end def self.error_namespace(namespace) define_method(:error_namespace) { namespace } end def initialize(message=nil, *args) message = { :_key => message } if message && !message.is_a?(Hash) message = { :_key => error_key, :_namespace => error_namespace }.merge(message || {}) if message[:_key] message = translate_error(message) else message = error_message end super end # The error message for this error. This is used if no error_key # is specified for a translatable error message. def error_message; "No error message"; end # The default error namespace which is used for the error key. # This can be overridden here or by calling the "error_namespace" # class method. def error_namespace; "vagrant.errors"; end # The key for the error message. This should be set using the # {error_key} method but can be overridden here if needed. def error_key; nil; end # This is the exit code that should be used when exiting from # this exception. # # @return [Integer] def status_code; 1; end protected def translate_error(opts) return nil if !opts[:_key] I18n.t("#{opts[:_namespace]}.#{opts[:_key]}", opts) end end class ActiveMachineWithDifferentProvider < VagrantError error_key(:active_machine_with_different_provider) end class AnsibleFailed < VagrantError error_key(:ansible_failed) end class AnsiblePlaybookAppNotFound < VagrantError error_key(:ansible_playbook_app_not_found) end class BaseVMNotFound < VagrantError error_key(:base_vm_not_found) end class BatchMultiError < VagrantError error_key(:batch_multi_error) end class BoxAlreadyExists < VagrantError error_key(:already_exists, "vagrant.actions.box.unpackage") end class BoxChecksumInvalidType < VagrantError error_key(:box_checksum_invalid_type) end class BoxChecksumMismatch < VagrantError error_key(:box_checksum_mismatch) end class BoxConfigChangingBox < VagrantError error_key(:box_config_changing_box) end class BoxMetadataCorrupted < VagrantError error_key(:box_metadata_corrupted) end class BoxMetadataFileNotFound < VagrantError error_key(:box_metadata_file_not_found) end class BoxNotFound < VagrantError error_key(:box_not_found) end class BoxNotSpecified < VagrantError error_key(:not_specified, "vagrant.actions.vm.check_box") end class BoxProviderDoesntMatch < VagrantError error_key(:box_provider_doesnt_match) end class BoxSpecifiedDoesntExist < VagrantError error_key(:does_not_exist, "vagrant.actions.vm.check_box") end class BoxUnpackageFailure < VagrantError error_key(:untar_failure, "vagrant.actions.box.unpackage") end class BoxUpgradeRequired < VagrantError error_key(:box_upgrade_required) end class BoxVerificationFailed < VagrantError error_key(:failed, "vagrant.actions.box.verify") end class CFEngineBootstrapFailed < VagrantError error_key(:cfengine_bootstrap_failed) end class CFEngineCantAutodetectIP < VagrantError error_key(:cfengine_cant_autodetect_ip) end class CFEngineInstallFailed < VagrantError error_key(:cfengine_install_failed) end class CFEngineNotInstalled < VagrantError error_key(:cfengine_not_installed) end class CLIInvalidUsage < VagrantError error_key(:cli_invalid_usage) end class CLIInvalidOptions < VagrantError error_key(:cli_invalid_options) end class CommandUnavailable < VagrantError error_key(:command_unavailable) end class CommandUnavailableWindows < CommandUnavailable error_key(:command_unavailable_windows) end class ConfigInvalid < VagrantError error_key(:config_invalid) end class ConfigUpgradeErrors < VagrantError error_key(:config_upgrade_errors) end class CopyPrivateKeyFailed < VagrantError error_key(:copy_private_key_failed) end class DarwinNFSMountFailed < VagrantError error_key(:darwin_nfs_mount_failed) end class DestroyRequiresForce < VagrantError error_key(:destroy_requires_force) end class DotfileIsDirectory < VagrantError error_key(:dotfile_is_directory) end class DotfileUpgradeJSONError < VagrantError error_key(:dotfile_upgrade_json_error) end class DownloaderError < VagrantError error_key(:downloader_error) end class DownloaderInterrupted < DownloaderError error_key(:downloader_interrupted) end class DownloaderFileDoesntExist < VagrantError error_key(:file_missing, "vagrant.downloaders.file") end class DownloaderHTTPConnectReset < VagrantError error_key(:connection_reset, "vagrant.downloaders.http") end class DownloaderHTTPConnectTimeout < VagrantError error_key(:connection_timeout, "vagrant.downloaders.http") end class DownloaderHTTPSocketError < VagrantError error_key(:socket_error, "vagrant.downloaders.http") end class DownloaderHTTPStatusError < VagrantError error_key(:status_error, "vagrant.downloaders.http") end class EnvironmentNonExistentCWD < VagrantError error_key(:environment_non_existent_cwd) end class EnvironmentLockedError < VagrantError error_key(:environment_locked) end class GemCommandInBundler < VagrantError error_key(:gem_command_in_bundler) end class HomeDirectoryMigrationFailed < VagrantError error_key(:home_dir_migration_failed) end class HomeDirectoryNotAccessible < VagrantError error_key(:home_dir_not_accessible) end class ForwardPortAdapterNotFound < VagrantError error_key(:forward_port_adapter_not_found) end class ForwardPortAutolistEmpty < VagrantError error_key(:auto_empty, "vagrant.actions.vm.forward_ports") end class ForwardPortCollision < VagrantError error_key(:collision_error, "vagrant.actions.vm.forward_ports") end class ForwardPortCollisionResume < VagrantError error_key(:port_collision_resume) end class GuestCapabilityInvalid < VagrantError error_key(:guest_capability_invalid) end class GuestCapabilityNotFound < VagrantError error_key(:guest_capability_not_found) end class GuestExplicitNotDetected < VagrantError error_key(:guest_explicit_not_detected) end class GuestNotDetected < VagrantError error_key(:guest_not_detected) end class LinuxMountFailed < VagrantError error_key(:linux_mount_failed) end class LinuxNFSMountFailed < VagrantError error_key(:linux_nfs_mount_failed) end class LinuxShellExpandFailed < VagrantError error_key(:linux_shell_expand_failed) end class LocalDataDirectoryNotAccessible < VagrantError error_key(:local_data_dir_not_accessible) end class MachineGuestNotReady < VagrantError error_key(:machine_guest_not_ready) end class MachineNotFound < VagrantError error_key(:machine_not_found) end class MachineStateInvalid < VagrantError error_key(:machine_state_invalid) end class MultiVMEnvironmentRequired < VagrantError error_key(:multi_vm_required) end class MultiVMTargetRequired < VagrantError error_key(:multi_vm_target_required) end class NetworkAdapterCollision < VagrantError error_key(:adapter_collision, "vagrant.actions.vm.network") end class NetworkCollision < VagrantError error_key(:collides, "vagrant.actions.vm.host_only_network") end class NetworkNoAdapters < VagrantError error_key(:no_adapters, "vagrant.actions.vm.network") end class NetworkDHCPAlreadyAttached < VagrantError error_key(:dhcp_already_attached, "vagrant.actions.vm.network") end class NetworkNotFound < VagrantError error_key(:not_found, "vagrant.actions.vm.host_only_network") end class NFSBadExports < VagrantError error_key(:nfs_bad_exports) end class NFSCantReadExports < VagrantError error_key(:nfs_cant_read_exports) end class NFSNoGuestIP < VagrantError error_key(:nfs_no_guest_ip) end class NFSNoHostIP < VagrantError error_key(:nfs_no_host_ip) end class NFSNoHostonlyNetwork < VagrantError error_key(:nfs_no_hostonly_network) end class NFSNoValidIds < VagrantError error_key(:nfs_no_valid_ids) end class NoDefaultSyncedFolderImpl < VagrantError error_key(:no_default_synced_folder_impl) end class NoEnvironmentError < VagrantError error_key(:no_env) end class PackageIncludeMissing < VagrantError error_key(:include_file_missing, "vagrant.actions.general.package") end class PackageOutputDirectory < VagrantError error_key(:output_is_directory, "vagrant.actions.general.package") end class PackageOutputExists < VagrantError error_key(:output_exists, "vagrant.actions.general.package") end class PackageRequiresDirectory < VagrantError error_key(:requires_directory, "vagrant.actions.general.package") end class PersistDotfileExists < VagrantError error_key(:dotfile_error, "vagrant.actions.vm.persist") end class ProviderNotFound < VagrantError error_key(:provider_not_found) end class ProvisionerFlagInvalid < VagrantError error_key(:provisioner_flag_invalid) end class PluginGemError < VagrantError error_key(:plugin_gem_error) end class PluginInstallBadEntryPoint < VagrantError error_key(:plugin_install_bad_entry_point) end class PluginInstallLicenseNotFound < VagrantError error_key(:plugin_install_license_not_found) end class PluginInstallNotFound < VagrantError error_key(:plugin_install_not_found) end class PluginLoadError < VagrantError error_key(:plugin_load_error) end class PluginLoadFailed < VagrantError error_key(:plugin_load_failed) end class PluginLoadFailedWithOutput < VagrantError error_key(:plugin_load_failed_with_output) end class PluginNotFound < VagrantError error_key(:plugin_not_found) end class PluginNotInstalled < VagrantError error_key(:plugin_not_installed) end class PluginStateFileParseError < VagrantError error_key(:plugin_state_file_not_parsable) end class SCPPermissionDenied < VagrantError error_key(:scp_permission_denied) end class SCPUnavailable < VagrantError error_key(:scp_unavailable) end class SharedFolderCreateFailed < VagrantError error_key(:shared_folder_create_failed) end class SSHAuthenticationFailed < VagrantError error_key(:ssh_authentication_failed) end class SSHConnectEACCES < VagrantError error_key(:ssh_connect_eacces) end class SSHConnectionRefused < VagrantError error_key(:ssh_connection_refused) end class SSHConnectionReset < VagrantError error_key(:ssh_connection_reset) end class SSHConnectionTimeout < VagrantError error_key(:ssh_connection_timeout) end class SSHDisconnected < VagrantError error_key(:ssh_disconnected) end class SSHHostDown < VagrantError error_key(:ssh_host_down) end class SSHIsPuttyLink < VagrantError error_key(:ssh_is_putty_link) end class SSHKeyBadOwner < VagrantError error_key(:ssh_key_bad_owner) end class SSHKeyBadPermissions < VagrantError error_key(:ssh_key_bad_permissions) end class SSHKeyTypeNotSupported < VagrantError error_key(:ssh_key_type_not_supported) end class SSHNoRoute < VagrantError error_key(:ssh_no_route) end class SSHNotReady < VagrantError error_key(:ssh_not_ready) end class SSHPortNotDetected < VagrantError error_key(:ssh_port_not_detected) end class SSHUnavailable < VagrantError error_key(:ssh_unavailable) end class SSHUnavailableWindows < VagrantError error_key(:ssh_unavailable_windows) end class SyncedFolderUnusable < VagrantError error_key(:synced_folder_unusable) end class UIExpectsTTY < VagrantError error_key(:ui_expects_tty) end class UnimplementedProviderAction < VagrantError error_key(:unimplemented_provider_action) end class VagrantInterrupt < VagrantError error_key(:interrupted) end class VagrantfileExistsError < VagrantError error_key(:vagrantfile_exists) end class VagrantfileLoadError < VagrantError error_key(:vagrantfile_load_error) end class VagrantfileSyntaxError < VagrantError error_key(:vagrantfile_syntax_error) end class VagrantfileWriteError < VagrantError error_key(:vagrantfile_write_error) end class VagrantVersionBad < VagrantError error_key(:vagrant_version_bad) end class VBoxManageError < VagrantError error_key(:vboxmanage_error) end class VBoxManageNotFoundError < VagrantError error_key(:vboxmanage_not_found_error) end class VirtualBoxBrokenVersion040214 < VagrantError error_key(:virtualbox_broken_version_040214) end class VirtualBoxGuestPropertyNotFound < VagrantError error_key(:virtualbox_guest_property_not_found) end class VirtualBoxInvalidVersion < VagrantError error_key(:virtualbox_invalid_version) end class VirtualBoxNoRoomForHighLevelNetwork < VagrantError error_key(:virtualbox_no_room_for_high_level_network) end class VirtualBoxNotDetected < VagrantError error_key(:virtualbox_not_detected) end class VirtualBoxKernelModuleNotLoaded < VagrantError error_key(:virtualbox_kernel_module_not_loaded) end class VirtualBoxInstallIncomplete < VagrantError error_key(:virtualbox_install_incomplete) end class VMBaseMacNotSpecified < VagrantError error_key(:no_base_mac, "vagrant.actions.vm.match_mac") end class VMBootBadState < VagrantError error_key(:boot_bad_state) end class VMBootTimeout < VagrantError error_key(:boot_timeout) end class VMCustomizationFailed < VagrantError error_key(:failure, "vagrant.actions.vm.customize") end class VMFailedToBoot < VagrantError error_key(:failed_to_boot, "vagrant.actions.vm.boot") end class VMFailedToRun < VagrantError error_key(:failed_to_run, "vagrant.actions.vm.boot") end class VMGuestError < VagrantError error_namespace("vagrant.errors.guest") end class VMImportFailure < VagrantError error_key(:failure, "vagrant.actions.vm.import") end class VMInaccessible < VagrantError error_key(:vm_inaccessible) end class VMNameExists < VagrantError error_key(:vm_name_exists) end class VMNoMatchError < VagrantError error_key(:vm_no_match) end class VMNotCreatedError < VagrantError error_key(:vm_creation_required) end class VMNotFoundError < VagrantError error_key(:vm_not_found) end class VMNotRunningError < VagrantError error_key(:vm_not_running) end class VMPowerOffToPackage < VagrantError error_key(:power_off, "vagrant.actions.vm.export") end end end vagrant-1.4.3/lib/vagrant/guest.rb000066400000000000000000000132001226132634600170460ustar00rootroot00000000000000require "log4r" module Vagrant # This class handles guest-OS specific interactions with a machine. # It is primarily responsible for detecting the proper guest OS # implementation and then delegating capabilities. # # Vagrant has many tasks which require specific guest OS knowledge. # These are implemented using a guest/capability system. Various plugins # register as "guests" which determine the underlying OS of the system. # Then, "guest capabilities" register themselves for a specific OS (one # or more), and these capabilities are called. # # Example capabilities might be "mount_virtualbox_shared_folder" or # "configure_networks". # # This system allows for maximum flexibility and pluginability for doing # guest OS specific operations. class Guest attr_reader :chain # The name of the guest OS. This is available after {#detect!} is # called. # # @return [Symbol] attr_reader :name def initialize(machine, guests, capabilities) @logger = Log4r::Logger.new("vagrant::guest") @capabilities = capabilities @chain = [] @guests = guests @machine = machine @name = nil end # This will detect the proper guest OS for the machine and set up # the class to actually execute capabilities. def detect! @logger.info("Detect guest for machine: #{@machine}") guest_name = @machine.config.vm.guest if guest_name @logger.info("Using explicit config.vm.guest value: #{guest_name}") else # No explicit guest was specified, so autodetect it. guest_name = autodetect_guest raise Errors::GuestNotDetected if !guest_name end if !@guests[guest_name] # This can happen if config.vm.guest was specified with a value # that doesn't exist. raise Errors::GuestExplicitNotDetected, value: guest_name.to_s end @name = guest_name @chain = guest_chain(@name) end # Tests whether the guest has the named capability. # # @return [Boolean] def capability?(cap_name) !capability_module(cap_name.to_sym).nil? end # Executes the capability with the given name, optionally passing # more arguments onwards to the capability. def capability(cap_name, *args) @logger.info("Execute capability: #{cap_name} (#{@chain[0][0]})") cap_mod = capability_module(cap_name.to_sym) if !cap_mod raise Errors::GuestCapabilityNotFound, :cap => cap_name.to_s, :guest => @chain[0][0].to_s end cap_method = nil begin cap_method = cap_mod.method(cap_name) rescue NameError raise Errors::GuestCapabilityInvalid, :cap => cap_name.to_s, :guest => @chain[0][0].to_s end cap_method.call(@machine, *args) end # This returns whether the guest is ready to work. If this returns # `false`, then {#detect!} should be called in order to detect the # guest OS. # # @return [Boolean] def ready? !@chain.empty? end protected # Returns the registered module for a capability with the given name. # # @param [Symbol] cap_name # @return [Module] def capability_module(cap_name) @logger.debug("Searching for cap: #{cap_name}") @chain.each do |guest_name, guest| @logger.debug("Checking in: #{guest_name}") caps = @capabilities[guest_name] if caps && caps.has_key?(cap_name) @logger.debug("Found cap: #{cap_name} in #{guest_name}") return caps[cap_name] end end nil end # This autodetects the guest to use and returns the name of the guest. # This returns nil if the guest type could not be autodetected. # # @return [Symbol] def autodetect_guest @logger.info("Autodetecting guest for machine: #{@machine}") # Get the mapping of guests with the most parents. We start searching # with the guests with the most parents first. parent_count = {} @guests.each do |name, parts| parent_count[name] = 0 parent = parts[1] while parent parent_count[name] += 1 parent = @guests[parent] parent = parent[1] if parent end end # Now swap around the mapping so that it is a mapping of # count to the actual list of guest names parent_count_to_guests = {} parent_count.each do |name, count| parent_count_to_guests[count] ||= [] parent_count_to_guests[count] << name end sorted_counts = parent_count_to_guests.keys.sort.reverse sorted_counts.each do |count| parent_count_to_guests[count].each do |name| @logger.debug("Trying: #{name}") guest_info = @guests[name] guest = guest_info[0].new if guest.detect?(@machine) @logger.info("Detected: #{name}!") return name end end end return nil end # This returns the complete chain for the given guest. # # @return [Array] def guest_chain(name) guest_info = @guests[name] guest = guest_info[0].new chain = [] chain << [name, guest] # Build the proper chain of parents if there are any. # This allows us to do "inheritance" of capabilities later if guest_info[1] parent_name = guest_info[1] parent_info = @guests[parent_name] while parent_info chain << [parent_name, parent_info[0].new] parent_name = parent_info[1] parent_info = @guests[parent_name] end end return chain end end end vagrant-1.4.3/lib/vagrant/hosts.rb000066400000000000000000000014131226132634600170620ustar00rootroot00000000000000require 'log4r' module Vagrant module Hosts # This method detects the correct host based on the `match?` methods # implemented in the registered hosts. # # @param [Hash] registry Hash mapping key to host class def self.detect(registry) logger = Log4r::Logger.new("vagrant::hosts") # Sort the hosts by their precedence host_klasses = registry.values.sort_by { |a| a.precedence }.reverse logger.debug("Host path search classes: #{host_klasses.inspect}") # Test for matches and return the host class that matches host_klasses.each do |klass| if klass.match? logger.info("Host class: #{klass}") return klass end end # No matches found... return nil end end end vagrant-1.4.3/lib/vagrant/machine.rb000066400000000000000000000260101226132634600173260ustar00rootroot00000000000000require "log4r" module Vagrant # This represents a machine that Vagrant manages. This provides a singular # API for querying the state and making state changes to the machine, which # is backed by any sort of provider (VirtualBox, VMWare, etc.). class Machine # The box that is backing this machine. # # @return [Box] attr_reader :box # Configuration for the machine. # # @return [Object] attr_reader :config # Directory where machine-specific data can be stored. # # @return [Pathname] attr_reader :data_dir # The environment that this machine is a part of. # # @return [Environment] attr_reader :env # ID of the machine. This ID comes from the provider and is not # guaranteed to be of any particular format except that it is # a string. # # @return [String] attr_reader :id # Name of the machine. This is assigned by the Vagrantfile. # # @return [String] attr_reader :name # The provider backing this machine. # # @return [Object] attr_reader :provider # The provider-specific configuration for this machine. # # @return [Object] attr_reader :provider_config # The name of the provider. # # @return [Symbol] attr_reader :provider_name # The options given to the provider when registering the plugin. # # @return [Hash] attr_reader :provider_options # The UI for outputting in the scope of this machine. # # @return [UI] attr_reader :ui # Initialize a new machine. # # @param [String] name Name of the virtual machine. # @param [Class] provider The provider backing this machine. This is # currently expected to be a V1 `provider` plugin. # @param [Object] provider_config The provider-specific configuration for # this machine. # @param [Hash] provider_options The provider-specific options from the # plugin definition. # @param [Object] config The configuration for this machine. # @param [Pathname] data_dir The directory where machine-specific data # can be stored. This directory is ensured to exist. # @param [Box] box The box that is backing this virtual machine. # @param [Environment] env The environment that this machine is a # part of. def initialize(name, provider_name, provider_cls, provider_config, provider_options, config, data_dir, box, env, base=false) @logger = Log4r::Logger.new("vagrant::machine") @logger.info("Initializing machine: #{name}") @logger.info(" - Provider: #{provider_cls}") @logger.info(" - Box: #{box}") @logger.info(" - Data dir: #{data_dir}") @box = box @config = config @data_dir = data_dir @env = env @guest = Guest.new( self, Vagrant.plugin("2").manager.guests, Vagrant.plugin("2").manager.guest_capabilities) @name = name @provider_config = provider_config @provider_name = provider_name @provider_options = provider_options @ui = @env.ui.scope(@name) # Read the ID, which is usually in local storage @id = nil # XXX: This is temporary. This will be removed very soon. if base @id = name else # Read the id file from the data directory if it exists as the # ID for the pre-existing physical representation of this machine. id_file = @data_dir.join("id") @id = id_file.read.chomp if id_file.file? end # Initializes the provider last so that it has access to all the # state we setup on this machine. @provider = provider_cls.new(self) end # This calls an action on the provider. The provider may or may not # actually implement the action. # # @param [Symbol] name Name of the action to run. # @param [Hash] extra_env This data will be passed into the action runner # as extra data set on the environment hash for the middleware # runner. def action(name, extra_env=nil) @logger.info("Calling action: #{name} on provider #{@provider}") # Get the callable from the provider. callable = @provider.action(name) # If this action doesn't exist on the provider, then an exception # must be raised. if callable.nil? raise Errors::UnimplementedProviderAction, :action => name, :provider => @provider.to_s end # Run the action with the action runner on the environment env = { :action_name => "machine_action_#{name}".to_sym, :machine => self, :machine_action => name, :ui => @ui }.merge(extra_env || {}) @env.action_runner.run(callable, env) end # Returns a communication object for executing commands on the remote # machine. Note that the _exact_ semantics of this are up to the # communication provider itself. Despite this, the semantics are expected # to be consistent across operating systems. For example, all linux-based # systems should have similar communication (usually a shell). All # Windows systems should have similar communication as well. Therefore, # prior to communicating with the machine, users of this method are # expected to check the guest OS to determine their behavior. # # This method will _always_ return some valid communication object. # The `ready?` API can be used on the object to check if communication # is actually ready. # # @return [Object] def communicate if !@communicator # For now, we always return SSH. In the future, we'll abstract # this and allow plugins to define new methods of communication. klass = Vagrant.plugin("2").manager.communicators[:ssh] @communicator = klass.new(self) end @communicator end # Returns a guest implementation for this machine. The guest implementation # knows how to do guest-OS specific tasks, such as configuring networks, # mounting folders, etc. # # @return [Guest] def guest raise Errors::MachineGuestNotReady if !communicate.ready? @guest.detect! if !@guest.ready? @guest end # This sets the unique ID associated with this machine. This will # persist this ID so that in the future Vagrant will be able to find # this machine again. The unique ID must be absolutely unique to the # virtual machine, and can be used by providers for finding the # actual machine associated with this instance. # # **WARNING:** Only providers should ever use this method. # # @param [String] value The ID. def id=(value) @logger.info("New machine ID: #{value.inspect}") # The file that will store the id if we have one. This allows the # ID to persist across Vagrant runs. id_file = @data_dir.join("id") if value # Write the "id" file with the id given. id_file.open("w+") do |f| f.write(value) end else # Delete the file, since the machine is now destroyed id_file.delete if id_file.file? # Delete the entire data directory contents since all state # associated with the VM is now gone. @data_dir.children.each do |child| begin child.rmtree rescue Errno::EACCES @logger.info("EACCESS deleting file: #{child}") end end end # Store the ID locally @id = value.to_s # Notify the provider that the ID changed in case it needs to do # any accounting from it. @provider.machine_id_changed end # This returns a clean inspect value so that printing the value via # a pretty print (`p`) results in a readable value. # # @return [String] def inspect "#<#{self.class}: #{@name} (#{@provider.class})>" end # This returns the SSH info for accessing this machine. This SSH info # is queried from the underlying provider. This method returns `nil` if # the machine is not ready for SSH communication. # # The structure of the resulting hash is guaranteed to contain the # following structure, although it may return other keys as well # not documented here: # # { # :host => "1.2.3.4", # :port => "22", # :username => "mitchellh", # :private_key_path => "/path/to/my/key" # } # # Note that Vagrant makes no guarantee that this info works or is # correct. This is simply the data that the provider gives us or that # is configured via a Vagrantfile. It is still possible after this # point when attempting to connect via SSH to get authentication # errors. # # @return [Hash] SSH information. def ssh_info # First, ask the provider for their information. If the provider # returns nil, then the machine is simply not ready for SSH, and # we return nil as well. info = @provider.ssh_info return nil if info.nil? # Delete out the nil entries. info.dup.each do |key, value| info.delete(key) if value.nil? end # We set the defaults info[:host] ||= @config.ssh.default.host info[:port] ||= @config.ssh.default.port info[:private_key_path] ||= @config.ssh.default.private_key_path info[:username] ||= @config.ssh.default.username # We set overrides if they are set. These take precedence over # provider-returned data. info[:host] = @config.ssh.host if @config.ssh.host info[:port] = @config.ssh.port if @config.ssh.port info[:username] = @config.ssh.username if @config.ssh.username # We also set some fields that are purely controlled by Varant info[:forward_agent] = @config.ssh.forward_agent info[:forward_x11] = @config.ssh.forward_x11 # Add in provided proxy command config info[:proxy_command] = @config.ssh.proxy_command if @config.ssh.proxy_command # Set the private key path. If a specific private key is given in # the Vagrantfile we set that. Otherwise, we use the default (insecure) # private key, but only if the provider didn't give us one. if !info[:private_key_path] if @config.ssh.private_key_path info[:private_key_path] = @config.ssh.private_key_path else info[:private_key_path] = @env.default_private_key_path end end # Setup the keys if !info[:private_key_path].is_a?(Array) info[:private_key_path] = [info[:private_key_path]] end # Expand the private key path relative to the root path info[:private_key_path].map! do |path| File.expand_path(path, @env.root_path) end # Return the final compiled SSH info data info end # Returns the state of this machine. The state is queried from the # backing provider, so it can be any arbitrary symbol. # # @return [Symbol] def state result = @provider.state raise Errors::MachineStateInvalid if !result.is_a?(MachineState) result end end end vagrant-1.4.3/lib/vagrant/machine_state.rb000066400000000000000000000026111226132634600205270ustar00rootroot00000000000000module Vagrant # This represents the state of a given machine. This is a very basic # class that simply stores a short and long description of the state # of a machine. # # The state also stores a state "id" which can be used as a unique # identifier for a state. This should be a symbol. This allows internal # code to compare state such as ":not_created" instead of using # string comparison. # # The short description should be a single word description of the # state of the machine such as "running" or "not created". # # The long description can span multiple lines describing what the # state actually means. class MachineState # Unique ID for this state. # # @return [Symbol] attr_reader :id # Short description for this state. # # @return [String] attr_reader :short_description # Long description for this state. # # @return [String] attr_reader :long_description # Creates a new instance to represent the state of a machine. # # @param [Symbol] id Unique identifier for this state. # @param [String] short Short (preferably one-word) description of # the state. # @param [String] long Long description (can span multiple lines) # of the state. def initialize(id, short, long) @id = id @short_description = short @long_description = long end end end vagrant-1.4.3/lib/vagrant/plugin.rb000066400000000000000000000001651226132634600172230ustar00rootroot00000000000000module Vagrant module Plugin autoload :V1, "vagrant/plugin/v1" autoload :V2, "vagrant/plugin/v2" end end vagrant-1.4.3/lib/vagrant/plugin/000077500000000000000000000000001226132634600166745ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/plugin/v1.rb000066400000000000000000000011321226132634600175440ustar00rootroot00000000000000require "log4r" require "vagrant/plugin/v1/errors" module Vagrant module Plugin module V1 autoload :Command, "vagrant/plugin/v1/command" autoload :Communicator, "vagrant/plugin/v1/communicator" autoload :Config, "vagrant/plugin/v1/config" autoload :Guest, "vagrant/plugin/v1/guest" autoload :Host, "vagrant/plugin/v1/host" autoload :Manager, "vagrant/plugin/v1/manager" autoload :Plugin, "vagrant/plugin/v1/plugin" autoload :Provider, "vagrant/plugin/v1/provider" autoload :Provisioner, "vagrant/plugin/v1/provisioner" end end end vagrant-1.4.3/lib/vagrant/plugin/v1/000077500000000000000000000000001226132634600172225ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/plugin/v1/command.rb000066400000000000000000000132551226132634600211730ustar00rootroot00000000000000require 'log4r' require "vagrant/util/safe_puts" module Vagrant module Plugin module V1 # This is the base class for a CLI command. class Command include Util::SafePuts def initialize(argv, env) @argv = argv @env = env @logger = Log4r::Logger.new("vagrant::command::#{self.class.to_s.downcase}") end # This is what is called on the class to actually execute it. Any # subclasses should implement this method and do any option parsing # and validation here. def execute end protected # Parses the options given an OptionParser instance. # # This is a convenience method that properly handles duping the # originally argv array so that it is not destroyed. # # This method will also automatically detect "-h" and "--help" # and print help. And if any invalid options are detected, the help # will be printed, as well. # # If this method returns `nil`, then you should assume that help # was printed and parsing failed. def parse_options(opts=nil) # Creating a shallow copy of the arguments so the OptionParser # doesn't destroy the originals. argv = @argv.dup # Default opts to a blank optionparser if none is given opts ||= OptionParser.new # Add the help option, which must be on every command. opts.on_tail("-h", "--help", "Print this help") do safe_puts(opts.help) return nil end opts.parse!(argv) return argv rescue OptionParser::InvalidOption raise Errors::CLIInvalidOptions, :help => opts.help.chomp end # Yields a VM for each target VM for the command. # # This is a convenience method for easily implementing methods that # take a target VM (in the case of multi-VM) or every VM if no # specific VM name is specified. # # @param [String] name The name of the VM. Nil if every VM. # @param [Boolean] single_target If true, then an exception will be # raised if more than one target is found. def with_target_vms(names=nil, options=nil) # Using VMs requires a Vagrant environment to be properly setup raise Errors::NoEnvironmentError if !@env.root_path # Setup the options hash options ||= {} # Require that names be an array names ||= [] names = [names] if !names.is_a?(Array) # First determine the proper array of VMs. vms = [] if names.length > 0 names.each do |name| if pattern = name[/^\/(.+?)\/$/, 1] # This is a regular expression name, so we convert to a regular # expression and allow that sort of matching. regex = Regexp.new(pattern) @env.vms.each do |name, vm| vms << vm if name =~ regex end raise Errors::VMNoMatchError if vms.empty? else # String name, just look for a specific VM vms << @env.vms[name.to_sym] raise Errors::VMNotFoundError, :name => name if !vms[0] end end else vms = @env.vms_ordered end # Make sure we're only working with one VM if single target if options[:single_target] && vms.length != 1 vm = @env.primary_vm raise Errors::MultiVMTargetRequired if !vm vms = [vm] end # If we asked for reversed ordering, then reverse it vms.reverse! if options[:reverse] # Go through each VM and yield it! vms.each do |old_vm| # We get a new VM from the environment here to avoid potentially # stale VMs (if there was a config reload on the environment # or something). vm = @env.vms[old_vm.name] yield vm end end # This method will split the argv given into three parts: the # flags to this command, the subcommand, and the flags to the # subcommand. For example: # # -v status -h -v # # The above would yield 3 parts: # # ["-v"] # "status" # ["-h", "-v"] # # These parts are useful because the first is a list of arguments # given to the current command, the second is a subcommand, and the # third are the commands given to the subcommand. # # @return [Array] The three parts. def split_main_and_subcommand(argv) # Initialize return variables main_args = nil sub_command = nil sub_args = [] # We split the arguments into two: One set containing any # flags before a word, and then the rest. The rest are what # get actually sent on to the subcommand. argv.each_index do |i| if !argv[i].start_with?("-") # We found the beginning of the sub command. Split the # args up. main_args = argv[0, i] sub_command = argv[i] sub_args = argv[i + 1, argv.length - i + 1] # Break so we don't find the next non flag and shift our # main args. break end end # Handle the case that argv was empty or didn't contain any subcommand main_args = argv.dup if main_args.nil? return [main_args, sub_command, sub_args] end end end end end vagrant-1.4.3/lib/vagrant/plugin/v1/communicator.rb000066400000000000000000000074431226132634600222570ustar00rootroot00000000000000module Vagrant module Plugin module V1 # Base class for a communicator in Vagrant. A communicator is # responsible for communicating with a machine in some way. There # are various stages of Vagrant that require things such as uploading # files to the machine, executing shell commands, etc. Implementors # of this class are expected to provide this functionality in some # way. # # Note that a communicator must provide **all** of the methods # in this base class. There is currently no way for one communicator # to provide say a more efficient way of uploading a file, but not # provide shell execution. This sort of thing will come in a future # version. class Communicator # This returns true/false depending on if the given machine # can be communicated with using this communicator. If this returns # `true`, then this class will be used as the primary communication # method for the machine. # # @return [Boolean] def self.match?(machine) false end # Initializes the communicator with the machine that we will be # communicating with. This base method does nothing (it doesn't # even store the machine in an instance variable for you), so you're # expected to override this and do something with the machine if # you care about it. # # @param [Machine] machine The machine this instance is expected to # communicate with. def initialize(machine) end # Checks if the target machine is ready for communication. If this # returns true, then all the other methods for communicating with # the machine are expected to be functional. # # @return [Boolean] def ready? false end # Download a file from the remote machine to the local machine. # # @param [String] from Path of the file on the remote machine. # @param [String] to Path of where to save the file locally. def download(from, to) end # Upload a file to the remote machine. # # @param [String] from Path of the file locally to upload. # @param [String] to Path of where to save the file on the remote # machine. def upload(from, to) end # Execute a command on the remote machine. The exact semantics # of this method are up to the implementor, but in general the # users of this class will expect this to be a shell. # # This method gives you no way to write data back to the remote # machine, so only execute commands that don't expect input. # # @param [String] command Command to execute. # @yield [type, data] Realtime output of the command being executed. # @yieldparam [String] type Type of the output. This can be # `:stdout`, `:stderr`, etc. The exact types are up to the # implementor. # @yieldparam [String] data Data for the given output. # @return [Integer] Exit code of the command. def execute(command, opts=nil) end # Executes a command on the remote machine with administrative # privileges. See {#execute} for documentation, as the API is the # same. # # @see #execute def sudo(command, opts=nil) end # Executes a command and returns true if the command succeeded, # and false otherwise. By default, this executes as a normal user, # and it is up to the communicator implementation if they expose an # option for running tests as an administrator. # # @see #execute def test(command, opts=nil) end end end end end vagrant-1.4.3/lib/vagrant/plugin/v1/config.rb000066400000000000000000000106011226132634600210120ustar00rootroot00000000000000module Vagrant module Plugin module V1 # This is the base class for a configuration key defined for # V1. Any configuration key plugins for V1 should inherit from this # class. class Config # This constant represents an unset value. This is useful so it is # possible to know the difference between a configuration value that # was never set, and a value that is nil (explicitly). Best practice # is to initialize all variables to this value, then the {#merge} # method below will "just work" in many cases. UNSET_VALUE = Object.new # This is called as a last-minute hook that allows the configuration # object to finalize itself before it will be put into use. This is # a useful place to do some defaults in the case the user didn't # configure something or so on. # # An example of where this sort of thing is used or has been used: # the "vm" configuration key uses this to make sure that at least # one sub-VM has been defined: the default VM. # # The configuration object is expected to mutate itself. def finalize! # Default implementation is to do nothing. end # Merge another configuration object into this one. This assumes that # the other object is the same class as this one. This should not # mutate this object, but instead should return a new, merged object. # # The default implementation will simply iterate over the instance # variables and merge them together, with this object overriding # any conflicting instance variables of the older object. Instance # variables starting with "__" (double underscores) will be ignored. # This lets you set some sort of instance-specific state on your # configuration keys without them being merged together later. # # @param [Object] other The other configuration object to merge from, # this must be the same type of object as this one. # @return [Object] The merged object. def merge(other) result = self.class.new # Set all of our instance variables on the new class [self, other].each do |obj| obj.instance_variables.each do |key| # Ignore keys that start with a double underscore. This allows # configuration classes to still hold around internal state # that isn't propagated. if !key.to_s.start_with?("@__") result.instance_variable_set(key, obj.instance_variable_get(key)) end end end result end # Allows setting options from a hash. By default this simply calls # the `#{key}=` method on the config class with the value, which is # the expected behavior most of the time. # # This is expected to mutate itself. # # @param [Hash] options A hash of options to set on this configuration # key. def set_options(options) options.each do |key, value| send("#{key}=", value) end end # Converts this configuration object to JSON. def to_json(*a) instance_variables_hash.to_json(*a) end # Returns the instance variables as a hash of key-value pairs. def instance_variables_hash instance_variables.inject({}) do |acc, iv| acc[iv.to_s[1..-1]] = instance_variable_get(iv) acc end end # This is called to upgrade this V1 config to V2. The parameter given # is the full V2 configuration object, so you can do anything to it # that you want. # # No return value is expected, modifications should be made directly # to the new V2 object. # # @param [V2::Root] new def upgrade(new) end # Called after the configuration is finalized and loaded to validate # this object. # # @param [Environment] env Vagrant::Environment object of the # environment that this configuration has been loaded into. This # gives you convenient access to things like the the root path # and so on. # @param [ErrorRecorder] errors def validate(env, errors) end end end end end vagrant-1.4.3/lib/vagrant/plugin/v1/errors.rb000066400000000000000000000006211226132634600210620ustar00rootroot00000000000000# This file contains all the errors that the V1 plugin interface # may throw. module Vagrant module Plugin module V1 # Exceptions that can be thrown within the plugin interface all # inherit from this parent exception. class Error < StandardError; end # This is thrown when a command name given is invalid. class InvalidCommandName < Error; end end end end vagrant-1.4.3/lib/vagrant/plugin/v1/guest.rb000066400000000000000000000067551226132634600207130ustar00rootroot00000000000000module Vagrant module Plugin module V1 # The base class for a guest. A guest represents an installed system # within a machine that Vagrant manages. There are some portions of # Vagrant which are OS-specific such as mountaing shared folders and # halting the machine, and this abstraction allows the implementation # for these to be seperate from the core of Vagrant. class Guest class BaseError < Errors::VagrantError error_namespace("vagrant.guest.base") end include Vagrant::Util # The VM which this system is tied to. attr_reader :vm # Initializes the system. Any subclasses MUST make sure this # method is called on the parent. Therefore, if a subclass overrides # `initialize`, then you must call `super`. def initialize(vm) @vm = vm end # This method is automatically called when the system is available (when # Vagrant can successfully SSH into the machine) to give the system a chance # to determine the distro and return a distro-specific system. # # If this method returns nil, then this instance is assumed to be # the most specific guest implementation. def distro_dispatch end # Halt the machine. This method should gracefully shut down the # operating system. This method will cause `vagrant halt` and associated # commands to _block_, meaning that if the machine doesn't halt # in a reasonable amount of time, this method should just return. # # If when this method returns, the machine's state isn't "powered_off," # Vagrant will proceed to forcefully shut the machine down. def halt raise BaseError, :_key => :unsupported_halt end # Mounts a shared folder. # # This method should create, mount, and properly set permissions # on the shared folder. This method should also properly # adhere to any configuration values such as `shared_folder_uid` # on `config.vm`. # # @param [String] name The name of the shared folder. # @param [String] guestpath The path on the machine which the user # wants the folder mounted. # @param [Hash] options Additional options for the shared folder # which can be honored. def mount_shared_folder(name, guestpath, options) raise BaseError, :_key => :unsupported_shared_folder end # Mounts a shared folder via NFS. This assumes that the exports # via the host are already done. def mount_nfs(ip, folders) raise BaseError, :_key => :unsupported_nfs end # Configures the given list of networks on the virtual machine. # # The networks parameter will be an array of hashes where the hashes # represent the configuration of a network interface. The structure # of the hash will be roughly the following: # # { # :type => :static, # :ip => "192.168.33.10", # :netmask => "255.255.255.0", # :interface => 1 # } # def configure_networks(networks) raise BaseError, :_key => :unsupported_configure_networks end # Called to change the hostname of the virtual machine. def change_host_name(name) raise BaseError, :_key => :unsupported_host_name end end end end end vagrant-1.4.3/lib/vagrant/plugin/v1/host.rb000066400000000000000000000041121226132634600205220ustar00rootroot00000000000000module Vagrant module Plugin module V1 # Base class for a host in Vagrant. A host class contains functionality # that is specific to a specific OS that is running Vagrant. This # abstraction is done becauase there is some host-specific logic that # Vagrant must do in some cases. class Host # This returns true/false depending on if the current running system # matches the host class. # # @return [Boolean] def self.match? nil end # The precedence of the host when checking for matches. This is to # allow certain host such as generic OS's ("Linux", "BSD", etc.) # to be specified last. # # The hosts with the higher numbers will be checked first. # # If you're implementing a basic host, you can probably ignore this. def self.precedence 5 end # Initializes a new host class. # # The only required parameter is a UI object so that the host # objects have some way to communicate with the outside world. # # @param [UI] ui UI for the hosts to output to. def initialize(ui) @ui = ui end # Returns true of false denoting whether or not this host supports # NFS shared folder setup. This method ideally should verify that # NFS is installed. # # @return [Boolean] def nfs? false end # Exports the given hash of folders via NFS. # # @param [String] id A unique ID that is guaranteed to be unique to # match these sets of folders. # @param [String] ip IP of the guest machine. # @param [Hash] folders Shared folders to sync. def nfs_export(id, ip, folders) end # Prunes any NFS exports made by Vagrant which aren't in the set # of valid ids given. # # @param [Array] valid_ids Valid IDs that should not be # pruned. def nfs_prune(valid_ids) end end end end end vagrant-1.4.3/lib/vagrant/plugin/v1/manager.rb000066400000000000000000000063601226132634600211660ustar00rootroot00000000000000require "log4r" module Vagrant module Plugin module V1 # This class maintains a list of all the registered plugins as well # as provides methods that allow querying all registered components of # those plugins as a single unit. class Manager attr_reader :registered def initialize @logger = Log4r::Logger.new("vagrant::plugin::v1::manager") @registered = [] end # This returns all the registered communicators. # # @return [Hash] def communicators result = {} @registered.each do |plugin| result.merge!(plugin.communicator.to_hash) end result end # This returns all the registered configuration classes. # # @return [Hash] def config result = {} @registered.each do |plugin| plugin.config.each do |key, klass| result[key] = klass end end result end # This returns all the registered configuration classes that were # marked as "upgrade safe." # # @return [Hash] def config_upgrade_safe result = {} @registered.each do |plugin| configs = plugin.data[:config_upgrade_safe] if configs configs.each do |key| result[key] = plugin.config.get(key) end end end result end # This returns all the registered guests. # # @return [Hash] def guests result = {} @registered.each do |plugin| result.merge!(plugin.guest.to_hash) end result end # This returns all registered host classes. # # @return [Hash] def hosts hosts = {} @registered.each do |plugin| hosts.merge!(plugin.host.to_hash) end hosts end # This returns all registered providers. # # @return [Hash] def providers providers = {} @registered.each do |plugin| providers.merge!(plugin.provider.to_hash) end providers end # This registers a plugin. This should _NEVER_ be called by the public # and should only be called from within Vagrant. Vagrant will # automatically register V1 plugins when a name is set on the # plugin. def register(plugin) if !@registered.include?(plugin) @logger.info("Registered plugin: #{plugin.name}") @registered << plugin end end # This clears out all the registered plugins. This is only used by # unit tests and should not be called directly. def reset! @registered.clear end # This unregisters a plugin so that its components will no longer # be used. Note that this should only be used for testing purposes. def unregister(plugin) if @registered.include?(plugin) @logger.info("Unregistered: #{plugin.name}") @registered.delete(plugin) end end end end end end vagrant-1.4.3/lib/vagrant/plugin/v1/plugin.rb000066400000000000000000000202021226132634600210410ustar00rootroot00000000000000require "set" require "log4r" module Vagrant module Plugin module V1 # This is the superclass for all V1 plugins. class Plugin # Special marker that can be used for action hooks that matches # all action sequences. ALL_ACTIONS = :__all_actions__ # The logger for this class. LOGGER = Log4r::Logger.new("vagrant::plugin::v1::plugin") # Set the root class up to be ourself, so that we can reference this # from within methods which are probably in subclasses. ROOT_CLASS = self # This returns the manager for all V1 plugins. # # @return [V1::Manager] def self.manager @manager ||= Manager.new end # Set the name of the plugin. The moment that this is called, the # plugin will be registered and available. Before this is called, a # plugin does not exist. The name must be unique among all installed # plugins. # # @param [String] name Name of the plugin. # @return [String] The name of the plugin. def self.name(name=UNSET_VALUE) # Get or set the value first, so we have a name for logging when # we register. result = get_or_set(:name, name) # The plugin should be registered if we're setting a real name on it Plugin.manager.register(self) if name != UNSET_VALUE # Return the result result end # Sets a human-friendly descrition of the plugin. # # @param [String] value Description of the plugin. # @return [String] Description of the plugin. def self.description(value=UNSET_VALUE) get_or_set(:description, value) end # Registers a callback to be called when a specific action sequence # is run. This allows plugin authors to hook into things like VM # bootup, VM provisioning, etc. # # @param [Symbol] name Name of the action. # @return [Array] List of the hooks for the given action. def self.action_hook(name, &block) # Get the list of hooks for the given hook name data[:action_hooks] ||= {} hooks = data[:action_hooks][name.to_sym] ||= [] # Return the list if we don't have a block return hooks if !block_given? # Otherwise add the block to the list of hooks for this action. hooks << block end # Defines additional command line commands available by key. The key # becomes the subcommand, so if you register a command "foo" then # "vagrant foo" becomes available. # # @param [String] name Subcommand key. def self.command(name=UNSET_VALUE, &block) data[:command] ||= Registry.new if name != UNSET_VALUE # Validate the name of the command if name.to_s !~ /^[-a-z0-9]+$/i raise InvalidCommandName, "Commands can only contain letters, numbers, and hyphens" end # Register a new command class only if a name was given. data[:command].register(name.to_sym, &block) end # Return the registry data[:command] end # Defines additional communicators to be available. Communicators # should be returned by a block passed to this method. This is done # to ensure that the class is lazy loaded, so if your class inherits # from or uses any Vagrant internals specific to Vagrant 1.0, then # the plugin can still be defined without breaking anything in future # versions of Vagrant. # # @param [String] name Communicator name. def self.communicator(name=UNSET_VALUE, &block) data[:communicator] ||= Registry.new # Register a new communicator class only if a name was given. data[:communicator].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:communicator] end # Defines additional configuration keys to be available in the # Vagrantfile. The configuration class should be returned by a # block passed to this method. This is done to ensure that the class # is lazy loaded, so if your class inherits from any classes that # are specific to Vagrant 1.0, then the plugin can still be defined # without breaking anything in future versions of Vagrant. # # @param [String] name Configuration key. # @param [Boolean] upgrade_safe If this is true, then this configuration # key is safe to load during an upgrade, meaning that it depends # on NO Vagrant internal classes. Do _not_ set this to true unless # you really know what you're doing, since you can cause Vagrant # to crash (although Vagrant will output a user-friendly error # message if this were to happen). def self.config(name=UNSET_VALUE, upgrade_safe=false, &block) data[:config] ||= Registry.new # Register a new config class only if a name was given. if name != UNSET_VALUE data[:config].register(name.to_sym, &block) # If we were told this is an upgrade safe configuration class # then we add it to the set. if upgrade_safe data[:config_upgrade_safe] ||= Set.new data[:config_upgrade_safe].add(name.to_sym) end end # Return the registry data[:config] end # Defines an additionally available guest implementation with # the given key. # # @param [String] name Name of the guest. def self.guest(name=UNSET_VALUE, &block) data[:guests] ||= Registry.new # Register a new guest class only if a name was given data[:guests].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:guests] end # Defines an additionally available host implementation with # the given key. # # @param [String] name Name of the host. def self.host(name=UNSET_VALUE, &block) data[:hosts] ||= Registry.new # Register a new host class only if a name was given data[:hosts].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:hosts] end # Registers additional providers to be available. # # @param [Symbol] name Name of the provider. def self.provider(name=UNSET_VALUE, &block) data[:providers] ||= Registry.new # Register a new provider class only if a name was given data[:providers].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:providers] end # Registers additional provisioners to be available. # # @param [String] name Name of the provisioner. def self.provisioner(name=UNSET_VALUE, &block) data[:provisioners] ||= Registry.new # Register a new provisioner class only if a name was given data[:provisioners].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:provisioners] end # Returns the internal data associated with this plugin. This # should NOT be called by the general public. # # @return [Hash] def self.data @data ||= {} end protected # Sentinel value denoting that a value has not been set. UNSET_VALUE = Object.new # Helper method that will set a value if a value is given, or otherwise # return the already set value. # # @param [Symbol] key Key for the data # @param [Object] value Value to store. # @return [Object] Stored value. def self.get_or_set(key, value=UNSET_VALUE) # If no value is to be set, then return the value we have already set return data[key] if value.eql?(UNSET_VALUE) # Otherwise set the value data[key] = value end end end end end vagrant-1.4.3/lib/vagrant/plugin/v1/provider.rb000066400000000000000000000045611226132634600214070ustar00rootroot00000000000000module Vagrant module Plugin module V1 # This is the base class for a provider for the V1 API. A provider # is responsible for creating compute resources to match the needs # of a Vagrant-configured system. class Provider # Initialize the provider to represent the given machine. # # @param [Vagrant::Machine] machine The machine that this provider # is responsible for. def initialize(machine) end # This should return an action callable for the given name. # # @param [Symbol] name Name of the action. # @return [Object] A callable action sequence object, whether it # is a proc, object, etc. def action(name) nil end # This method is called if the underying machine ID changes. Providers # can use this method to load in new data for the actual backing # machine or to realize that the machine is now gone (the ID can # become `nil`). No parameters are given, since the underlying machine # is simply the machine instance given to this object. And no # return value is necessary. def machine_id_changed end # This should return a hash of information that explains how to # SSH into the machine. If the machine is not at a point where # SSH is even possible, then `nil` should be returned. # # The general structure of this returned hash should be the # following: # # { # :host => "1.2.3.4", # :port => "22", # :username => "mitchellh", # :private_key_path => "/path/to/my/key" # } # # **Note:** Vagrant only supports private key based authentication, # mainly for the reason that there is no easy way to exec into an # `ssh` prompt with a password, whereas we can pass a private key # via commandline. # # @return [Hash] SSH information. For the structure of this hash # read the accompanying documentation for this method. def ssh_info nil end # This should return the state of the machine within this provider. # The state can be any symbol. # # @return [Symbol] def state nil end end end end end vagrant-1.4.3/lib/vagrant/plugin/v1/provisioner.rb000066400000000000000000000032641226132634600221330ustar00rootroot00000000000000module Vagrant module Plugin module V1 # This is the base class for a provisioner for the V1 API. A provisioner # is primarily responsible for installing software on a Vagrant guest. class Provisioner # The environment which provisioner is running in. This is the # action environment, not a Vagrant::Environment. attr_reader :env # The configuration for this provisioner. This will be an instance of # the `Config` class which is part of the provisioner. attr_reader :config def initialize(env, config) @env = env @config = config end # This method is expected to return a class that is used for # configuring the provisioner. This return value is expected to be # a subclass of {Config}. # # @return [Config] def self.config_class end # This is the method called to "prepare" the provisioner. This is called # before any actions are run by the action runner (see {Vagrant::Actions::Runner}). # This can be used to setup shared folders, forward ports, etc. Whatever is # necessary on a "meta" level. # # No return value is expected. def prepare end # This is the method called to provision the system. This method # is expected to do whatever necessary to provision the system (create files, # SSH, etc.) def provision! end # This is the method called to when the system is being destroyed # and allows the provisioners to engage in any cleanup tasks necessary. def cleanup end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2.rb000066400000000000000000000015201226132634600175460ustar00rootroot00000000000000require "log4r" # We don't autoload components because if we're loading anything in the # V2 namespace anyways, then we're going to need the Components class. require "vagrant/plugin/v2/components" require "vagrant/plugin/v2/errors" module Vagrant module Plugin module V2 autoload :Command, "vagrant/plugin/v2/command" autoload :Communicator, "vagrant/plugin/v2/communicator" autoload :Config, "vagrant/plugin/v2/config" autoload :Guest, "vagrant/plugin/v2/guest" autoload :Host, "vagrant/plugin/v2/host" autoload :Manager, "vagrant/plugin/v2/manager" autoload :Plugin, "vagrant/plugin/v2/plugin" autoload :Provider, "vagrant/plugin/v2/provider" autoload :Provisioner, "vagrant/plugin/v2/provisioner" autoload :SyncedFolder, "vagrant/plugin/v2/synced_folder" end end end vagrant-1.4.3/lib/vagrant/plugin/v2/000077500000000000000000000000001226132634600172235ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/plugin/v2/command.rb000066400000000000000000000224251226132634600211730ustar00rootroot00000000000000require 'log4r' require "vagrant/util/safe_puts" module Vagrant module Plugin module V2 # This is the base class for a CLI command. class Command include Util::SafePuts # This should return a brief (60 characters or less) synopsis of what # this command does. It will be used in the output of the help. # # @return [String] def self.synopsis "" end def initialize(argv, env) @argv = argv @env = env @logger = Log4r::Logger.new("vagrant::command::#{self.class.to_s.downcase}") end # This is what is called on the class to actually execute it. Any # subclasses should implement this method and do any option parsing # and validation here. def execute end protected # Parses the options given an OptionParser instance. # # This is a convenience method that properly handles duping the # originally argv array so that it is not destroyed. # # This method will also automatically detect "-h" and "--help" # and print help. And if any invalid options are detected, the help # will be printed, as well. # # If this method returns `nil`, then you should assume that help # was printed and parsing failed. def parse_options(opts=nil) # Creating a shallow copy of the arguments so the OptionParser # doesn't destroy the originals. argv = @argv.dup # Default opts to a blank optionparser if none is given opts ||= OptionParser.new # Add the help option, which must be on every command. opts.on_tail("-h", "--help", "Print this help") do safe_puts(opts.help) return nil end opts.parse!(argv) return argv rescue OptionParser::InvalidOption raise Errors::CLIInvalidOptions, :help => opts.help.chomp end # Yields a VM for each target VM for the command. # # This is a convenience method for easily implementing methods that # take a target VM (in the case of multi-VM) or every VM if no # specific VM name is specified. # # @param [String] name The name of the VM. Nil if every VM. # @param [Hash] options Additional tweakable settings. # @option options [Symbol] :provider The provider to back the # machines with. All machines will be backed with this # provider. If none is given, a sensible default is chosen. # @option options [Boolean] :reverse If true, the resulting order # of machines is reversed. # @option options [Boolean] :single_target If true, then an # exception will be raised if more than one target is found. def with_target_vms(names=nil, options=nil) @logger.debug("Getting target VMs for command. Arguments:") @logger.debug(" -- names: #{names.inspect}") @logger.debug(" -- options: #{options.inspect}") # Using VMs requires a Vagrant environment to be properly setup raise Errors::NoEnvironmentError if !@env.root_path # Setup the options hash options ||= {} # Require that names be an array names ||= [] names = [names] if !names.is_a?(Array) # Cache the active machines outside the loop active_machines = @env.active_machines # This is a helper that gets a single machine with the proper # provider. The "proper provider" in this case depends on what was # given: # # * If a provider was explicitly specified, then use that provider. # But if an active machine exists with a DIFFERENT provider, # then throw an error (for now), since we don't yet support # bringing up machines with different providers. # # * If no provider was specified, then use the active machine's # provider if it exists, otherwise use the default provider. # get_machine = lambda do |name| # Check for an active machine with the same name provider_to_use = options[:provider] provider_to_use = provider_to_use.to_sym if provider_to_use active_machines.each do |active_name, active_provider| if name == active_name # We found an active machine with the same name if provider_to_use && provider_to_use != active_provider # We found an active machine with a provider that doesn't # match the requested provider. Show an error. raise Errors::ActiveMachineWithDifferentProvider, :name => active_name.to_s, :active_provider => active_provider.to_s, :requested_provider => provider_to_use.to_s else # Use this provider and exit out of the loop. One of the # invariants [for now] is that there shouldn't be machines # with multiple providers. @logger.info("Active machine found with name #{active_name}. " + "Using provider: #{active_provider}") provider_to_use = active_provider break end end end # Use the default provider if nothing else provider_to_use ||= @env.default_provider # Get the right machine with the right provider @env.machine(name, provider_to_use) end # First determine the proper array of VMs. machines = [] if names.length > 0 names.each do |name| if pattern = name[/^\/(.+?)\/$/, 1] @logger.debug("Finding machines that match regex: #{pattern}") # This is a regular expression name, so we convert to a regular # expression and allow that sort of matching. regex = Regexp.new(pattern) @env.machine_names.each do |machine_name| if machine_name =~ regex machines << get_machine.call(machine_name) end end raise Errors::VMNoMatchError if machines.empty? else # String name, just look for a specific VM @logger.debug("Finding machine that match name: #{name}") machines << get_machine.call(name.to_sym) raise Errors::VMNotFoundError, :name => name if !machines[0] end end else # No name was given, so we return every VM in the order # configured. @logger.debug("Loading all machines...") machines = @env.machine_names.map do |machine_name| get_machine.call(machine_name) end end # Make sure we're only working with one VM if single target if options[:single_target] && machines.length != 1 @logger.debug("Using primary machine since single target") primary_name = @env.primary_machine_name raise Errors::MultiVMTargetRequired if !primary_name machines = [get_machine.call(primary_name)] end # If we asked for reversed ordering, then reverse it machines.reverse! if options[:reverse] # Go through each VM and yield it! machines.each do |machine| @logger.info("With machine: #{machine.name} (#{machine.provider.inspect})") yield machine end end # This method will split the argv given into three parts: the # flags to this command, the subcommand, and the flags to the # subcommand. For example: # # -v status -h -v # # The above would yield 3 parts: # # ["-v"] # "status" # ["-h", "-v"] # # These parts are useful because the first is a list of arguments # given to the current command, the second is a subcommand, and the # third are the commands given to the subcommand. # # @return [Array] The three parts. def split_main_and_subcommand(argv) # Initialize return variables main_args = nil sub_command = nil sub_args = [] # We split the arguments into two: One set containing any # flags before a word, and then the rest. The rest are what # get actually sent on to the subcommand. argv.each_index do |i| if !argv[i].start_with?("-") # We found the beginning of the sub command. Split the # args up. main_args = argv[0, i] sub_command = argv[i] sub_args = argv[i + 1, argv.length - i + 1] # Break so we don't find the next non flag and shift our # main args. break end end # Handle the case that argv was empty or didn't contain any subcommand main_args = argv.dup if main_args.nil? return [main_args, sub_command, sub_args] end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/communicator.rb000066400000000000000000000105761226132634600222610ustar00rootroot00000000000000require "timeout" module Vagrant module Plugin module V2 # Base class for a communicator in Vagrant. A communicator is # responsible for communicating with a machine in some way. There # are various stages of Vagrant that require things such as uploading # files to the machine, executing shell commands, etc. Implementors # of this class are expected to provide this functionality in some # way. # # Note that a communicator must provide **all** of the methods # in this base class. There is currently no way for one communicator # to provide say a more efficient way of uploading a file, but not # provide shell execution. This sort of thing will come in a future # version. class Communicator # This returns true/false depending on if the given machine # can be communicated with using this communicator. If this returns # `true`, then this class will be used as the primary communication # method for the machine. # # @return [Boolean] def self.match?(machine) false end # Initializes the communicator with the machine that we will be # communicating with. This base method does nothing (it doesn't # even store the machine in an instance variable for you), so you're # expected to override this and do something with the machine if # you care about it. # # @param [Machine] machine The machine this instance is expected to # communicate with. def initialize(machine) end # Checks if the target machine is ready for communication. If this # returns true, then all the other methods for communicating with # the machine are expected to be functional. # # @return [Boolean] def ready? false end # wait_for_ready waits until the communicator is ready, blocking # until then. It will wait up to the given duration or raise an # exception if something goes wrong. def wait_for_ready(duration) # By default, we implement a naive solution. begin Timeout.timeout(duration) do while true return true if ready? sleep 0.5 end end rescue Timeout::Error # We timed out, we failed. end return false end # Download a file from the remote machine to the local machine. # # @param [String] from Path of the file on the remote machine. # @param [String] to Path of where to save the file locally. def download(from, to) end # Upload a file to the remote machine. # # @param [String] from Path of the file locally to upload. # @param [String] to Path of where to save the file on the remote # machine. def upload(from, to) end # Execute a command on the remote machine. The exact semantics # of this method are up to the implementor, but in general the # users of this class will expect this to be a shell. # # This method gives you no way to write data back to the remote # machine, so only execute commands that don't expect input. # # @param [String] command Command to execute. # @yield [type, data] Realtime output of the command being executed. # @yieldparam [String] type Type of the output. This can be # `:stdout`, `:stderr`, etc. The exact types are up to the # implementor. # @yieldparam [String] data Data for the given output. # @return [Integer] Exit code of the command. def execute(command, opts=nil) end # Executes a command on the remote machine with administrative # privileges. See {#execute} for documentation, as the API is the # same. # # @see #execute def sudo(command, opts=nil) end # Executes a command and returns true if the command succeeded, # and false otherwise. By default, this executes as a normal user, # and it is up to the communicator implementation if they expose an # option for running tests as an administrator. # # @see #execute def test(command, opts=nil) end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/components.rb000066400000000000000000000033051226132634600217360ustar00rootroot00000000000000module Vagrant module Plugin module V2 # This is the container class for the components of a single plugin. # This allows us to separate the plugin class which defines the # components, and the actual container of those components. This # removes a bit of state overhead from the plugin class itself. class Components # This contains all the action hooks. # # @return [Hash] attr_reader :action_hooks # This contains all the configuration plugins by scope. # # @return [Hash] attr_reader :configs # This contains all the guests and their parents. # # @return [Registry>] attr_reader :guests # This contains all the registered guest capabilities. # # @return [Hash] attr_reader :guest_capabilities # This contains all the provider plugins by name, and returns # the provider class and options. # # @return [Hash] attr_reader :providers # This contains all the synced folder implementations by name. # # @return [Registry>] attr_reader :synced_folders def initialize # The action hooks hash defaults to [] @action_hooks = Hash.new { |h, k| h[k] = [] } @configs = Hash.new { |h, k| h[k] = Registry.new } @guests = Registry.new @guest_capabilities = Hash.new { |h, k| h[k] = Registry.new } @providers = Registry.new @synced_folders = Registry.new end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/config.rb000066400000000000000000000123141226132634600210160ustar00rootroot00000000000000require "set" module Vagrant module Plugin module V2 # This is the base class for a configuration key defined for # V2. Any configuration key plugins for V2 should inherit from this # class. class Config # This constant represents an unset value. This is useful so it is # possible to know the difference between a configuration value that # was never set, and a value that is nil (explicitly). Best practice # is to initialize all variables to this value, then the {#merge} # method below will "just work" in many cases. UNSET_VALUE = Object.new # This is called as a last-minute hook that allows the configuration # object to finalize itself before it will be put into use. This is # a useful place to do some defaults in the case the user didn't # configure something or so on. # # An example of where this sort of thing is used or has been used: # the "vm" configuration key uses this to make sure that at least # one sub-VM has been defined: the default VM. # # The configuration object is expected to mutate itself. def finalize! # Default implementation is to do nothing. end # Merge another configuration object into this one. This assumes that # the other object is the same class as this one. This should not # mutate this object, but instead should return a new, merged object. # # The default implementation will simply iterate over the instance # variables and merge them together, with this object overriding # any conflicting instance variables of the older object. Instance # variables starting with "__" (double underscores) will be ignored. # This lets you set some sort of instance-specific state on your # configuration keys without them being merged together later. # # @param [Object] other The other configuration object to merge from, # this must be the same type of object as this one. # @return [Object] The merged object. def merge(other) result = self.class.new # Set all of our instance variables on the new class [self, other].each do |obj| obj.instance_variables.each do |key| # Ignore keys that start with a double underscore. This allows # configuration classes to still hold around internal state # that isn't propagated. if !key.to_s.start_with?("@__") # Don't set the value if it is the unset value, either. value = obj.instance_variable_get(key) result.instance_variable_set(key, value) if value != UNSET_VALUE end end end # Persist through the set of invalid methods this_invalid = @__invalid_methods || Set.new other_invalid = other.instance_variable_get(:"@__invalid_methods") || Set.new result.instance_variable_set(:"@__invalid_methods", this_invalid + other_invalid) result end # Capture all bad configuration calls and save them for an error # message later during validation. def method_missing(name, *args, &block) name = name.to_s name = name[0...-1] if name.end_with?("=") @__invalid_methods ||= Set.new @__invalid_methods.add(name) # Return the dummy object so that anything else works ::Vagrant::Config::V2::DummyConfig.new end # Allows setting options from a hash. By default this simply calls # the `#{key}=` method on the config class with the value, which is # the expected behavior most of the time. # # This is expected to mutate itself. # # @param [Hash] options A hash of options to set on this configuration # key. def set_options(options) options.each do |key, value| send("#{key}=", value) end end # Converts this configuration object to JSON. def to_json(*a) instance_variables_hash.to_json(*a) end # A default to_s implementation. def to_s self.class.to_s end # Returns the instance variables as a hash of key-value pairs. def instance_variables_hash instance_variables.inject({}) do |acc, iv| acc[iv.to_s[1..-1]] = instance_variable_get(iv) acc end end # Called after the configuration is finalized and loaded to validate # this object. # # @param [Machine] machine Access to the machine that is being # validated. # @return [Hash] def validate(machine) return { self.to_s => _detected_errors } end # This returns any automatically detected errors. # # @return [Array] def _detected_errors return [] if !@__invalid_methods || @__invalid_methods.empty? return [I18n.t("vagrant.config.common.bad_field", :fields => @__invalid_methods.to_a.sort.join(", "))] end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/errors.rb000066400000000000000000000006211226132634600210630ustar00rootroot00000000000000# This file contains all the errors that the V2 plugin interface # may throw. module Vagrant module Plugin module V2 # Exceptions that can be thrown within the plugin interface all # inherit from this parent exception. class Error < StandardError; end # This is thrown when a command name given is invalid. class InvalidCommandName < Error; end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/guest.rb000066400000000000000000000014161226132634600207010ustar00rootroot00000000000000module Vagrant module Plugin module V2 # A base class for a guest OS. A guest OS is responsible for detecting # that the guest operating system running within the machine. The guest # can then be extended with various "guest capabilities" which are their # own form of plugin. # # The guest class itself is only responsible for detecting itself, # and may provide helpers for the capabilties. class Guest # This method is called when the machine is booted and has communication # capabilities in order to detect whether this guest operating system # is running within the machine. # # @return [Boolean] def detect?(machine) false end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/host.rb000066400000000000000000000041121226132634600205230ustar00rootroot00000000000000module Vagrant module Plugin module V2 # Base class for a host in Vagrant. A host class contains functionality # that is specific to a specific OS that is running Vagrant. This # abstraction is done becauase there is some host-specific logic that # Vagrant must do in some cases. class Host # This returns true/false depending on if the current running system # matches the host class. # # @return [Boolean] def self.match? nil end # The precedence of the host when checking for matches. This is to # allow certain host such as generic OS's ("Linux", "BSD", etc.) # to be specified last. # # The hosts with the higher numbers will be checked first. # # If you're implementing a basic host, you can probably ignore this. def self.precedence 5 end # Initializes a new host class. # # The only required parameter is a UI object so that the host # objects have some way to communicate with the outside world. # # @param [UI] ui UI for the hosts to output to. def initialize(ui) @ui = ui end # Returns true of false denoting whether or not this host supports # NFS shared folder setup. This method ideally should verify that # NFS is installed. # # @return [Boolean] def nfs? false end # Exports the given hash of folders via NFS. # # @param [String] id A unique ID that is guaranteed to be unique to # match these sets of folders. # @param [String] ip IP of the guest machine. # @param [Hash] folders Shared folders to sync. def nfs_export(id, ip, folders) end # Prunes any NFS exports made by Vagrant which aren't in the set # of valid ids given. # # @param [Array] valid_ids Valid IDs that should not be # pruned. def nfs_prune(valid_ids) end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/manager.rb000066400000000000000000000127351226132634600211720ustar00rootroot00000000000000require "log4r" module Vagrant module Plugin module V2 # This class maintains a list of all the registered plugins as well # as provides methods that allow querying all registered components of # those plugins as a single unit. class Manager attr_reader :registered attr_reader :required def initialize @logger = Log4r::Logger.new("vagrant::plugin::v2::manager") @registered = [] @required = [] end # This returns all the action hooks. # # @return [Array] def action_hooks(hook_name) result = [] @registered.each do |plugin| result += plugin.components.action_hooks[Plugin::ALL_ACTIONS] result += plugin.components.action_hooks[hook_name] end result end # This returns all the registered commands. # # @return [Hash] def commands Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.command) end end end # This returns all the registered communicators. # # @return [Hash] def communicators Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.communicator) end end end # This returns all the registered configuration classes. # # @return [Hash] def config Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.configs[:top]) end end end # This returns all the registered guests. # # @return [Hash] def guests Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.guests) end end end # This returns all the registered guest capabilities. # # @return [Hash] def guest_capabilities results = Hash.new { |h, k| h[k] = Registry.new } @registered.each do |plugin| plugin.components.guest_capabilities.each do |guest, caps| results[guest].merge!(caps) end end results end # This returns all registered host classes. # # @return [Hash] def hosts Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.host) end end end # This returns all registered providers. # # @return [Hash] def providers Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.providers) end end end # This returns all the config classes for the various providers. # # @return [Hash] def provider_configs Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.configs[:provider]) end end end # This returns all the config classes for the various provisioners. # # @return [Registry] def provisioner_configs Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.configs[:provisioner]) end end end # This returns all registered provisioners. # # @return [Hash] def provisioners Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.provisioner) end end end # This returns all synced folder implementations. # # @return [Registry] def synced_folders Registry.new.tap do |result| @registered.each do |plugin| result.merge!(plugin.components.synced_folders) end end end # This registers a plugin. This should _NEVER_ be called by the public # and should only be called from within Vagrant. Vagrant will # automatically register V2 plugins when a name is set on the # plugin. def register(plugin) if !@registered.include?(plugin) @logger.info("Registered plugin: #{plugin.name}") @registered << plugin end end # This registers a required plugin. This should _NEVER_ be called by # the public and should only be called from within Vagrant. def plugin_required(gem_name) if !@required.include?(gem_name) @logger.info("Registered required plugin: #{gem_name}") @required << gem_name end end # This clears out all the registered plugins. This is only used by # unit tests and should not be called directly. def reset! @registered.clear @required.clear end # This unregisters a plugin so that its components will no longer # be used. Note that this should only be used for testing purposes. def unregister(plugin) if @registered.include?(plugin) @logger.info("Unregistered: #{plugin.name}") @registered.delete(plugin) end end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/plugin.rb000066400000000000000000000205361226132634600210540ustar00rootroot00000000000000require "set" require "log4r" require "vagrant/plugin/v2/components" module Vagrant module Plugin module V2 # This is the superclass for all V2 plugins. class Plugin # Special marker that can be used for action hooks that matches # all action sequences. ALL_ACTIONS = :__all_actions__ # The logger for this class. LOGGER = Log4r::Logger.new("vagrant::plugin::v2::plugin") # Set the root class up to be ourself, so that we can reference this # from within methods which are probably in subclasses. ROOT_CLASS = self # This returns the manager for all V2 plugins. # # @return [V2::Manager] def self.manager @manager ||= Manager.new end # Returns the {Components} for this plugin. # # @return [Components] def self.components @components ||= Components.new end # Set the name of the plugin. The moment that this is called, the # plugin will be registered and available. Before this is called, a # plugin does not exist. The name must be unique among all installed # plugins. # # @param [String] name Name of the plugin. # @return [String] The name of the plugin. def self.name(name=UNSET_VALUE) # Get or set the value first, so we have a name for logging when # we register. result = get_or_set(:name, name) # The plugin should be registered if we're setting a real name on it Plugin.manager.register(self) if name != UNSET_VALUE # Return the result result end # Sets a human-friendly descrition of the plugin. # # @param [String] value Description of the plugin. # @return [String] Description of the plugin. def self.description(value=UNSET_VALUE) get_or_set(:description, value) end # Registers a callback to be called when a specific action sequence # is run. This allows plugin authors to hook into things like VM # bootup, VM provisioning, etc. # # @param [String] name Name of the action. # @param [Symbol] hook_name The location to hook. If this isn't # set, every middleware action is hooked. # @return [Array] List of the hooks for the given action. def self.action_hook(name, hook_name=nil, &block) # The name is currently not used but we want it for the future. hook_name ||= ALL_ACTIONS components.action_hooks[hook_name.to_sym] << block end # Defines additional command line commands available by key. The key # becomes the subcommand, so if you register a command "foo" then # "vagrant foo" becomes available. # # @param [String] name Subcommand key. def self.command(name=UNSET_VALUE, &block) data[:command] ||= Registry.new if name != UNSET_VALUE # Validate the name of the command if name.to_s !~ /^[-a-z0-9]+$/i raise InvalidCommandName, "Commands can only contain letters, numbers, and hyphens" end # Register a new command class only if a name was given. data[:command].register(name.to_sym, &block) end # Return the registry data[:command] end # Defines additional communicators to be available. Communicators # should be returned by a block passed to this method. This is done # to ensure that the class is lazy loaded, so if your class inherits # from or uses any Vagrant internals specific to Vagrant 1.0, then # the plugin can still be defined without breaking anything in future # versions of Vagrant. # # @param [String] name Communicator name. def self.communicator(name=UNSET_VALUE, &block) data[:communicator] ||= Registry.new # Register a new communicator class only if a name was given. data[:communicator].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:communicator] end # Defines additional configuration keys to be available in the # Vagrantfile. The configuration class should be returned by a # block passed to this method. This is done to ensure that the class # is lazy loaded, so if your class inherits from any classes that # are specific to Vagrant 1.0, then the plugin can still be defined # without breaking anything in future versions of Vagrant. # # @param [String] name Configuration key. def self.config(name, scope=nil, &block) scope ||= :top components.configs[scope].register(name.to_sym, &block) nil end # Defines an additionally available guest implementation with # the given key. # # @param [String] name Name of the guest. # @param [String] parent Name of the parent guest (if any) def self.guest(name=UNSET_VALUE, parent=nil, &block) components.guests.register(name.to_sym) do parent = parent.to_sym if parent [block.call, parent] end nil end # Defines a capability for the given guest. The block should return # a class/module that has a method with the capability name, ready # to be executed. This means that if it is an instance method, # the block should return an instance of the class. # # @param [String] guest The name of the guest # @param [String] cap The name of the capability def self.guest_capability(guest, cap, &block) components.guest_capabilities[guest.to_sym].register(cap.to_sym, &block) nil end # Defines an additionally available host implementation with # the given key. # # @param [String] name Name of the host. def self.host(name=UNSET_VALUE, &block) data[:hosts] ||= Registry.new # Register a new host class only if a name was given data[:hosts].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:hosts] end # Registers additional providers to be available. # # @param [Symbol] name Name of the provider. def self.provider(name=UNSET_VALUE, options=nil, &block) components.providers.register(name.to_sym) do [block.call, options || {}] end nil end # Registers additional provisioners to be available. # # @param [String] name Name of the provisioner. def self.provisioner(name=UNSET_VALUE, &block) data[:provisioners] ||= Registry.new # Register a new provisioner class only if a name was given data[:provisioners].register(name.to_sym, &block) if name != UNSET_VALUE # Return the registry data[:provisioners] end # Registers additional synced folder implementations. # # @param [String] name Name of the implementation. # @param [Integer] priority The priority of the implementation, # higher (big) numbers are tried before lower (small) numbers. def self.synced_folder(name, priority=10, &block) components.synced_folders.register(name.to_sym) do [block.call, priority] end nil end # Returns the internal data associated with this plugin. This # should NOT be called by the general public. # # @return [Hash] def self.data @data ||= {} end protected # Sentinel value denoting that a value has not been set. UNSET_VALUE = Object.new # Helper method that will set a value if a value is given, or otherwise # return the already set value. # # @param [Symbol] key Key for the data # @param [Object] value Value to store. # @return [Object] Stored value. def self.get_or_set(key, value=UNSET_VALUE) # If no value is to be set, then return the value we have already set return data[key] if value.eql?(UNSET_VALUE) # Otherwise set the value data[key] = value end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/provider.rb000066400000000000000000000047271226132634600214140ustar00rootroot00000000000000module Vagrant module Plugin module V2 # This is the base class for a provider for the V2 API. A provider # is responsible for creating compute resources to match the needs # of a Vagrant-configured system. class Provider # Initialize the provider to represent the given machine. # # @param [Vagrant::Machine] machine The machine that this provider # is responsible for. def initialize(machine) end # This should return an action callable for the given name. # # @param [Symbol] name Name of the action. # @return [Object] A callable action sequence object, whether it # is a proc, object, etc. def action(name) nil end # This method is called if the underying machine ID changes. Providers # can use this method to load in new data for the actual backing # machine or to realize that the machine is now gone (the ID can # become `nil`). No parameters are given, since the underlying machine # is simply the machine instance given to this object. And no # return value is necessary. def machine_id_changed end # This should return a hash of information that explains how to # SSH into the machine. If the machine is not at a point where # SSH is even possible, then `nil` should be returned. # # The general structure of this returned hash should be the # following: # # { # :host => "1.2.3.4", # :port => "22", # :username => "mitchellh", # :private_key_path => "/path/to/my/key" # } # # **Note:** Vagrant only supports private key based authentication, # mainly for the reason that there is no easy way to exec into an # `ssh` prompt with a password, whereas we can pass a private key # via commandline. # # @return [Hash] SSH information. For the structure of this hash # read the accompanying documentation for this method. def ssh_info nil end # This should return the state of the machine within this provider. # The state must be an instance of {MachineState}. Please read the # documentation of that class for more information. # # @return [MachineState] def state nil end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/provisioner.rb000066400000000000000000000036661226132634600221420ustar00rootroot00000000000000module Vagrant module Plugin module V2 # This is the base class for a provisioner for the V2 API. A provisioner # is primarily responsible for installing software on a Vagrant guest. class Provisioner attr_reader :machine attr_reader :config # Initializes the provisioner with the machine that it will be # provisioning along with the provisioner configuration (if there # is any). # # The provisioner should _not_ do anything at this point except # initialize internal state. # # @param [Machine] machine The machine that this will be provisioning. # @param [Object] config Provisioner configuration, if one was set. def initialize(machine, config) @machine = machine @config = config end # Called with the root configuration of the machine so the provisioner # can add some configuration on top of the machine. # # During this step, and this step only, the provisioner should modify # the root machine configuration to add any additional features it # may need. Examples include sharing folders, networking, and so on. # This step is guaranteed to be called before any of those steps are # done so the provisioner may do that. # # No return value is expected. def configure(root_config) end # This is the method called when the actual provisioning should be # done. The communicator is guaranteed to be ready at this point, # and any shared folders or networks are already setup. # # No return value is expected. def provision end # This is the method called when destroying a machine that allows # for any state related to the machine created by the provisioner # to be cleaned up. def cleanup end end end end end vagrant-1.4.3/lib/vagrant/plugin/v2/synced_folder.rb000066400000000000000000000022411226132634600223670ustar00rootroot00000000000000module Vagrant module Plugin module V2 # This is the base class for a synced folder implementation. class SyncedFolder # This is called early when the synced folder is set to determine # if this implementation can be used for this machine. This should # return true or false. # # @return [Boolean] def usable?(machine) end # This is called before the machine is booted, allowing the # implementation to make any machine modifications or perhaps # verifications. # # No return value. def prepare(machine, folders, opts) end # This is called after the machine is booted and after networks # are setup. # # No return value. def enable(machine, folders, opts) end # This is called after destroying the machine during a # `vagrant destroy` and also prior to syncing folders during # a `vagrant up`. # # No return value. # # @param [Machine] machine # @param [Hash] opts def cleanup(machine, opts) end end end end end vagrant-1.4.3/lib/vagrant/registry.rb000066400000000000000000000036061226132634600176000ustar00rootroot00000000000000module Vagrant # Register components in a single location that can be queried. # # This allows certain components (such as guest systems, configuration # pieces, etc.) to be registered and queried, lazily. class Registry def initialize @items = {} @results_cache = {} end # Register a key with a lazy-loaded value. # # If a key with the given name already exists, it is overwritten. def register(key, &block) raise ArgumentError, "block required" if !block_given? @items[key] = block end # Get a value by the given key. # # This will evaluate the block given to `register` and return the # resulting value. def get(key) return nil if !@items.has_key?(key) return @results_cache[key] if @results_cache.has_key?(key) @results_cache[key] = @items[key].call end alias :[] :get # Checks if the given key is registered with the registry. # # @return [Boolean] def has_key?(key) @items.has_key?(key) end # Iterate over the keyspace. def each(&block) @items.each do |key, _| yield key, get(key) end end # Merge one registry with another and return a completely new # registry. Note that the result cache is completely busted, so # any gets on the new registry will result in a cache miss. def merge(other) self.class.new.tap do |result| result.merge!(self) result.merge!(other) end end # Like #{merge} but merges into self. def merge!(other) @items.merge!(other.__internal_state[:items]) self end # Converts this registry to a hash def to_hash result = {} self.each do |key, value| result[key] = value end result end def __internal_state { :items => @items, :results_cache => @results_cache } end end end vagrant-1.4.3/lib/vagrant/ui.rb000066400000000000000000000174301226132634600163450ustar00rootroot00000000000000require "thread" require "log4r" require "vagrant/util/platform" require "vagrant/util/safe_puts" module Vagrant module UI # Vagrant UIs handle communication with the outside world (typically # through a shell). They must respond to the following methods: # # * `info` # * `warn` # * `error` # * `success` class Interface def initialize @logger = Log4r::Logger.new("vagrant::ui::interface") end [:ask, :warn, :error, :info, :success].each do |method| define_method(method) do |message, *opts| # Log normal console messages @logger.info { "#{method}: #{message}" } end end [:clear_line, :report_progress].each do |method| # By default do nothing, these aren't logged define_method(method) { |*args| } end # For machine-readable output. # # @param [String] type The type of the data # @param [Array] data The data associated with the type def machine(type, *data) @logger.info("Machine: #{type} #{data.inspect}") end # Returns a new UI class that is scoped to the given resource name. # Subclasses can then use this scope name to do whatever they please. # # @param [String] scope_name # @return [Interface] def scope(scope_name) self end end # This is a UI implementation that does nothing. class Silent < Interface def ask(*args) super # Silent can't do this, obviously. raise Errors::UIExpectsTTY end end class MachineReadable < Interface include Util::SafePuts def initialize super @lock = Mutex.new end def ask(*args) super # Machine-readable can't ask for input raise Errors::UIExpectsTTY end def machine(type, *data) opts = {} opts = data.pop if data.last.kind_of?(Hash) target = opts[:scope] || "" # Prepare the data by replacing characters that aren't outputted data.each_index do |i| data[i] = data[i].to_s data[i].gsub!(",", "%!(VAGRANT_COMMA)") data[i].gsub!("\n", "\\n") data[i].gsub!("\r", "\\r") end @lock.synchronize do safe_puts("#{Time.now.utc.to_i},#{target},#{type},#{data.join(",")}") end end def scope(scope_name) BasicScope.new(self, scope_name) end end # This is a UI implementation that outputs the text as is. It # doesn't add any color. class Basic < Interface include Util::SafePuts def initialize super @lock = Mutex.new end # Use some light meta-programming to create the various methods to # output text to the UI. These all delegate the real functionality # to `say`. [:info, :warn, :error, :success].each do |method| class_eval <<-CODE def #{method}(message, *args) super(message) say(#{method.inspect}, message, *args) end CODE end def ask(message, opts=nil) super(message) # We can't ask questions when the output isn't a TTY. raise Errors::UIExpectsTTY if !$stdin.tty? && !Vagrant::Util::Platform.cygwin? # Setup the options so that the new line is suppressed opts ||= {} opts[:new_line] = false if !opts.has_key?(:new_line) opts[:prefix] = false if !opts.has_key?(:prefix) # Output the data say(:info, message, opts) # Get the results and chomp off the newline. We do a logical OR # here because `gets` can return a nil, for example in the case # that ctrl-D is pressed on the input. input = $stdin.gets || "" input.chomp end # This is used to output progress reports to the UI. # Send this method progress/total and it will output it # to the UI. Send `clear_line` to clear the line to show # a continuous progress meter. def report_progress(progress, total, show_parts=true) if total && total > 0 percent = (progress.to_f / total.to_f) * 100 line = "Progress: #{percent.to_i}%" line << " (#{progress} / #{total})" if show_parts else line = "Progress: #{progress}" end info(line, :new_line => false) end def clear_line reset = "\r" info(reset, :new_line => false) end # This method handles actually outputting a message of a given type # to the console. def say(type, message, opts=nil) defaults = { :new_line => true, :prefix => true } opts = defaults.merge(opts || {}) # Determine whether we're expecting to output our # own new line or not. printer = opts[:new_line] ? :puts : :print # Determine the proper IO channel to send this message # to based on the type of the message channel = type == :error || opts[:channel] == :error ? $stderr : $stdout # Output! We wrap this in a lock so that it safely outputs only # one line at a time. We wrap this in a thread because as of Ruby 2.0 # we can't acquire locks in a trap context (ctrl-c), so we have to # do this. Thread.new do @lock.synchronize do safe_puts(format_message(type, message, opts), :io => channel, :printer => printer) end end.join end def scope(scope_name) BasicScope.new(self, scope_name) end # This is called by `say` to format the message for output. def format_message(type, message, opts=nil) opts ||= {} message = "[#{opts[:scope]}] #{message}" if opts[:scope] && opts[:prefix] message end end # This implements a scope for the {Basic} UI. class BasicScope < Interface def initialize(ui, scope) super() @ui = ui @scope = scope end [:ask, :warn, :error, :info, :success].each do |method| define_method(method) do |message, opts=nil| opts ||= {} opts[:scope] = @scope @ui.send(method, message, opts) end end [:clear_line, :report_progress].each do |method| # By default do nothing, these aren't logged define_method(method) { |*args| @ui.send(method, *args) } end def machine(type, *data) opts = {} opts = data.pop if data.last.is_a?(Hash) opts[:scope] = @scope data << opts @ui.machine(type, *data) end end # This is a UI implementation that outputs color for various types # of messages. This should only be used with a TTY that supports color, # but is up to the user of the class to verify this is the case. class Colored < Basic # Terminal colors COLORS = { :clear => "\e[0m", :red => "\e[31m", :green => "\e[32m", :yellow => "\e[33m" } # Mapping between type of message and the color to output COLOR_MAP = { :warn => COLORS[:yellow], :error => COLORS[:red], :success => COLORS[:green] } # This is called by `say` to format the message for output. def format_message(type, message, opts=nil) # Get the format of the message before adding color. message = super # Colorize the message if there is a color for this type of message, # either specified by the options or via the default color map. if opts.has_key?(:color) color = COLORS[opts[:color]] message = "#{color}#{message}#{COLORS[:clear]}" else message = "#{COLOR_MAP[type]}#{message}#{COLORS[:clear]}" if COLOR_MAP[type] end message end end end end vagrant-1.4.3/lib/vagrant/util.rb000066400000000000000000000011241226132634600166760ustar00rootroot00000000000000module Vagrant module Util autoload :Busy, 'vagrant/util/busy' autoload :Counter, 'vagrant/util/counter' autoload :HashWithIndifferentAccess, 'vagrant/util/hash_with_indifferent_access' autoload :Platform, 'vagrant/util/platform' autoload :Retryable, 'vagrant/util/retryable' autoload :SafeExec, 'vagrant/util/safe_exec' autoload :StackedProcRunner, 'vagrant/util/stacked_proc_runner' autoload :TemplateRenderer, 'vagrant/util/template_renderer' end end vagrant-1.4.3/lib/vagrant/util/000077500000000000000000000000001226132634600163535ustar00rootroot00000000000000vagrant-1.4.3/lib/vagrant/util/ansi_escape_code_remover.rb000066400000000000000000000024251226132634600237060ustar00rootroot00000000000000module Vagrant module Util module ANSIEscapeCodeRemover # Removes ANSI escape code sequences from the text and returns # it. # # This removes all the ANSI escape codes listed here along with # the escape codes for VT100 terminals: # # http://ascii-table.com/ansi-escape-sequences.php def remove_ansi_escape_codes(text) # An array of regular expressions which match various kinds # of escape sequences. I can't think of a better single regular # expression or any faster way to do this. matchers = [/\e\[\d*[ABCD]/, # Matches things like \e[4D /\e\[(\d*;)?\d*[HF]/, # Matches \e[1;2H or \e[H /\e\[(s|u|2J|K)/, # Matches \e[s, \e[2J, etc. /\e\[=\d*[hl]/, # Matches \e[=24h /\e\[\?[1-9][hl]/, # Matches \e[?2h /\e\[20[hl]/, # Matches \e[20l] /\e[DME78H]/, # Matches \eD, \eH, etc. /\e\[[0-2]?[JK]/, # Matches \e[0J, \e[K, etc. ] # Take each matcher and replace it with emptiness. matchers.each do |matcher| text.gsub!(matcher, "") end text end end end end vagrant-1.4.3/lib/vagrant/util/busy.rb000066400000000000000000000040721226132634600176650ustar00rootroot00000000000000module Vagrant module Util # Utility class which allows blocks of code to be marked as "busy" # with a specified interrupt handler. During busy areas of code, it # is often undesirable for SIGINTs to immediately kill the application. # This class is a helper to cleanly register callbacks to handle this # situation. class Busy @@registered = [] @@mutex = Mutex.new class << self # Mark a given block of code as a "busy" block of code, which will # register a SIGINT handler for the duration of the block. When a # SIGINT occurs, the `sig_callback` proc will be called. It is up # to the callback to behave properly and exit the application. def busy(sig_callback) register(sig_callback) return yield ensure unregister(sig_callback) end # Registers a SIGINT handler. This typically is called from {busy}. # Callbacks are only registered once, so calling this multiple times # with the same callback has no consequence. def register(sig_callback) @@mutex.synchronize do registered << sig_callback registered.uniq! # Register the handler if this is our first callback. Signal.trap("INT") { fire_callbacks } if registered.length == 1 end end # Unregisters a SIGINT handler. def unregister(sig_callback) @@mutex.synchronize do registered.delete(sig_callback) # Remove the signal trap if no more registered callbacks exist Signal.trap("INT", "DEFAULT") if registered.empty? end end # Fires all the registered callbacks. def fire_callbacks registered.reverse.each { |r| r.call } end # Helper method to get access to the class variable. This is mostly # exposed for tests. This shouldn't be mucked with directly, since it's # structure may change at any time. def registered; @@registered; end end end end end vagrant-1.4.3/lib/vagrant/util/counter.rb000066400000000000000000000010241226132634600203540ustar00rootroot00000000000000require 'thread' module Vagrant module Util # Atomic counter implementation. This is useful for incrementing # a counter which is guaranteed to only be used once in its class. module Counter def get_and_update_counter(name=nil) name ||= :global mutex.synchronize do @__counter ||= Hash.new(1) result = @__counter[name] @__counter[name] += 1 result end end def mutex @__counter_mutex ||= Mutex.new end end end end vagrant-1.4.3/lib/vagrant/util/downloader.rb000066400000000000000000000124621226132634600210430ustar00rootroot00000000000000require "log4r" require "vagrant/util/busy" require "vagrant/util/subprocess" module Vagrant module Util # This class downloads files using various protocols by subprocessing # to cURL. cURL is a much more capable and complete download tool than # a hand-rolled Ruby library, so we defer to its expertise. class Downloader # Custom user agent provided to cURL so that requests to URL shorteners # are properly tracked. USER_AGENT = "Vagrant/#{VERSION}" def initialize(source, destination, options=nil) @logger = Log4r::Logger.new("vagrant::util::downloader") @source = source.to_s @destination = destination.to_s # Get the various optional values options ||= {} @ca_cert = options[:ca_cert] @continue = options[:continue] @insecure = options[:insecure] @ui = options[:ui] @client_cert = options[:client_cert] end # This executes the actual download, downloading the source file # to the destination with the given options used to initialize this # class. # # If this method returns without an exception, the download # succeeded. An exception will be raised if the download failed. def download! # Build the list of parameters to execute with cURL options = [ "--fail", "--location", "--max-redirs", "10", "--user-agent", USER_AGENT, "--output", @destination, ] options += ["--cacert", @ca_cert] if @ca_cert options += ["--continue-at", "-"] if @continue options << "--insecure" if @insecure options << "--cert" << @client_cert if @client_cert options << @source # Specify some options for the subprocess subprocess_options = {} # If we're in Vagrant, then we use the packaged CA bundle if Vagrant.in_installer? subprocess_options[:env] ||= {} subprocess_options[:env]["CURL_CA_BUNDLE"] = File.expand_path("cacert.pem", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]) end # This variable can contain the proc that'll be sent to # the subprocess execute. data_proc = nil if @ui # If we're outputting progress, then setup the subprocess to # tell us output so we can parse it out. subprocess_options[:notify] = :stderr progress_data = "" progress_regexp = /(\r(.+?))\r/ # Setup the proc that'll receive the real-time data from # the downloader. data_proc = Proc.new do |type, data| # Type will always be "stderr" because that is the only # type of data we're subscribed for notifications. # Accumulate progress_data progress_data << data while true # If we have a full amount of column data (two "\r") then # we report new progress reports. Otherwise, just keep # accumulating. match = progress_regexp.match(progress_data) break if !match data = match[2] progress_data.gsub!(match[1], "") # Ignore the first \r and split by whitespace to grab the columns columns = data.strip.split(/\s+/) # COLUMN DATA: # # 0 - % total # 1 - Total size # 2 - % received # 3 - Received size # 4 - % transferred # 5 - Transferred size # 6 - Average download speed # 7 - Average upload speed # 9 - Total time # 9 - Time spent # 10 - Time left # 11 - Current speed output = "Progress: #{columns[0]}% (Rate: #{columns[11]}/s, Estimated time remaining: #{columns[10]})" @ui.clear_line @ui.info(output, :new_line => false) end end end # Add the subprocess options onto the options we'll execute with options << subprocess_options # Create the callback that is called if we are interrupted interrupted = false int_callback = Proc.new do @logger.info("Downloader interrupted!") interrupted = true end @logger.info("Downloader starting download: ") @logger.info(" -- Source: #{@source}") @logger.info(" -- Destination: #{@destination}") # Execute! result = Busy.busy(int_callback) do Subprocess.execute("curl", *options, &data_proc) end # If the download was interrupted, then raise a specific error raise Errors::DownloaderInterrupted if interrupted # If we're outputting to the UI, clear the output to # avoid lingering progress meters. @ui.clear_line if @ui # If it didn't exit successfully, we need to parse the data and # show an error message. if result.exit_code != 0 @logger.warn("Downloader exit code: #{result.exit_code}") parts = result.stderr.split(/\n*curl:\s+\(\d+\)\s*/, 2) parts[1] ||= "" raise Errors::DownloaderError, :message => parts[1].chomp end # Everything succeeded true end end end end vagrant-1.4.3/lib/vagrant/util/file_checksum.rb000066400000000000000000000023121226132634600214770ustar00rootroot00000000000000# This is an "interface" that should be implemented by any digest class # passed into FileChecksum. Note that this isn't strictly enforced at # the moment, and this class isn't directly used. It is merely here for # documentation of structure of the class. class DigestClass def update(string); end def hexdigest; end end class FileChecksum BUFFER_SIZE = 16328 # Initializes an object to calculate the checksum of a file. The given # ``digest_klass`` should implement the ``DigestClass`` interface. Note # that the built-in Ruby digest classes duck type this properly: # Digest::MD5, Digest::SHA1, etc. def initialize(path, digest_klass) @digest_klass = digest_klass @path = path end # This calculates the checksum of the file and returns it as a # string. # # @return [String] def checksum digest = @digest_klass.new File.open(@path, "r") do |f| while !f.eof begin buf = f.readpartial(BUFFER_SIZE) digest.update(buf) rescue EOFError # Although we check for EOF earlier, this seems to happen # sometimes anyways [GH-2716]. break end end end return digest.hexdigest end end vagrant-1.4.3/lib/vagrant/util/file_mode.rb000066400000000000000000000004121226132634600206200ustar00rootroot00000000000000module Vagrant module Util class FileMode # This returns the file permissions as a string from # an octal number. def self.from_octal(octal) perms = sprintf("%o", octal) perms.reverse[0..2].reverse end end end end vagrant-1.4.3/lib/vagrant/util/hash_with_indifferent_access.rb000066400000000000000000000026431226132634600245610ustar00rootroot00000000000000module Vagrant module Util # A hash with indifferent access. Mostly taken from Thor/Rails (thanks). # Normally I'm not a fan of using an indifferent access hash since Symbols # are basically memory leaks in Ruby, but since Vagrant is typically a quick # one-off binary run and it doesn't use too many hash keys where this is # used, the effect should be minimal. # # hash[:foo] #=> 'bar' # hash['foo'] #=> 'bar' # class HashWithIndifferentAccess < ::Hash def initialize(hash={}, &block) super(&block) hash.each do |key, value| self[convert_key(key)] = value end end def [](key) super(convert_key(key)) end def []=(key, value) super(convert_key(key), value) end def delete(key) super(convert_key(key)) end def values_at(*indices) indices.collect { |key| self[convert_key(key)] } end def merge(other) dup.merge!(other) end def merge!(other) other.each do |key, value| self[convert_key(key)] = value end self end def key?(key) super(convert_key(key)) end alias_method :include?, :key? alias_method :has_key?, :key? alias_method :member?, :key? protected def convert_key(key) key.is_a?(Symbol) ? key.to_s : key end end end end vagrant-1.4.3/lib/vagrant/util/is_port_open.rb000066400000000000000000000025601226132634600214030ustar00rootroot00000000000000require "socket" require "timeout" module Vagrant module Util # Contains the method {#is_port_open?} to check if a port is open # (listening) or closed (not in use). This method isn't completely # fool-proof, but it works enough of the time to be useful. module IsPortOpen # Checks if a port is open (listening) on a given host and port. # # @param [String] host Hostname or IP address. # @param [Integer] port Port to check. # @return [Boolean] `true` if the port is open (listening), `false` # otherwise. def is_port_open?(host, port) # We wrap this in a timeout because once in awhile the TCPSocket # _will_ hang, but this signals that the port is closed. Timeout.timeout(1) do # Attempt to make a connection s = TCPSocket.new(host, port) # A connection was made! Properly clean up the socket, not caring # at all if any exception is raised, because we already know the # result. s.close rescue nil # The port is open if we reached this point, since we were able # to connect. return true end rescue Timeout::Error, Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::ENETUNREACH # Any of the above exceptions signal that the port is closed. return false end end end end vagrant-1.4.3/lib/vagrant/util/line_ending_helpers.rb000066400000000000000000000005151226132634600226760ustar00rootroot00000000000000module Vagrant module Util module LineEndingHelpers # Converts line endings to unix-style line endings in the # given string. # # @param [String] string Original string # @return [String] The fixed string def dos_to_unix(string) string.gsub("\r\n", "\n") end end end end vagrant-1.4.3/lib/vagrant/util/network_ip.rb000066400000000000000000000013251226132634600210620ustar00rootroot00000000000000module Vagrant module Util module NetworkIP # Returns the network address of the given IP and subnet. # # @return [String] def network_address(ip, subnet) ip = ip_parts(ip) netmask = ip_parts(subnet) # Bitwise-AND each octet to get the network address # in octets and join each part with a period to get # the resulting network address. ip.map { |part| part & netmask.shift }.join(".") end protected # Splits an IP into the four octets and returns each as an # integer in an array. # # @return [Array] def ip_parts(ip) ip.split(".").map { |i| i.to_i } end end end end vagrant-1.4.3/lib/vagrant/util/platform.rb000066400000000000000000000054711226132634600205330ustar00rootroot00000000000000require 'rbconfig' require 'tmpdir' require "vagrant/util/subprocess" module Vagrant module Util # This class just contains some platform checking code. class Platform class << self def cygwin? return true if ENV["VAGRANT_DETECTED_OS"] && ENV["VAGRANT_DETECTED_OS"].downcase.include?("cygwin") platform.include?("cygwin") end [:darwin, :bsd, :freebsd, :linux, :solaris].each do |type| define_method("#{type}?") do platform.include?(type.to_s) end end def windows? %W[mingw mswin].each do |text| return true if platform.include?(text) end false end # This takes any path and converts it to a full-length Windows # path on Windows machines in Cygwin. # # @return [String] def cygwin_windows_path(path) return path if !cygwin? process = Subprocess.execute("cygpath", "-w", "-l", "-a", path.to_s) process.stdout.chomp end # This checks if the filesystem is case sensitive. This is not a # 100% correct check, since it is possible that the temporary # directory runs a different filesystem than the root directory. # However, this works in many cases. def fs_case_sensitive? tmp_dir = Dir.mktmpdir("vagrant") tmp_file = File.join(tmp_dir, "FILE") File.open(tmp_file, "w") do |f| f.write("foo") end # The filesystem is case sensitive if the lowercased version # of the filename is NOT reported as existing. return !File.file?(File.join(tmp_dir, "file")) end # This expands the path and ensures proper casing of each part # of the path. def fs_real_path(path) path = Pathname.new(File.expand_path(path)) raise "Path must exist for path expansion" if !path.exist? return path if fs_case_sensitive? # Build up all the parts of the path original = [] while !path.root? original.unshift(path.basename.to_s) path = path.parent end # Traverse each part and join it into the resulting path original.each do |single| Dir.entries(path).each do |entry| if entry.downcase == single.downcase path = path.join(entry) end end end path end # Returns a boolean noting whether the terminal supports color. # output. def terminal_supports_colors? return ENV.has_key?("ANSICON") || cygwin? if windows? true end def platform RbConfig::CONFIG["host_os"].downcase end end end end end vagrant-1.4.3/lib/vagrant/util/retryable.rb000066400000000000000000000016301226132634600206710ustar00rootroot00000000000000require "log4r" module Vagrant module Util module Retryable # Retries a given block a specified number of times in the # event the specified exception is raised. If the retries # run out, the final exception is raised. # # This code is adapted slightly from the following blog post: # http://blog.codefront.net/2008/01/14/retrying-code-blocks-in-ruby-on-exceptions-whatever/ def retryable(opts=nil) logger = nil opts = { :tries => 1, :on => Exception }.merge(opts || {}) begin return yield rescue *opts[:on] => e if (opts[:tries] -= 1) > 0 logger = Log4r::Logger.new("vagrant::util::retryable") logger.info("Retryable exception raised: #{e.inspect}") sleep opts[:sleep].to_f if opts[:sleep] retry end raise end end end end end vagrant-1.4.3/lib/vagrant/util/safe_chdir.rb000066400000000000000000000014651226132634600207750ustar00rootroot00000000000000require 'thread' module Vagrant module Util class SafeChdir @@chdir_lock = Mutex.new # Safely changes directory of this process by putting a lock around # it so that it is thread safe. This will yield a block and when the # block exits it changes back to the original directory. # # @param [String] dir Dir to change to temporarily def self.safe_chdir(dir) lock = @@chdir_lock begin @@chdir_lock.synchronize {} rescue ThreadError # If we already hold the lock, just create a new lock so we # definitely don't block and don't get an error. lock = Mutex.new end lock.synchronize do Dir.chdir(dir) do return yield end end end end end end vagrant-1.4.3/lib/vagrant/util/safe_exec.rb000066400000000000000000000023611226132634600206240ustar00rootroot00000000000000module Vagrant module Util # This module provies a `safe_exec` method which is a drop-in # replacement for `Kernel.exec` which addresses a specific issue # which manifests on OS X 10.5 (GH-51) and perhaps other operating systems. # This issue causes `exec` to fail if there is more than one system # thread. In that case, `safe_exec` automatically falls back to # forking. class SafeExec def self.exec(command, *args) # Create a list of things to rescue from. Since this is OS # specific, we need to do some defined? checks here to make # sure they exist. rescue_from = [] rescue_from << Errno::EOPNOTSUPP if defined?(Errno::EOPNOTSUPP) rescue_from << Errno::E045 if defined?(Errno::E045) rescue_from << SystemCallError fork_instead = false begin pid = nil pid = fork if fork_instead Kernel.exec(command, *args) if pid.nil? Process.wait(pid) if pid rescue *rescue_from # We retried already, raise the issue and be done raise if fork_instead # The error manifested itself, retry with a fork. fork_instead = true retry end end end end end vagrant-1.4.3/lib/vagrant/util/safe_puts.rb000066400000000000000000000015771226132634600207030ustar00rootroot00000000000000module Vagrant module Util # This module provides a `safe_puts` method which outputs to # the given IO object, and rescues any broken pipe errors and # ignores them. This is useful in cases where you're outputting # to stdout, for example, and the stdout is closed, but you want to # keep running. module SafePuts # Uses `puts` on the given IO object and safely ignores any # Errno::EPIPE. # # @param [String] message Message to output. # @param [Hash] opts Options hash. def safe_puts(message=nil, opts=nil) message ||= "" opts = { :io => $stdout, :printer => :puts }.merge(opts || {}) begin opts[:io].send(opts[:printer], message) rescue Errno::EPIPE # This is what makes this a `safe` puts. return end end end end end vagrant-1.4.3/lib/vagrant/util/scoped_hash_override.rb000066400000000000000000000022431226132634600230600ustar00rootroot00000000000000module Vagrant module Util # This allows for hash options to be overridden by a scope key # prefix. An example speaks best here. Imagine the following hash: # # original = { # :id => "foo", # :mitchellh__id => "bar", # :mitchellh__other => "foo" # } # # scoped = scoped_hash_override(original, "mitchellh") # # scoped == { # :id => "bar", # :other => "foo" # } # module ScopedHashOverride def scoped_hash_override(original, scope) # Convert the scope to a string in case a symbol was given since # we use string comparisons for everything. scope = scope.to_s # Shallow copy the hash for the result result = original.dup original.each do |key, value| parts = key.to_s.split("__", 2) # If we don't have the proper parts, then bail next if parts.length != 2 # If this is our scope, then override if parts[0] == scope result[parts[1].to_sym] = value result.delete(key) end end result end end end end vagrant-1.4.3/lib/vagrant/util/shell_quote.rb000066400000000000000000000005661226132634600212330ustar00rootroot00000000000000module Vagrant module Util module ShellQuote # This will auto-escape the text with the given quote mark type. # # @param [String] text Text to escape # @param [String] quote The quote character, such as " def self.escape(text, quote) text.gsub(/#{quote}/) do |m| "#{m}\\#{m}#{m}" end end end end end vagrant-1.4.3/lib/vagrant/util/ssh.rb000066400000000000000000000150111226132634600174730ustar00rootroot00000000000000require "log4r" require 'childprocess' require "vagrant/util/file_mode" require "vagrant/util/platform" require "vagrant/util/safe_exec" require "vagrant/util/subprocess" require "vagrant/util/which" module Vagrant module Util # This is a class that has helpers on it for dealing with SSH. These # helpers don't depend on any part of Vagrant except what is given # via the parameters. class SSH LOGGER = Log4r::Logger.new("vagrant::util::ssh") # Checks that the permissions for a private key are valid, and fixes # them if possible. SSH requires that permissions on the private key # are 0600 on POSIX based systems. This will make a best effort to # fix these permissions if they are not properly set. # # @param [Pathname] key_path The path to the private key. def self.check_key_permissions(key_path) # Don't do anything if we're on Windows, since Windows doesn't worry # about key permissions. return if Platform.windows? LOGGER.debug("Checking key permissions: #{key_path}") stat = key_path.stat if !stat.owned? # The SSH key must be owned by ourselves raise Errors::SSHKeyBadOwner, :key_path => key_path end if FileMode.from_octal(stat.mode) != "600" LOGGER.info("Attempting to correct key permissions to 0600") key_path.chmod(0600) # Re-stat the file to get the new mode, and verify it worked stat = key_path.stat if FileMode.from_octal(stat.mode) != "600" raise Errors::SSHKeyBadPermissions, :key_path => key_path end end rescue Errno::EPERM # This shouldn't happen since we verify we own the file, but # it is possible in theory, so we raise an error. raise Errors::SSHKeyBadPermissions, :key_path => key_path end # Halts the running of this process and replaces it with a full-fledged # SSH shell into a remote machine. # # Note: This method NEVER returns. The process ends after this. # # @param [Hash] ssh_info This is the SSH information. For the keys # required please see the documentation of {Machine#ssh_info}. # @param [Hash] opts These are additional options that are supported # by exec. def self.exec(ssh_info, opts={}) # Ensure the platform supports ssh. On Windows there are several programs which # include ssh, notably git, mingw and cygwin, but make sure ssh is in the path! ssh_path = Which.which("ssh") if !ssh_path if Platform.windows? raise Errors::SSHUnavailableWindows, :host => ssh_info[:host], :port => ssh_info[:port], :username => ssh_info[:username], :key_path => ssh_info[:private_key_path].join(", ") end raise Errors::SSHUnavailable end # On Windows, we need to detect whether SSH is actually "plink" # underneath the covers. In this case, we tell the user. if Platform.windows? r = Subprocess.execute(ssh_path) if r.stdout.include?("PuTTY Link") raise Errors::SSHIsPuttyLink, :host => ssh_info[:host], :port => ssh_info[:port], :username => ssh_info[:username], :key_path => ssh_info[:private_key_path].join(", ") end end # If plain mode is enabled then we don't do any authentication (we don't # set a user or an identity file) plain_mode = opts[:plain_mode] options = {} options[:host] = ssh_info[:host] options[:port] = ssh_info[:port] options[:username] = ssh_info[:username] options[:private_key_path] = ssh_info[:private_key_path] # Command line options command_options = [ "-p", options[:port].to_s, "-o", "Compression=yes", "-o", "DSAAuthentication=yes", "-o", "LogLevel=FATAL", "-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null"] # Solaris/OpenSolaris/Illumos uses SunSSH which doesn't support the # IdentitiesOnly option. Also, we don't enable it in plain mode so # that SSH properly searches our identities and tries to do it itself. if !Platform.solaris? && !plain_mode command_options += ["-o", "IdentitiesOnly=yes"] end # If we're not in plain mode, attach the private key path. if !plain_mode options[:private_key_path].each do |path| command_options += ["-i", path.to_s] end end if ssh_info[:forward_x11] # Both are required so that no warnings are shown regarding X11 command_options += [ "-o", "ForwardX11=yes", "-o", "ForwardX11Trusted=yes"] end if ssh_info[:proxy_command] command_options += ["-o", "ProxyCommand=#{ssh_info[:proxy_command]}"] end # Configurables -- extra_args should always be last due to the way the # ssh args parser works. e.g. if the user wants to use the -t option, # any shell command(s) she'd like to run on the remote server would # have to be the last part of the 'ssh' command: # # $ ssh localhost -t -p 2222 "cd mydirectory; bash" # # Without having extra_args be last, the user loses this ability command_options += ["-o", "ForwardAgent=yes"] if ssh_info[:forward_agent] command_options.concat(opts[:extra_args]) if opts[:extra_args] # Build up the host string for connecting host_string = options[:host] host_string = "#{options[:username]}@#{host_string}" if !plain_mode command_options.unshift(host_string) # On Cygwin we want to get rid of any DOS file warnings because # we really don't care since both work. ENV["nodosfilewarning"] = "1" if Platform.cygwin? # Invoke SSH with all our options if !opts[:subprocess] LOGGER.info("Invoking SSH: #{command_options.inspect}") SafeExec.exec("ssh", *command_options) return end # If we're still here, it means we're supposed to subprocess # out to ssh rather than exec it. LOGGER.info("Executing SSH in subprocess: #{command_options.inspect}") process = ChildProcess.build("ssh", *command_options) process.io.inherit! process.start process.wait return process.exit_code end end end end vagrant-1.4.3/lib/vagrant/util/stacked_proc_runner.rb000066400000000000000000000021531226132634600227330ustar00rootroot00000000000000module Vagrant module Util # Represents the "stacked proc runner" behavior which is used a # couple places within Vagrant. This allows procs to "stack" on # each other, then all execute in a single action. An example of # its uses can be seen in the {Config} class. module StackedProcRunner # Returns the proc stack. This should always be called as the # accessor of the stack. The instance variable itself should _never_ # be used. # # @return [Array] def proc_stack @_proc_stack ||= [] end # Adds (pushes) a proc to the stack. The actual proc added here is # not executed, but merely stored. # # @param [Proc] block def push_proc(&block) proc_stack << block end # Executes all the procs on the stack, passing in the given arguments. # The stack is not cleared afterwords. It is up to the user of this # mixin to clear the stack by calling `proc_stack.clear`. def run_procs!(*args) proc_stack.each do |proc| proc.call(*args) end end end end endvagrant-1.4.3/lib/vagrant/util/string_block_editor.rb000066400000000000000000000044271226132634600227350ustar00rootroot00000000000000module Vagrant module Util # This class modifies strings by creating and managing Vagrant-owned # "blocks" via wrapping them in specially formed comments. # # This is useful when modifying a file that someone else owns and adding # automatic entries into it. Example: /etc/exports or some other # configuration file. # # Vagrant marks ownership of a block in the string by wrapping it in # VAGRANT-BEGIN and VAGRANT-END comments with a unique ID. Example: # # foo # # VAGRANT-BEGIN: id # some contents # created by vagrant # # VAGRANT-END: id # # The goal of this class is to be able to insert and remove these # blocks without modifying anything else in the string. # # The strings usually come from files but it is up to the caller to # manage the file resource. class StringBlockEditor # The current string value. This is the value that is modified by # the methods below. # # @return [String] attr_reader :value def initialize(string) @value = string end # This returns the keys (or ids) that are in the string. # # @return [] def keys regexp = /^#\s*VAGRANT-BEGIN:\s*(.+?)$\r?\n?(.*)$\r?\n?^#\s*VAGRANT-END:\s(\1)$/m @value.scan(regexp).map do |match| match[0] end end # This deletes the block with the given key if it exists. def delete(key) key = Regexp.quote(key) regexp = /^#\s*VAGRANT-BEGIN:\s*#{key}$.*^#\s*VAGRANT-END:\s*#{key}$\r?\n?/m @value.gsub!(regexp, "") end # This gets the value of the block with the given key. def get(key) key = Regexp.quote(key) regexp = /^#\s*VAGRANT-BEGIN:\s*#{key}$\r?\n?(.*?)\r?\n?^#\s*VAGRANT-END:\s*#{key}$\r?\n?/m match = regexp.match(@value) return nil if !match match[1] end # This inserts a block with the given key and value. # # @param [String] key # @param [String] value def insert(key, value) # Insert the new block into the value new_block = < ex # Raise our own version of the error so that users of the class # don't need to be aware of ChildProcess raise LaunchError.new(ex.message) end # Make sure the stdin does not buffer process.io.stdin.sync = true if RUBY_PLATFORM != "java" # On Java, we have to close after. See down the method... # Otherwise, we close the writers right here, since we're # not on the writing side. stdout_writer.close stderr_writer.close end # Create a dictionary to store all the output we see. io_data = { :stdout => "", :stderr => "" } # Record the start time for timeout purposes start_time = Time.now.to_i @logger.debug("Selecting on IO") while true writers = notify_stdin ? [process.io.stdin] : [] results = IO.select([stdout, stderr], writers, nil, timeout || 0.1) results ||= [] readers = results[0] writers = results[1] # Check if we have exceeded our timeout raise TimeoutExceeded, process.pid if timeout && (Time.now.to_i - start_time) > timeout # Check the readers to see if they're ready if readers && !readers.empty? readers.each do |r| # Read from the IO object data = read_io(r) # We don't need to do anything if the data is empty next if data.empty? io_name = r == stdout ? :stdout : :stderr @logger.debug("#{io_name}: #{data.chomp}") io_data[io_name] += data yield io_name, data if block_given? && notify_table[io_name] end end # Break out if the process exited. We have to do this before # attempting to write to stdin otherwise we'll get a broken pipe # error. break if process.exited? # Check the writers to see if they're ready, and notify any listeners if writers && !writers.empty? yield :stdin, process.io.stdin if block_given? end end # Wait for the process to end. begin remaining = (timeout || 32000) - (Time.now.to_i - start_time) remaining = 0 if remaining < 0 @logger.debug("Waiting for process to exit. Remaining to timeout: #{remaining}") process.poll_for_exit(remaining) rescue ChildProcess::TimeoutError raise TimeoutExceeded, process.pid end @logger.debug("Exit status: #{process.exit_code}") # Read the final output data, since it is possible we missed a small # amount of text between the time we last read data and when the # process exited. [stdout, stderr].each do |io| # Read the extra data, ignoring if there isn't any extra_data = read_io(io) next if extra_data == "" # Log it out and accumulate io_name = io == stdout ? :stdout : :stderr io_data[io_name] += extra_data @logger.debug("#{io_name}: #{extra_data.chomp}") # Yield to any listeners any remaining data yield io_name, extra_data if block_given? end if RUBY_PLATFORM == "java" # On JRuby, we need to close the writers after the process, # for some reason. See GH-711. stdout_writer.close stderr_writer.close end # Return an exit status container return Result.new(process.exit_code, io_data[:stdout], io_data[:stderr]) end protected # Reads data from an IO object while it can, returning the data it reads. # When it encounters a case when it can't read anymore, it returns the # data. # # @return [String] def read_io(io) data = "" while true begin if Platform.windows? # Windows doesn't support non-blocking reads on # file descriptors or pipes so we have to get # a bit more creative. # Check if data is actually ready on this IO device. # We have to do this since `readpartial` will actually block # until data is available, which can cause blocking forever # in some cases. results = IO.select([io], nil, nil, 0.1) break if !results || results[0].empty? # Read! data << io.readpartial(READ_CHUNK_SIZE) else # Do a simple non-blocking read on the IO object data << io.read_nonblock(READ_CHUNK_SIZE) end rescue Exception => e # The catch-all rescue here is to support multiple Ruby versions, # since we use some Ruby 1.9 specific exceptions. breakable = false if e.is_a?(EOFError) # An `EOFError` means this IO object is done! breakable = true elsif defined?(IO::WaitReadable) && e.is_a?(IO::WaitReadable) # IO::WaitReadable is only available on Ruby 1.9+ # An IO::WaitReadable means there may be more IO but this # IO object is not ready to be read from yet. No problem, # we read as much as we can, so we break. breakable = true elsif e.is_a?(Errno::EAGAIN) # Otherwise, we just look for the EAGAIN error which should be # all that IO::WaitReadable does in Ruby 1.9. breakable = true end # Break out if we're supposed to. Otherwise re-raise the error # because it is a real problem. break if breakable raise end end data end # An error which raises when a process fails to start class LaunchError < StandardError; end # An error which occurs when the process doesn't end within # the given timeout. class TimeoutExceeded < StandardError attr_reader :pid def initialize(pid) super() @pid = pid end end # Container class to store the results of executing a subprocess. class Result attr_reader :exit_code attr_reader :stdout attr_reader :stderr def initialize(exit_code, stdout, stderr) @exit_code = exit_code @stdout = stdout @stderr = stderr end end end end end vagrant-1.4.3/lib/vagrant/util/template_renderer.rb000066400000000000000000000052631226132634600224070ustar00rootroot00000000000000require 'ostruct' require 'erubis' module Vagrant module Util # This class is used to render the ERB templates in the # `GEM_ROOT/templates` directory. class TemplateRenderer < OpenStruct class << self # Render a given template and return the result. This method optionally # takes a block which will be passed the renderer prior to rendering, which # allows the caller to set any view variables within the renderer itself. # # @return [String] Rendered template def render(*args) render_with(:render, *args) end # Render a given string and return the result. This method optionally # takes a block which will be passed the renderer prior to rendering, which # allows the caller to set any view variables within the renderer itself. # # @param [String] template The template data string. # @return [String] Rendered template def render_string(*args) render_with(:render_string, *args) end # Method used internally to DRY out the other renderers. This method # creates and sets up the renderer before calling a specified method on it. def render_with(method, template, data={}) renderer = new(template, data) yield renderer if block_given? renderer.send(method.to_sym) end end def initialize(template, data = {}) super() data[:template] = template data.each do |key, value| send("#{key}=", value) end end # Renders the template using the class intance as the binding. Because the # renderer inherits from `OpenStruct`, additional view variables can be # added like normal accessors. # # @return [String] def render # TODO: Seems like a pretty dirty way to do this. Perhaps refactor this old_template = template result = nil File.open(full_template_path, 'r') do |f| self.template = f.read result = render_string end result ensure self.template = old_template end # Renders a template, handling the template as a string, but otherwise # acting the same way as {#render}. # # @return [String] def render_string Erubis::Eruby.new(template, :trim => true).result(binding) end # Returns the full path to the template, taking into accoun the gem directory # and adding the `.erb` extension to the end. # # @return [String] def full_template_path Vagrant.source_root.join('templates', "#{template}.erb").to_s.squeeze("/") end end end end vagrant-1.4.3/lib/vagrant/util/which.rb000066400000000000000000000026741226132634600200130ustar00rootroot00000000000000require "vagrant/util/platform" module Vagrant module Util class Which # Cross-platform way of finding an executable in the PATH. # # which('ruby') #=> /usr/bin/ruby # # This code is adapted from the following post by mislav: # http://stackoverflow.com/questions/2108727/which-in-ruby-checking-if-program-exists-in-path-from-ruby # # @param [String] cmd The command to search for in the PATH. # @return [String] The full path to the executable or `nil` if not found. def self.which(cmd) exts = nil if !Platform.windows? || ENV['PATHEXT'].nil? # If the PATHEXT variable is empty, we're on *nix and need to find # the exact filename exts = [''] elsif File.extname(cmd).length != 0 # On Windows: if filename contains an extension, we must match that # exact filename exts = [''] else # On Windows: otherwise try to match all possible executable file # extensions (.EXE .COM .BAT etc.) exts = ENV['PATHEXT'].split(';') end ENV['PATH'].encode('UTF-8', 'binary', invalid: :replace, undef: :replace, replace: '').split(File::PATH_SEPARATOR).each do |path| exts.each do |ext| exe = "#{path}#{File::SEPARATOR}#{cmd}#{ext}" return exe if File.executable? exe end end return nil end end end end vagrant-1.4.3/lib/vagrant/version.rb000066400000000000000000000003271226132634600174120ustar00rootroot00000000000000module Vagrant # This will always be up to date with the current version of Vagrant, # since it is used to generate the gemspec and is also the source of # the version for `vagrant -v` VERSION = "1.4.3" end vagrant-1.4.3/plugins/000077500000000000000000000000001226132634600146475ustar00rootroot00000000000000vagrant-1.4.3/plugins/README.md000066400000000000000000000003531226132634600161270ustar00rootroot00000000000000# Vagrant Core Plugins These are plugins that ship with Vagrant. Vagrant core uses its own plugin system to power a lot of the core pieces that ship with Vagrant. Each plugin will have its own README which explains its specific role. vagrant-1.4.3/plugins/commands/000077500000000000000000000000001226132634600164505ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/box/000077500000000000000000000000001226132634600172405ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/box/command/000077500000000000000000000000001226132634600206565ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/box/command/add.rb000066400000000000000000000046021226132634600217350ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandBox module Command class Add < Vagrant.plugin("2", :command) def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant box add [--provider provider] [-h]" o.separator "" o.on("--checksum VALUE", String, "Checksum") do |c| options[:checksum] = c end o.on("--checksum-type VALUE", String, "Checksum type") do |c| options[:checksum_type] = c.to_sym end o.on("-c", "--clean", "Remove old temporary download if it exists.") do |c| options[:clean] = c end o.on("-f", "--force", "Overwrite an existing box if it exists.") do |f| options[:force] = f end o.on("--insecure", "If set, SSL certs will not be validated.") do |i| options[:insecure] = i end o.on("--cacert certfile", String, "CA certificate") do |c| options[:ca_cert] = c end o.on("--cert certfile", String, "The client SSL cert") do |c| options[:client_cert] = c end o.on("--provider provider", String, "The provider that backs the box.") do |p| options[:provider] = p end end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 2 # Get the provider if one was set provider = nil provider = options[:provider].to_sym if options[:provider] @env.action_runner.run(Vagrant::Action.action_box_add, { :box_name => argv[0], :box_provider => provider, :box_url => argv[1], :box_checksum_type => options[:checksum_type], :box_checksum => options[:checksum], :box_clean => options[:clean], :box_force => options[:force], :box_download_ca_cert => options[:ca_cert], :box_download_client_cert => options[:client_cert], :box_download_insecure => options[:insecure], }) # Success, exit status 0 0 end end end end end vagrant-1.4.3/plugins/commands/box/command/list.rb000066400000000000000000000037451226132634600221670ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandBox module Command class List < Vagrant.plugin("2", :command) def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant box list" o.separator "" o.on("-i", "--box-info", "Displays additional information about the boxes.") do |i| options[:info] = i end end # Parse the options argv = parse_options(opts) return if !argv boxes = @env.boxes.all.sort if boxes.empty? return @env.ui.warn(I18n.t("vagrant.commands.box.no_installed_boxes"), :prefix => false) end list_boxes(boxes, options[:info]) # Success, exit status 0 0 end private def list_boxes(boxes, extra_info) # Find the longest box name longest_box = boxes.max_by { |x| x[0].length } longest_box_length = longest_box[0].length # Go through each box and output the information about it. We # ignore the "v1" param for now since I'm not yet sure if its # important for the user to know what boxes need to be upgraded # and which don't, since we plan on doing that transparently. boxes.each do |name, provider, _v1| @env.ui.info("#{name.ljust(longest_box_length)} (#{provider})", :prefix => false) @env.ui.machine("box-name", name) @env.ui.machine("box-provider", provider) info_file = @env.boxes.find(name, provider).directory.join("info.json") if info_file.file? info = JSON.parse(info_file.read) info.each do |k, v| @env.ui.machine("box-info", k, v) if extra_info @env.ui.info(" - #{k}: #{v}", prefix: false) end end end end end end end end end vagrant-1.4.3/plugins/commands/box/command/remove.rb000066400000000000000000000024351226132634600225040ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandBox module Command class Remove < Vagrant.plugin("2", :command) def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant box remove " end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 1 if !argv[1] # Try to automatically determine the provider. providers = [] @env.boxes.all.each do |name, provider| if name == argv[0] providers << provider end end if providers.length > 1 @env.ui.error( I18n.t("vagrant.commands.box.remove_must_specify_provider", name: argv[0], providers: providers.join(", "))) return 1 end argv[1] = providers[0] || "" end @env.action_runner.run(Vagrant::Action.action_box_remove, { :box_name => argv[0], :box_provider => argv[1] }) # Success, exit status 0 0 end end end end end vagrant-1.4.3/plugins/commands/box/command/repackage.rb000066400000000000000000000023071226132634600231270ustar00rootroot00000000000000require "fileutils" require 'optparse' require "pathname" module VagrantPlugins module CommandBox module Command class Repackage < Vagrant.plugin("2", :command) def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant box repackage " end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 2 box_name = argv[0] box_provider = argv[1].to_sym # Verify the box exists that we want to repackage box = nil begin box = @env.boxes.find(box_name, box_provider) rescue Vagrant::Errors::BoxUpgradeRequired @env.boxes.upgrade(box_name) retry end raise Vagrant::Errors::BoxNotFound, :name => box_name, :provider => box_provider if !box # Repackage the box output_path = Pathname.new(File.expand_path(@env.config_global.package.name, FileUtils.pwd)) box.repackage(output_path) # Success, exit status 0 0 end end end end end vagrant-1.4.3/plugins/commands/box/command/root.rb000066400000000000000000000045411226132634600221720ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandBox module Command class Root < Vagrant.plugin("2", :command) def self.synopsis "manages boxes: installation, removal, etc." end def initialize(argv, env) super @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) @subcommands = Vagrant::Registry.new @subcommands.register(:add) do require File.expand_path("../add", __FILE__) Add end @subcommands.register(:list) do require File.expand_path("../list", __FILE__) List end @subcommands.register(:remove) do require File.expand_path("../remove", __FILE__) Remove end @subcommands.register(:repackage) do require File.expand_path("../repackage", __FILE__) Repackage end end def execute if @main_args.include?("-h") || @main_args.include?("--help") # Print the help for all the box commands. return help end # If we reached this far then we must have a subcommand. If not, # then we also just print the help and exit. command_class = @subcommands.get(@sub_command.to_sym) if @sub_command return help if !command_class || !@sub_command @logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}") # Initialize and execute the command class command_class.new(@sub_args, @env).execute end # Prints the help out for this command def help opts = OptionParser.new do |opts| opts.banner = "Usage: vagrant box []" opts.separator "" opts.separator "Available subcommands:" # Add the available subcommands as separators in order to print them # out as well. keys = [] @subcommands.each { |key, value| keys << key.to_s } keys.sort.each do |key| opts.separator " #{key}" end opts.separator "" opts.separator "For help on any individual command run `vagrant box COMMAND -h`" end @env.ui.info(opts.help, :prefix => false) end end end end end vagrant-1.4.3/plugins/commands/box/plugin.rb000066400000000000000000000005151226132634600210640ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandBox class Plugin < Vagrant.plugin("2") name "box command" description "The `box` command gives you a way to manage boxes." command("box") do require File.expand_path("../command/root", __FILE__) Command::Root end end end end vagrant-1.4.3/plugins/commands/destroy/000077500000000000000000000000001226132634600201415ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/destroy/command.rb000066400000000000000000000021121226132634600221000ustar00rootroot00000000000000module VagrantPlugins module CommandDestroy class Command < Vagrant.plugin("2", :command) def self.synopsis "stops and deletes all traces of the vagrant machine" end def execute options = {} options[:force] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant destroy [vm-name]" o.separator "" o.on("-f", "--force", "Destroy without confirmation.") do |f| options[:force] = f end end # Parse the options argv = parse_options(opts) return if !argv @logger.debug("'Destroy' each target VM...") declined = false with_target_vms(argv, :reverse => true) do |vm| action_env = vm.action( :destroy, :force_confirm_destroy => options[:force]) declined = true if action_env.has_key?(:force_confirm_destroy_result) && action_env[:force_confirm_destroy_result] == false end # Success if no confirms were declined declined ? 1 : 0 end end end end vagrant-1.4.3/plugins/commands/destroy/plugin.rb000066400000000000000000000007221226132634600217650ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandDestroy class Plugin < Vagrant.plugin("2") name "destroy command" description <<-DESC The `destroy` command deletes and removes the files and record of your virtual machines. All data is lost and a new VM will have to be created using `up` DESC command("destroy") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/halt/000077500000000000000000000000001226132634600174005ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/halt/command.rb000066400000000000000000000015571226132634600213530ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandHalt class Command < Vagrant.plugin("2", :command) def self.synopsis "stops the vagrant machine" end def execute options = {} options[:force] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant halt [vm-name] [--force] [-h]" o.separator "" o.on("-f", "--force", "Force shut down (equivalent of pulling power)") do |f| options[:force] = f end end # Parse the options argv = parse_options(opts) return if !argv @logger.debug("Halt command: #{argv.inspect} #{options.inspect}") with_target_vms(argv) do |vm| vm.action(:halt, :force_halt => options[:force]) end # Success, exit status 0 0 end end end end vagrant-1.4.3/plugins/commands/halt/plugin.rb000066400000000000000000000006151226132634600212250ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandHalt class Plugin < Vagrant.plugin("2") name "halt command" description <<-DESC The `halt` command shuts your virtual machine down forcefully. The command `up` recreates it. DESC command("halt") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/help/000077500000000000000000000000001226132634600174005ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/help/command.rb000066400000000000000000000004731226132634600213470ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandHelp class Command < Vagrant.plugin("2", :command) def self.synopsis "shows the help for a subcommand" end def execute return @env.cli([]) if @argv.empty? @env.cli([@argv[0], "-h"]) end end end end vagrant-1.4.3/plugins/commands/help/plugin.rb000066400000000000000000000005361226132634600212270ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandHelp class Plugin < Vagrant.plugin("2") name "help command" description <<-DESC The `help` command shows help for the given command. DESC command("help") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/init/000077500000000000000000000000001226132634600174135ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/init/command.rb000066400000000000000000000033161226132634600213610ustar00rootroot00000000000000require 'optparse' require 'vagrant/util/template_renderer' module VagrantPlugins module CommandInit class Command < Vagrant.plugin("2", :command) def self.synopsis "initializes a new Vagrant environment by creating a Vagrantfile" end def execute options = { output: "Vagrantfile" } opts = OptionParser.new do |o| o.banner = "Usage: vagrant init [box-name] [box-url]" o.on("--output FILENAME", String, "Output path for the box. '-' for stdout.") do |output| options[:output] = output end end # Parse the options argv = parse_options(opts) return if !argv save_path = nil if options[:output] != "-" save_path = Pathname.new(options[:output]).expand_path(@env.cwd) raise Vagrant::Errors::VagrantfileExistsError if save_path.exist? end template_path = ::Vagrant.source_root.join("templates/commands/init/Vagrantfile") contents = Vagrant::Util::TemplateRenderer.render(template_path, :box_name => argv[0] || "base", :box_url => argv[1]) if save_path # Write out the contents begin save_path.open("w+") do |f| f.write(contents) end rescue Errno::EACCES raise Vagrant::Errors::VagrantfileWriteError end @env.ui.info(I18n.t("vagrant.commands.init.success"), prefix: false) else @env.ui.info(contents, prefix: false) end # Success, exit status 0 0 end end end end vagrant-1.4.3/plugins/commands/init/plugin.rb000066400000000000000000000006061226132634600212400ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandInit class Plugin < Vagrant.plugin("2") name "init command" description <<-DESC The `init` command sets up your working directory to be a Vagrant-managed environment. DESC command("init") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/package/000077500000000000000000000000001226132634600200435ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/package/command.rb000066400000000000000000000046601226132634600220140ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandPackage class Command < Vagrant.plugin("2", :command) def self.synopsis "packages a running vagrant environment into a box" end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant package [vm-name] [--base name] [--output name.box]" o.separator " [--include one,two,three] [--vagrantfile file]" o.separator "" o.on("--base NAME", "Name of a VM in virtualbox to package as a base box") do |b| options[:base] = b end o.on("--output NAME", "Name of the file to output") do |output| options[:output] = output end o.on("--include x,y,z", Array, "Additional files to package with the box.") do |i| options[:include] = i end o.on("--vagrantfile file", "Vagrantfile to package with the box.") do |v| options[:vagrantfile] = v end end # Parse the options argv = parse_options(opts) return if !argv @logger.debug("package options: #{options.inspect}") if options[:base] package_base(options) else package_target(argv[0], options) end # Success, exit status 0 0 end protected def package_base(options) # XXX: This whole thing is hardcoded and very temporary. The whole # `vagrant package --base` process is deprecated for something much # better in the future. We just hardcode this to keep VirtualBox working # for now. provider = Vagrant.plugin("2").manager.providers[:virtualbox] vm = Vagrant::Machine.new( options[:base], :virtualbox, provider[0], nil, provider[1], @env.config_global, nil, nil, @env, true) @logger.debug("Packaging base VM: #{vm.name}") package_vm(vm, options) end def package_target(name, options) with_target_vms(name, :single_target => true) do |vm| @logger.debug("Packaging VM: #{vm.name}") package_vm(vm, options) end end def package_vm(vm, options) opts = options.inject({}) do |acc, data| k,v = data acc["package.#{k}"] = v acc end vm.action(:package, opts) end end end end vagrant-1.4.3/plugins/commands/package/plugin.rb000066400000000000000000000006421226132634600216700ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandPackage class Plugin < Vagrant.plugin("2") name "package command" description <<-DESC The `package` command will take a previously existing Vagrant environment and package it into a box file. DESC command("package") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/plugin/000077500000000000000000000000001226132634600177465ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/plugin/action.rb000066400000000000000000000035341226132634600215550ustar00rootroot00000000000000require "pathname" require "vagrant/action/builder" module VagrantPlugins module CommandPlugin module Action # This middleware sequence will install a plugin. def self.action_install Vagrant::Action::Builder.new.tap do |b| b.use BundlerCheck b.use InstallGem b.use PruneGems end end # This middleware sequence licenses paid addons. def self.action_license Vagrant::Action::Builder.new.tap do |b| b.use BundlerCheck b.use LicensePlugin end end # This middleware sequence will list all installed plugins. def self.action_list Vagrant::Action::Builder.new.tap do |b| b.use BundlerCheck b.use ListPlugins end end # This middleware sequence will uninstall a plugin. def self.action_uninstall Vagrant::Action::Builder.new.tap do |b| b.use BundlerCheck b.use UninstallPlugin b.use PruneGems end end # This middleware sequence will update a plugin. def self.action_update Vagrant::Action::Builder.new.tap do |b| b.use BundlerCheck b.use PluginExistsCheck b.use InstallGem b.use PruneGems end end # The autoload farm action_root = Pathname.new(File.expand_path("../action", __FILE__)) autoload :BundlerCheck, action_root.join("bundler_check") autoload :InstallGem, action_root.join("install_gem") autoload :LicensePlugin, action_root.join("license_plugin") autoload :ListPlugins, action_root.join("list_plugins") autoload :PluginExistsCheck, action_root.join("plugin_exists_check") autoload :PruneGems, action_root.join("prune_gems") autoload :UninstallPlugin, action_root.join("uninstall_plugin") end end end vagrant-1.4.3/plugins/commands/plugin/action/000077500000000000000000000000001226132634600212235ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/plugin/action/bundler_check.rb000066400000000000000000000012471226132634600243440ustar00rootroot00000000000000module VagrantPlugins module CommandPlugin module Action class BundlerCheck def initialize(app, env) @app = app end def call(env) # Bundler sets up its own custom gem load paths such that our # own gems are never loaded. Therefore, give an error if a user # tries to install gems while within a Bundler-managed environment. if defined?(Bundler) require 'bundler/shared_helpers' if Bundler::SharedHelpers.in_bundle? raise Vagrant::Errors::GemCommandInBundler end end @app.call(env) end end end end end vagrant-1.4.3/plugins/commands/plugin/action/install_gem.rb000066400000000000000000000105001226132634600240420ustar00rootroot00000000000000require "rubygems" require "rubygems/dependency_installer" begin require "rubygems/format" rescue LoadError # rubygems 2.x end require "log4r" module VagrantPlugins module CommandPlugin module Action # This action takes the `:plugin_name` variable in the environment # and installs it using the RubyGems API. class InstallGem def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::plugins::plugincommand::installgem") end def call(env) plugin_name = env[:plugin_name] prerelease = env[:plugin_prerelease] version = env[:plugin_version] # Determine the plugin name we'll look for in the installed set # in order to determine the version and all that. find_plugin_name = plugin_name if plugin_name =~ /\.gem$/ # If we're installing from a gem file, determine the name # based on the spec in the file. pkg = if defined?(Gem::Format) # RubyGems 1.x Gem::Format.from_file_by_path(plugin_name) else # RubyGems 2.x Gem::Package.new(plugin_name) end find_plugin_name = pkg.spec.name version = pkg.spec.version end # Install the gem plugin_name_label = plugin_name plugin_name_label += ' --prerelease' if prerelease plugin_name_label += " --version '#{version}'" if version env[:ui].info(I18n.t("vagrant.commands.plugin.installing", :name => plugin_name_label)) installed_gems = env[:gem_helper].with_environment do # Override the list of sources by the ones set as a parameter if given if env[:plugin_sources] @logger.info("Custom plugin sources: #{env[:plugin_sources]}") Gem.sources = env[:plugin_sources] end installer = Gem::DependencyInstaller.new(:document => [], :prerelease => prerelease) # If we don't have a version, use the default version version ||= Gem::Requirement.default begin installer.install(plugin_name, version) rescue Gem::GemNotFoundException raise Vagrant::Errors::PluginInstallNotFound, :name => plugin_name end end # The plugin spec is the last installed gem since RubyGems # currently always installed the requested gem last. @logger.debug("Installed #{installed_gems.length} gems.") plugin_spec = installed_gems.find do |gem| gem.name.downcase == find_plugin_name.downcase end # Store the installed name so we can uninstall it if things go # wrong. @installed_plugin_name = plugin_spec.name # Mark that we installed the gem @logger.info("Adding the plugin to the state file...") env[:plugin_state_file].add_plugin(plugin_spec.name) # Tell the user env[:ui].success(I18n.t("vagrant.commands.plugin.installed", :name => plugin_spec.name, :version => plugin_spec.version.to_s)) # If the plugin's spec includes a post-install message display it post_install_message = plugin_spec.post_install_message if post_install_message if post_install_message.is_a?(Array) post_install_message = post_install_message.join(" ") end env[:ui].info(I18n.t("vagrant.commands.plugin.post_install", :name => plugin_spec.name, :message => post_install_message.to_s)) end # Continue @app.call(env) end def recover(env) # If any error happens, we uninstall it and remove it from # the state file. We can only do this if we successfully installed # the gem in the first place. if @installed_plugin_name new_env = env.dup new_env.delete(:interrupted) new_env[:plugin_name] = @installed_plugin_name new_env[:action_runner].run(Action.action_uninstall, new_env) end end end end end end vagrant-1.4.3/plugins/commands/plugin/action/license_plugin.rb000066400000000000000000000034161226132634600245540ustar00rootroot00000000000000require "fileutils" require "pathname" require "rubygems" require "set" require "log4r" module VagrantPlugins module CommandPlugin module Action # This middleware licenses a plugin by copying the license file to # the proper place. class LicensePlugin def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::plugins::plugincommand::license") end def call(env) # Get the list of installed plugins according to the state file installed = env[:plugin_state_file].installed_plugins.keys # If the plugin we're trying to license doesn't exist in the # state file, then it is an error. if !installed.include?(env[:plugin_name]) raise Vagrant::Errors::PluginNotFound, :name => env[:plugin_name] end # Verify the license file exists license_file = Pathname.new(env[:plugin_license_path]) if !license_file.file? raise Vagrant::Errors::PluginInstallLicenseNotFound, :name => env[:plugin_name], :path => license_file.to_s end # Copy it in. final_path = env[:home_path].join("license-#{env[:plugin_name]}.lic") @logger.info("Copying license from: #{license_file}") @logger.info("Copying license to: #{final_path}") env[:ui].info(I18n.t("vagrant.commands.plugin.installing_license", :name => env[:plugin_name])) FileUtils.cp(license_file, final_path) # Installed! env[:ui].success(I18n.t("vagrant.commands.plugin.installed_license", :name => env[:plugin_name])) @app.call(env) end end end end end vagrant-1.4.3/plugins/commands/plugin/action/list_plugins.rb000066400000000000000000000033031226132634600242630ustar00rootroot00000000000000require "rubygems" require "set" module VagrantPlugins module CommandPlugin module Action # This middleware lists all the installed plugins. # # This is a bit more complicated than simply listing installed # gems or what is in the state file as installed. Instead, this # actually compares installed gems with what the state file claims # is installed, and outputs the appropriate truly installed # plugins. class ListPlugins def initialize(app, env) @app = app end def call(env) # Get the list of installed plugins according to the state file installed = env[:plugin_state_file].installed_plugins.keys # Go through the plugins installed in this environment and # get the latest version of each. installed_map = {} env[:gem_helper].with_environment do Gem::Specification.find_all.each do |spec| # Ignore specs that aren't in our installed list next if !installed.include?(spec.name) # If we already have a newer version in our list of installed, # then ignore it next if installed_map.has_key?(spec.name) && installed_map[spec.name].version >= spec.version installed_map[spec.name] = spec end end # Output! if installed_map.empty? env[:ui].info(I18n.t("vagrant.commands.plugin.no_plugins")) else installed_map.values.each do |spec| env[:ui].info "#{spec.name} (#{spec.version})" end end @app.call(env) end end end end end vagrant-1.4.3/plugins/commands/plugin/action/plugin_exists_check.rb000066400000000000000000000013011226132634600255750ustar00rootroot00000000000000require "set" module VagrantPlugins module CommandPlugin module Action # This class checks to see if the plugin is installed already, and # if so, raises an exception/error to output to the user. class PluginExistsCheck def initialize(app, env) @app = app end def call(env) # Get the list of installed plugins according to the state file installed = env[:plugin_state_file].installed_plugins.keys if !installed.include?(env[:plugin_name]) raise Vagrant::Errors::PluginNotInstalled, name: env[:plugin_name] end @app.call(env) end end end end end vagrant-1.4.3/plugins/commands/plugin/action/prune_gems.rb000066400000000000000000000131461226132634600237210ustar00rootroot00000000000000require "rubygems" require "rubygems/user_interaction" require "rubygems/uninstaller" require "set" require "log4r" module VagrantPlugins module CommandPlugin module Action # This class prunes any unnecessary gems from the Vagrant-managed # gem folder. This keeps the gem folder to the absolute minimum set # of required gems and doesn't let it blow up out of control. # # A high-level description of how this works: # # 1. Get the list of installed plugins. Vagrant maintains this # list on its own. # 2. Get the list of installed RubyGems. # 3. Find the latest version of each RubyGem that matches an installed # plugin. These are our root RubyGems that must be installed. # 4. Go through each root and mark all dependencies recursively as # necessary. # 5. Set subtraction between all gems and necessary gems yields a # list of gems that aren't needed. Uninstall them. # class PruneGems def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::plugins::plugincommand::prune") end def call(env) @logger.info("Pruning gems...") # Get the list of installed plugins according to the state file installed = env[:plugin_state_file].installed_plugins.keys # Get the actual specifications of installed gems all_specs = env[:gem_helper].with_environment do [].tap do |result| Gem::Specification.find_all do |s| # Ignore default gems since they can't be uninstalled next if s.respond_to?(:default_gem?) && s.default_gem? result << s end end end # The list of specs to prune initially starts out as all of them all_specs = Set.new(all_specs) # Go through each spec and find the latest version of the installed # gems, since we want to keep those. installed_specs = {} @logger.debug("Collecting installed plugin gems...") all_specs.each do |spec| # If this isn't a spec that we claim is installed, skip it next if !installed.include?(spec.name) # If it is already in the specs, then we need to make sure we # have the latest version. if installed_specs.has_key?(spec.name) if installed_specs[spec.name].version > spec.version next end end @logger.debug(" -- #{spec.name} (#{spec.version})") installed_specs[spec.name] = spec end # Recursive dependency checker to keep all dependencies and remove # all non-crucial gems from the prune list. good_specs = Set.new to_check = installed_specs.values while true # If we're out of gems to check then we break out break if to_check.empty? # Get a random (first) element to check spec = to_check.shift # If we already checked this, then do the next one next if good_specs.include?(spec) # Find all the dependencies and add the latest compliant gem # to the `to_check` list. if spec.dependencies.length > 0 @logger.debug("Finding dependencies for '#{spec.name}' to mark as good...") spec.dependencies.each do |dep| # Ignore non-runtime dependencies next if dep.type != :runtime @logger.debug("Searching for: '#{dep.name}'") latest_matching = nil all_specs.each do |prune_spec| if dep =~ prune_spec # If we have a matching one already and this one isn't newer # then we ditch it. next if latest_matching && prune_spec.version <= latest_matching.version latest_matching = prune_spec end end if latest_matching.nil? @logger.error("Missing dependency for '#{spec.name}': #{dep.name}") next end @logger.debug("Latest matching dep: '#{latest_matching.name}' (#{latest_matching.version})") to_check << latest_matching end end # Add ito the list of checked things so we don't accidentally # re-check it good_specs.add(spec) end # Figure out the gems we need to prune prune_specs = all_specs - good_specs @logger.debug("Gems to prune: #{prune_specs.inspect}") @logger.info("Pruning #{prune_specs.length} gems.") if prune_specs.length > 0 env[:gem_helper].with_environment do # Due to a bug in rubygems 2.0, we need to load the # specifications before removing any. This achieves that. Gem::Specification.to_a prune_specs.each do |prune_spec| uninstaller = Gem::Uninstaller.new(prune_spec.name, { :all => true, :executables => true, :force => true, :ignore => true, :version => prune_spec.version.version }) @logger.info("Uninstalling: #{prune_spec.name} (#{prune_spec.version})") uninstaller.uninstall end end end @app.call(env) end end end end end vagrant-1.4.3/plugins/commands/plugin/action/uninstall_plugin.rb000066400000000000000000000012041226132634600251340ustar00rootroot00000000000000module VagrantPlugins module CommandPlugin module Action # This middleware uninstalls a plugin by simply removing it from # the state file. Running a {PruneGems} after should properly remove # it from the gem index. class UninstallPlugin def initialize(app, env) @app = app end def call(env) # Remove it! env[:ui].info(I18n.t("vagrant.commands.plugin.uninstalling", :name => env[:plugin_name])) env[:plugin_state_file].remove_plugin(env[:plugin_name]) @app.call(env) end end end end end vagrant-1.4.3/plugins/commands/plugin/command/000077500000000000000000000000001226132634600213645ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/plugin/command/base.rb000066400000000000000000000013341226132634600226240ustar00rootroot00000000000000module VagrantPlugins module CommandPlugin module Command class Base < Vagrant.plugin("2", :command) # This is a helper for executing an action sequence with the proper # environment hash setup so that the plugin specific helpers are # in. # # @param [Object] callable the Middleware callable # @param [Hash] env Extra environment hash that is merged in. def action(callable, env=nil) env = { :gem_helper => GemHelper.new(@env.gems_path), :plugin_state_file => StateFile.new(@env.home_path.join("plugins.json")) }.merge(env || {}) @env.action_runner.run(callable, env) end end end end end vagrant-1.4.3/plugins/commands/plugin/command/install.rb000066400000000000000000000021031226132634600233530ustar00rootroot00000000000000require 'optparse' require_relative "base" require_relative "mixin_install_opts" module VagrantPlugins module CommandPlugin module Command class Install < Base include MixinInstallOpts def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin install [-h]" o.separator "" build_install_opts(o, options) end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 1 # Install the gem action(Action.action_install, { :plugin_entry_point => options[:entry_point], :plugin_prerelease => options[:plugin_prerelease], :plugin_version => options[:plugin_version], :plugin_sources => options[:plugin_sources], :plugin_name => argv[0] }) # Success, exit status 0 0 end end end end end vagrant-1.4.3/plugins/commands/plugin/command/license.rb000066400000000000000000000013511226132634600233330ustar00rootroot00000000000000require 'optparse' require_relative "base" module VagrantPlugins module CommandPlugin module Command class License < Base def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin license [-h]" end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 2 # License the plugin action(Action.action_license, { :plugin_license_path => argv[1], :plugin_name => argv[0] }) # Success, exit status 0 0 end end end end end vagrant-1.4.3/plugins/commands/plugin/command/list.rb000066400000000000000000000011521226132634600226630ustar00rootroot00000000000000require 'optparse' require_relative "base" module VagrantPlugins module CommandPlugin module Command class List < Base def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin list [-h]" end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length > 0 # List the installed plugins action(Action.action_list) # Success, exit status 0 0 end end end end end vagrant-1.4.3/plugins/commands/plugin/command/mixin_install_opts.rb000066400000000000000000000017711226132634600256360ustar00rootroot00000000000000module VagrantPlugins module CommandPlugin module Command module MixinInstallOpts def build_install_opts(o, options) o.on("--entry-point NAME", String, "The name of the entry point file for loading the plugin.") do |entry_point| options[:entry_point] = entry_point end o.on("--plugin-prerelease", "Allow prerelease versions of this plugin.") do |plugin_prerelease| options[:plugin_prerelease] = plugin_prerelease end o.on("--plugin-source PLUGIN_SOURCE", String, "Add a RubyGems repository source") do |plugin_source| options[:plugin_sources] ||= [] options[:plugin_sources] << plugin_source end o.on("--plugin-version PLUGIN_VERSION", String, "Install a specific version of the plugin") do |plugin_version| options[:plugin_version] = plugin_version end end end end end end vagrant-1.4.3/plugins/commands/plugin/command/root.rb000066400000000000000000000046071226132634600227030ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandPlugin module Command class Root < Vagrant.plugin("2", :command) def self.synopsis "manages plugins: install, uninstall, update, etc." end def initialize(argv, env) super @main_args, @sub_command, @sub_args = split_main_and_subcommand(argv) @subcommands = Vagrant::Registry.new @subcommands.register(:install) do require_relative "install" Install end @subcommands.register(:license) do require_relative "license" License end @subcommands.register(:list) do require_relative "list" List end @subcommands.register(:update) do require_relative "update" Update end @subcommands.register(:uninstall) do require_relative "uninstall" Uninstall end end def execute if @main_args.include?("-h") || @main_args.include?("--help") # Print the help for all the sub-commands. return help end # If we reached this far then we must have a subcommand. If not, # then we also just print the help and exit. command_class = @subcommands.get(@sub_command.to_sym) if @sub_command return help if !command_class || !@sub_command @logger.debug("Invoking command class: #{command_class} #{@sub_args.inspect}") # Initialize and execute the command class command_class.new(@sub_args, @env).execute end # Prints the help out for this command def help opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin []" o.separator "" o.separator "Available subcommands:" # Add the available subcommands as separators in order to print them # out as well. keys = [] @subcommands.each { |key, value| keys << key.to_s } keys.sort.each do |key| o.separator " #{key}" end o.separator "" o.separator "For help on any individual command run `vagrant plugin COMMAND -h`" end @env.ui.info(opts.help, :prefix => false) end end end end end vagrant-1.4.3/plugins/commands/plugin/command/uninstall.rb000066400000000000000000000012201226132634600237150ustar00rootroot00000000000000require 'optparse' require_relative "base" module VagrantPlugins module CommandPlugin module Command class Uninstall < Base def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin uninstall [-h]" end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 1 # Uninstall the gem action(Action.action_uninstall, :plugin_name => argv[0]) # Success, exit status 0 0 end end end end end vagrant-1.4.3/plugins/commands/plugin/command/update.rb000066400000000000000000000020771226132634600232010ustar00rootroot00000000000000require 'optparse' require_relative "base" require_relative "mixin_install_opts" module VagrantPlugins module CommandPlugin module Command class Update < Base include MixinInstallOpts def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant plugin update [-h]" o.separator "" build_install_opts(o, options) end # Parse the options argv = parse_options(opts) return if !argv raise Vagrant::Errors::CLIInvalidUsage, :help => opts.help.chomp if argv.length < 1 # Update the gem action(Action.action_update, { :plugin_entry_point => options[:entry_point], :plugin_prerelease => options[:plugin_prerelease], :plugin_version => options[:plugin_version], :plugin_sources => options[:plugin_sources], :plugin_name => argv[0] }) # Success, exit status 0 0 end end end end end vagrant-1.4.3/plugins/commands/plugin/gem_helper.rb000066400000000000000000000044631226132634600224110ustar00rootroot00000000000000require "rubygems" require "rubygems/config_file" require "rubygems/gem_runner" require "log4r" module VagrantPlugins module CommandPlugin # This class provides methods to help with calling out to the # `gem` command but using the RubyGems API. class GemHelper def initialize(gem_home) @gem_home = gem_home.to_s @logger = Log4r::Logger.new("vagrant::plugins::plugincommand::gemhelper") end # This will yield the given block with the proper ENV setup so # that RubyGems only sees the gems in the Vagrant-managed gem # path. def with_environment old_gem_home = ENV["GEM_HOME"] old_gem_path = ENV["GEM_PATH"] ENV["GEM_HOME"] = @gem_home ENV["GEM_PATH"] = @gem_home @logger.debug("Set GEM_* to: #{ENV["GEM_HOME"]}") # Clear paths so that it reads the new GEM_HOME setting Gem.paths = ENV # Set a custom configuration to avoid loading ~/.gemrc loads and # /etc/gemrc and so on. old_config = Gem.configuration Gem.configuration = NilGemConfig.new # Clear the sources so that installation uses custom sources old_sources = Gem.sources Gem.sources = Gem.default_sources Gem.sources << "http://gems.hashicorp.com" # Use a silent UI so that we have no output Gem::DefaultUserInteraction.use_ui(Gem::SilentUI.new) do return yield end ensure # Restore the old GEM_* settings ENV["GEM_HOME"] = old_gem_home ENV["GEM_PATH"] = old_gem_path # Reset everything Gem.configuration = old_config Gem.paths = ENV Gem.sources = old_sources.to_a end # This is pretty hacky but it is a custom implementatin of # Gem::ConfigFile so that we don't load any gemrc files. class NilGemConfig < Gem::ConfigFile def initialize # We _can not_ `super` here because that can really mess up # some other configuration state. We need to just set everything # directly. @api_keys = {} @args = [] @backtrace = false @bulk_threshold = 1000 @hash = {} @update_sources = true @verbose = true end end end end end vagrant-1.4.3/plugins/commands/plugin/plugin.rb000066400000000000000000000011211226132634600215640ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandPlugin class Plugin < Vagrant.plugin("2") name "plugin command" description <<-DESC This command helps manage and install plugins within the Vagrant environment. DESC command("plugin") do require File.expand_path("../command/root", __FILE__) Command::Root end end autoload :Action, File.expand_path("../action", __FILE__) autoload :GemHelper, File.expand_path("../gem_helper", __FILE__) autoload :StateFile, File.expand_path("../state_file", __FILE__) end end vagrant-1.4.3/plugins/commands/plugin/state_file.rb000066400000000000000000000041741226132634600224200ustar00rootroot00000000000000require "json" module VagrantPlugins module CommandPlugin # This is a helper to deal with the plugin state file that Vagrant # uses to track what plugins are installed and activated and such. class StateFile def initialize(path) @path = path @data = {} if @path.exist? begin @data = JSON.parse(@path.read) rescue JSON::ParserError => e raise Vagrant::Errors::PluginStateFileParseError, :path => path, :message => e.message end upgrade_v0! if !@data["version"] end @data["version"] ||= "1" @data["installed"] ||= {} end # Add a plugin that is installed to the state file. # # @param [String] name The name of the plugin def add_plugin(name) if !@data["installed"].has_key?(name) @data["installed"][name] = { "ruby_version" => RUBY_VERSION, "vagrant_version" => Vagrant::VERSION, } end save! end # This returns a hash of installed plugins according to the state # file. Note that this may _not_ directly match over to actually # installed gems. # # @return [Hash] def installed_plugins @data["installed"] end # Remove a plugin that is installed from the state file. # # @param [String] name The name of the plugin. def remove_plugin(name) @data["installed"].delete(name) save! end # This saves the state back into the state file. def save! @path.open("w+") do |f| f.write(JSON.dump(@data)) end end protected # This upgrades the internal data representation from V0 (the initial # version) to V1. def upgrade_v0! @data["version"] = "1" new_installed = {} (@data["installed"] || []).each do |plugin| new_installed[plugin] = { "ruby_version" => "0", "vagrant_version" => "0", } end @data["installed"] = new_installed save! end end end end vagrant-1.4.3/plugins/commands/provision/000077500000000000000000000000001226132634600205005ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/provision/command.rb000066400000000000000000000022031226132634600224400ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandProvision class Command < Vagrant.plugin("2", :command) def self.synopsis "provisions the vagrant machine" end def execute options = {} options[:provision_types] = nil opts = OptionParser.new do |o| o.banner = "Usage: vagrant provision [vm-name] [--provision-with x,y,z]" o.on("--provision-with x,y,z", Array, "Enable only certain provisioners, by type.") do |list| options[:provision_types] = list.map { |type| type.to_sym } end o.on("--[no-]parallel", "Enable or disable parallelism if provider supports it.") do |parallel| options[:parallel] = parallel end end # Parse the options argv = parse_options(opts) return if !argv # Go over each VM and provision! @logger.debug("'provision' each target VM...") with_target_vms(argv) do |machine| machine.action(:provision, options) end # Success, exit status 0 0 end end end end vagrant-1.4.3/plugins/commands/provision/plugin.rb000066400000000000000000000006451226132634600223300ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandProvision class Plugin < Vagrant.plugin("2") name "provision command" description <<-DESC The `provision` command provisions your virtual machine based on the configuration of the Vagrantfile. DESC command("provision") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/reload/000077500000000000000000000000001226132634600177165ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/reload/command.rb000066400000000000000000000021311226132634600216560ustar00rootroot00000000000000require 'optparse' require "vagrant" require Vagrant.source_root.join("plugins/commands/up/start_mixins") module VagrantPlugins module CommandReload class Command < Vagrant.plugin("2", :command) # We assume that the `up` plugin exists and that we'll have access # to this. include VagrantPlugins::CommandUp::StartMixins def self.synopsis "restarts vagrant machine, loads new Vagrantfile configuration" end def execute options = {} options[:provision_ignore_sentinel] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant reload [vm-name]" o.separator "" build_start_options(o, options) end # Parse the options argv = parse_options(opts) return if !argv # Validate the provisioners validate_provisioner_flags!(options) @logger.debug("'reload' each target VM...") with_target_vms(argv) do |machine| machine.action(:reload, options) end # Success, exit status 0 0 end end end end vagrant-1.4.3/plugins/commands/reload/plugin.rb000066400000000000000000000006361226132634600215460ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandReload class Plugin < Vagrant.plugin("2") name "reload command" description <<-DESC The `reload` command will halt, reconfigure your machine based on the Vagrantfile, and bring it back up. DESC command("reload") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/resume/000077500000000000000000000000001226132634600177505ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/resume/command.rb000066400000000000000000000011551226132634600217150ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandResume class Command < Vagrant.plugin("2", :command) def self.synopsis "resume a suspended vagrant machine" end def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant resume [vm-name]" end # Parse the options argv = parse_options(opts) return if !argv @logger.debug("'resume' each target VM...") with_target_vms(argv) do |machine| machine.action(:resume) end # Success, exit status 0 0 end end end end vagrant-1.4.3/plugins/commands/resume/plugin.rb000066400000000000000000000005471226132634600216010ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandResume class Plugin < Vagrant.plugin("2") name "resume command" description <<-DESC The `resume` command resumes a suspend virtual machine. DESC command("resume") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/ssh/000077500000000000000000000000001226132634600172455ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/ssh/command.rb000066400000000000000000000041571226132634600212170ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandSSH class Command < Vagrant.plugin("2", :command) def self.synopsis "connects to machine via SSH" end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant ssh [vm-name] [-c command] [-- extra ssh args]" o.separator "" o.on("-c", "--command COMMAND", "Execute an SSH command directly.") do |c| options[:command] = c end o.on("-p", "--plain", "Plain mode, leaves authentication up to user.") do |p| options[:plain_mode] = p end end # Parse out the extra args to send to SSH, which is everything # after the "--" split_index = @argv.index("--") if split_index options[:ssh_args] = @argv.drop(split_index + 1) @argv = @argv.take(split_index) end # Parse the options and return if we don't have any target. argv = parse_options(opts) return if !argv # Execute the actual SSH with_target_vms(argv, :single_target => true) do |vm| ssh_opts = { :plain_mode => options[:plain_mode], :extra_args => options[:ssh_args] } if options[:command] @logger.debug("Executing single command on remote machine: #{options[:command]}") env = vm.action(:ssh_run, ssh_opts: ssh_opts, ssh_run_command: options[:command],) # Exit with the exit status of the command or a 0 if we didn't # get one. exit_status = env[:ssh_run_exit_status] || 0 return exit_status else @logger.debug("Invoking `ssh` action on machine") vm.action(:ssh, :ssh_opts => ssh_opts) # We should never reach this point, since the point of `ssh` # is to exec into the proper SSH shell, but we'll just return # an exit status of 0 anyways. return 0 end end end end end end vagrant-1.4.3/plugins/commands/ssh/plugin.rb000066400000000000000000000005561226132634600210760ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandSSH class Plugin < Vagrant.plugin("2") name "ssh command" description <<-DESC The `ssh` command allows you to SSH in to your running virtual machine. DESC command("ssh") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/ssh_config/000077500000000000000000000000001226132634600205725ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/ssh_config/command.rb000066400000000000000000000027611226132634600225430ustar00rootroot00000000000000require 'optparse' require "vagrant/util/safe_puts" module VagrantPlugins module CommandSSHConfig class Command < Vagrant.plugin("2", :command) include Vagrant::Util::SafePuts def self.synopsis "outputs OpenSSH valid configuration to connect to the machine" end def execute options = {} opts = OptionParser.new do |o| o.banner = "Usage: vagrant ssh-config [vm-name] [--host name]" o.separator "" o.on("--host COMMAND", "Name the host for the config..") do |h| options[:host] = h end end argv = parse_options(opts) return if !argv with_target_vms(argv, :single_target => true) do |machine| ssh_info = machine.ssh_info raise Vagrant::Errors::SSHNotReady if ssh_info.nil? variables = { :host_key => options[:host] || machine.name || "vagrant", :ssh_host => ssh_info[:host], :ssh_port => ssh_info[:port], :ssh_user => ssh_info[:username], :private_key_path => ssh_info[:private_key_path], :forward_agent => ssh_info[:forward_agent], :forward_x11 => ssh_info[:forward_x11] } # Render the template and output directly to STDOUT template = "commands/ssh_config/config" safe_puts(Vagrant::Util::TemplateRenderer.render(template, variables)) end # Success, exit status 0 0 end end end end vagrant-1.4.3/plugins/commands/ssh_config/plugin.rb000066400000000000000000000006761226132634600224260ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandSSHConfig class Plugin < Vagrant.plugin("2") name "ssh-config command" description <<-DESC The `ssh-config` command dumps an OpenSSH compatible configuration that can be used to quickly SSH into your virtual machine. DESC command("ssh-config") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/status/000077500000000000000000000000001226132634600177735ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/status/command.rb000066400000000000000000000033011226132634600217330ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandStatus class Command < Vagrant.plugin("2", :command) def self.synopsis "outputs status of the vagrant machine" end def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant status [machine-name]" end # Parse the options argv = parse_options(opts) return if !argv max_name_length = 25 with_target_vms(argv) do |machine| max_name_length = machine.name.length if machine.name.length > max_name_length end state = nil results = [] with_target_vms(argv) do |machine| state = machine.state if !state current_state = machine.state results << "#{machine.name.to_s.ljust(max_name_length)} " + "#{current_state.short_description} (#{machine.provider_name})" opts = { scope: machine.name.to_s } @env.ui.machine("provider-name", machine.provider_name, opts) @env.ui.machine("state", current_state.id, opts) @env.ui.machine("state-human-short", current_state.short_description, opts) @env.ui.machine("state-human-long", current_state.long_description, opts) end message = nil if results.length == 1 message = state.long_description else message = I18n.t("vagrant.commands.status.listing") end @env.ui.info(I18n.t("vagrant.commands.status.output", :states => results.join("\n"), :message => message), :prefix => false) # Success, exit status 0 0 end end end end vagrant-1.4.3/plugins/commands/status/plugin.rb000066400000000000000000000006571226132634600216260ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandStatus class Plugin < Vagrant.plugin("2") name "status command" description <<-DESC The `status` command shows what the running state (running/saved/..) is of all your virtual machines in this environment. DESC command("status") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/suspend/000077500000000000000000000000001226132634600201315ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/suspend/command.rb000066400000000000000000000011311226132634600220700ustar00rootroot00000000000000require 'optparse' module VagrantPlugins module CommandSuspend class Command < Vagrant.plugin("2", :command) def self.synopsis "suspends the machine" end def execute opts = OptionParser.new do |o| o.banner = "Usage: vagrant suspend [vm-name]" end # Parse the options argv = parse_options(opts) return if !argv @logger.debug("'suspend' each target VM...") with_target_vms(argv) do |vm| vm.action(:suspend) end # Success, exit status 0 0 end end end end vagrant-1.4.3/plugins/commands/suspend/plugin.rb000066400000000000000000000006521226132634600217570ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandSuspend class Plugin < Vagrant.plugin("2") name "suspend command" description <<-DESC The `suspend` command suspends execution and puts it to sleep. The command `resume` returns it to running status. DESC command("suspend") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/up/000077500000000000000000000000001226132634600170745ustar00rootroot00000000000000vagrant-1.4.3/plugins/commands/up/command.rb000066400000000000000000000036531226132634600210460ustar00rootroot00000000000000require 'optparse' require "vagrant" require File.expand_path("../start_mixins", __FILE__) module VagrantPlugins module CommandUp class Command < Vagrant.plugin("2", :command) include StartMixins def self.synopsis "starts and provisions the vagrant environment" end def execute options = {} options[:destroy_on_error] = true options[:parallel] = true options[:provision_ignore_sentinel] = false opts = OptionParser.new do |o| o.banner = "Usage: vagrant up [vm-name] [options] [-h]" o.separator "" build_start_options(o, options) o.on("--[no-]destroy-on-error", "Destroy machine if any fatal error happens (default to true).") do |destroy| options[:destroy_on_error] = destroy end o.on("--[no-]parallel", "Enable or disable parallelism if provider supports it.") do |parallel| options[:parallel] = parallel end o.on("--provider provider", String, "Back the machine with a specific provider.") do |provider| options[:provider] = provider end end # Parse the options argv = parse_options(opts) return if !argv # Validate the provisioners validate_provisioner_flags!(options) # Go over each VM and bring it up @logger.debug("'Up' each target VM...") # Build up the batch job of what we'll do @env.batch(options[:parallel]) do |batch| with_target_vms(argv, :provider => options[:provider]) do |machine| @env.ui.info(I18n.t( "vagrant.commands.up.upping", :name => machine.name, :provider => machine.provider_name)) batch.action(machine, :up, options) end end # Success, exit status 0 0 end end end end vagrant-1.4.3/plugins/commands/up/plugin.rb000066400000000000000000000005431226132634600207210ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommandUp class Plugin < Vagrant.plugin("2") name "up command" description <<-DESC The `up` command brings the virtual environment up and running. DESC command("up") do require File.expand_path("../command", __FILE__) Command end end end end vagrant-1.4.3/plugins/commands/up/start_mixins.rb000066400000000000000000000023031226132634600221430ustar00rootroot00000000000000module VagrantPlugins module CommandUp module StartMixins # This adds the standard `start` command line flags to the given # OptionParser, storing the result in the `options` dictionary. # # @param [OptionParser] parser # @param [Hash] options def build_start_options(parser, options) # Setup the defaults options[:provision_types] = nil # Add the options parser.on("--[no-]provision", "Enable or disable provisioning") do |p| options[:provision_enabled] = p end parser.on("--provision-with x,y,z", Array, "Enable only certain provisioners, by type.") do |list| options[:provision_types] = list.map { |type| type.to_sym } end end # This validates the provisioner flags and raises an exception # if there are invalid ones. def validate_provisioner_flags!(options) (options[:provision_types] || []).each do |type| klass = Vagrant.plugin("2").manager.provisioners[type] if !klass raise Vagrant::Errors::ProvisionerFlagInvalid, name: type.to_s end end end end end end vagrant-1.4.3/plugins/communicators/000077500000000000000000000000001226132634600175325ustar00rootroot00000000000000vagrant-1.4.3/plugins/communicators/ssh/000077500000000000000000000000001226132634600203275ustar00rootroot00000000000000vagrant-1.4.3/plugins/communicators/ssh/communicator.rb000066400000000000000000000347711226132634600233700ustar00rootroot00000000000000require 'logger' require 'pathname' require 'stringio' require 'timeout' require 'log4r' require 'net/ssh' require 'net/ssh/proxy/command' require 'net/scp' require 'vagrant/util/ansi_escape_code_remover' require 'vagrant/util/file_mode' require 'vagrant/util/platform' require 'vagrant/util/retryable' require 'vagrant/util/ssh' module VagrantPlugins module CommunicatorSSH # This class provides communication with the VM via SSH. class Communicator < Vagrant.plugin("2", :communicator) include Vagrant::Util::ANSIEscapeCodeRemover include Vagrant::Util::Retryable def self.match?(machine) # All machines are currently expected to have SSH. true end def initialize(machine) @machine = machine @logger = Log4r::Logger.new("vagrant::communication::ssh") @connection = nil end def ready? @logger.debug("Checking whether SSH is ready...") # Attempt to connect. This will raise an exception if it fails. connect # If we reached this point then we successfully connected @logger.info("SSH is ready!") true rescue Vagrant::Errors::VagrantError => e # We catch a `VagrantError` which would signal that something went # wrong expectedly in the `connect`, which means we didn't connect. @logger.info("SSH not up: #{e.inspect}") return false end def execute(command, opts=nil, &block) opts = { :error_check => true, :error_class => Vagrant::Errors::VagrantError, :error_key => :ssh_bad_exit_status, :command => command, :shell => nil, :sudo => false }.merge(opts || {}) # Connect via SSH and execute the command in the shell. stdout = "" stderr = "" exit_status = connect do |connection| shell_execute(connection, command, opts[:sudo], opts[:shell]) do |type, data| if type == :stdout stdout += data elsif type == :stderr stderr += data end block.call(type, data) if block end end # Check for any errors if opts[:error_check] && exit_status != 0 # The error classes expect the translation key to be _key, # but that makes for an ugly configuration parameter, so we # set it here from `error_key` error_opts = opts.merge( :_key => opts[:error_key], :stdout => stdout, :stderr => stderr ) raise opts[:error_class], error_opts end # Return the exit status exit_status end def sudo(command, opts=nil, &block) # Run `execute` but with the `sudo` option. opts = { :sudo => true }.merge(opts || {}) execute(command, opts, &block) end def download(from, to=nil) @logger.debug("Downloading: #{from} to #{to}") scp_connect do |scp| scp.download!(from, to) end end def test(command, opts=nil) opts = { :error_check => false }.merge(opts || {}) execute(command, opts) == 0 end def upload(from, to) @logger.debug("Uploading: #{from} to #{to}") scp_connect do |scp| if File.directory?(from) # Recurisvely upload directories scp.upload!(from, to, :recursive => true) else # Open file read only to fix issue [GH-1036] scp.upload!(File.open(from, "r"), to) end end rescue RuntimeError => e # Net::SCP raises a runtime error for this so the only way we have # to really catch this exception is to check the message to see if # it is something we care about. If it isn't, we re-raise. raise if e.message !~ /Permission denied/ # Otherwise, it is a permission denied, so let's raise a proper # exception raise Vagrant::Errors::SCPPermissionDenied, :path => from.to_s end protected # Opens an SSH connection and yields it to a block. def connect if @connection && !@connection.closed? # There is a chance that the socket is closed despite us checking # 'closed?' above. To test this we need to send data through the # socket. begin @connection.exec!("") rescue Exception => e @logger.info("Connection errored, not re-using. Will reconnect.") @logger.debug(e.inspect) @connection = nil end # If the @connection is still around, then it is valid, # and we use it. if @connection @logger.debug("Re-using SSH connection.") return yield @connection if block_given? return end end # Get the SSH info for the machine, raise an exception if the # provider is saying that SSH is not ready. ssh_info = @machine.ssh_info raise Vagrant::Errors::SSHNotReady if ssh_info.nil? # Build the options we'll use to initiate the connection via Net::SSH opts = { :auth_methods => ["none", "publickey", "hostbased", "password"], :config => false, :forward_agent => ssh_info[:forward_agent], :keys => ssh_info[:private_key_path], :keys_only => true, :paranoid => false, :port => ssh_info[:port], :user_known_hosts_file => [] } # Check that the private key permissions are valid ssh_info[:private_key_path].each do |path| Vagrant::Util::SSH.check_key_permissions(Pathname.new(path)) end # Connect to SSH, giving it a few tries connection = nil begin # These are the exceptions that we retry because they represent # errors that are generally fixed from a retry and don't # necessarily represent immediate failure cases. exceptions = [ Errno::EACCES, Errno::EADDRINUSE, Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ENETUNREACH, Errno::EHOSTUNREACH, Net::SSH::Disconnect, Timeout::Error ] retries = 5 timeout = 60 @logger.info("Attempting SSH connnection...") connection = retryable(:tries => retries, :on => exceptions) do Timeout.timeout(timeout) do begin # This logger will get the Net-SSH log data for us. ssh_logger_io = StringIO.new ssh_logger = Logger.new(ssh_logger_io) # Setup logging for connections connect_opts = opts.merge({ :logger => ssh_logger, :timeout => 15, :verbose => :debug }) if ssh_info[:proxy_command] connect_opts[:proxy] = Net::SSH::Proxy::Command.new(ssh_info[:proxy_command]) end @logger.info("Attempting to connect to SSH...") @logger.info(" - Host: #{ssh_info[:host]}") @logger.info(" - Port: #{ssh_info[:port]}") @logger.info(" - Username: #{ssh_info[:username]}") @logger.info(" - Key Path: #{ssh_info[:private_key_path]}") Net::SSH.start(ssh_info[:host], ssh_info[:username], connect_opts) ensure # Make sure we output the connection log @logger.debug("== Net-SSH connection debug-level log START ==") @logger.debug(ssh_logger_io.string) @logger.debug("== Net-SSH connection debug-level log END ==") end end end rescue Errno::EACCES # This happens on connect() for unknown reasons yet... raise Vagrant::Errors::SSHConnectEACCES rescue Errno::ETIMEDOUT, Timeout::Error # This happens if we continued to timeout when attempting to connect. raise Vagrant::Errors::SSHConnectionTimeout rescue Net::SSH::AuthenticationFailed # This happens if authentication failed. We wrap the error in our # own exception. raise Vagrant::Errors::SSHAuthenticationFailed rescue Net::SSH::Disconnect # This happens if the remote server unexpectedly closes the # connection. This is usually raised when SSH is running on the # other side but can't properly setup a connection. This is # usually a server-side issue. raise Vagrant::Errors::SSHDisconnected rescue Errno::ECONNREFUSED # This is raised if we failed to connect the max amount of times raise Vagrant::Errors::SSHConnectionRefused rescue Errno::ECONNRESET # This is raised if we failed to connect the max number of times # due to an ECONNRESET. raise Vagrant::Errors::SSHConnectionReset rescue Errno::EHOSTDOWN # This is raised if we get an ICMP DestinationUnknown error. raise Vagrant::Errors::SSHHostDown rescue Errno::EHOSTUNREACH # This is raised if we can't work out how to route traffic. raise Vagrant::Errors::SSHNoRoute rescue NotImplementedError # This is raised if a private key type that Net-SSH doesn't support # is used. Show a nicer error. raise Vagrant::Errors::SSHKeyTypeNotSupported end @connection = connection # Yield the connection that is ready to be used and # return the value of the block return yield connection if block_given? end # Executes the command on an SSH connection within a login shell. def shell_execute(connection, command, sudo=false, shell=nil) @logger.info("Execute: #{command} (sudo=#{sudo.inspect})") exit_status = nil # Determine the shell to execute. Prefer the explicitly passed in shell # over the default configured shell. If we are using `sudo` then we # need to wrap the shell in a `sudo` call. shell_cmd = @machine.config.ssh.shell shell_cmd = shell if shell shell_cmd = "sudo -E -H #{shell_cmd}" if sudo # Open the channel so we can execute or command channel = connection.open_channel do |ch| if @machine.config.ssh.pty ch.request_pty do |ch2, success| if success @logger.debug("pty obtained for connection") else @logger.warn("failed to obtain pty, will try to continue anyways") end end end ch.exec(shell_cmd) do |ch2, _| # Setup the channel callbacks so we can get data and exit status ch2.on_data do |ch3, data| # Filter out the clear screen command data = remove_ansi_escape_codes(data) @logger.debug("stdout: #{data}") yield :stdout, data if block_given? end ch2.on_extended_data do |ch3, type, data| # Filter out the clear screen command data = remove_ansi_escape_codes(data) @logger.debug("stderr: #{data}") yield :stderr, data if block_given? end ch2.on_request("exit-status") do |ch3, data| exit_status = data.read_long @logger.debug("Exit status: #{exit_status}") # Close the channel, since after the exit status we're # probably done. This fixes up issues with hanging. channel.close end # Set the terminal ch2.send_data "export TERM=vt100\n" # Set SSH_AUTH_SOCK if we are in sudo and forwarding agent. # This is to work around often misconfigured boxes where # the SSH_AUTH_SOCK env var is not preserved. if @machine.ssh_info[:forward_agent] && sudo auth_socket = "" execute("echo; printf $SSH_AUTH_SOCK") do |type, data| if type == :stdout auth_socket += data end end if auth_socket != "" # Make sure we only read the last line which should be # the $SSH_AUTH_SOCK env var we printed. auth_socket = auth_socket.split("\n").last.chomp end if auth_socket == "" @logger.warn("No SSH_AUTH_SOCK found despite forward_agent being set.") else @logger.info("Setting SSH_AUTH_SOCK remotely: #{auth_socket}") ch2.send_data "export SSH_AUTH_SOCK=#{auth_socket}\n" end end # Output the command ch2.send_data "#{command}\n" # Remember to exit or this channel will hang open ch2.send_data "exit\n" # Send eof to let server know we're done ch2.eof! end end begin keep_alive = nil if @machine.config.ssh.keep_alive # Begin sending keep-alive packets while we wait for the script # to complete. This avoids connections closing on long-running # scripts. keep_alive = Thread.new do loop do sleep 5 @logger.debug("Sending SSH keep-alive...") connection.send_global_request("keep-alive@openssh.com") end end end # Wait for the channel to complete begin channel.wait rescue IOError @logger.info("SSH connection unexpected closed. Assuming reboot or something.") exit_status = 0 end ensure # Kill the keep-alive thread keep_alive.kill if keep_alive end # Return the final exit status return exit_status end # Opens an SCP connection and yields it so that you can download # and upload files. def scp_connect # Connect to SCP and yield the SCP object connect do |connection| scp = Net::SCP.new(connection) return yield scp end rescue Net::SCP::Error => e # If we get the exit code of 127, then this means SCP is unavailable. raise Vagrant::Errors::SCPUnavailable if e.message =~ /\(127\)/ # Otherwise, just raise the error up raise end end end end vagrant-1.4.3/plugins/communicators/ssh/plugin.rb000066400000000000000000000007361226132634600221600ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CommunicatorSSH class Plugin < Vagrant.plugin("2") name "ssh communicator" description <<-DESC This plugin allows Vagrant to communicate with remote machines using SSH as the underlying protocol, powered internally by Ruby's net-ssh library. DESC communicator("ssh") do require File.expand_path("../communicator", __FILE__) Communicator end end end end vagrant-1.4.3/plugins/guests/000077500000000000000000000000001226132634600161615ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/arch/000077500000000000000000000000001226132634600170765ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/arch/cap/000077500000000000000000000000001226132634600176415ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/arch/cap/change_host_name.rb000066400000000000000000000010171226132634600234470ustar00rootroot00000000000000module VagrantPlugins module GuestArch module Cap class ChangeHostName def self.change_host_name(machine, name) machine.communicate.tap do |comm| # Only do this if the hostname is not already set if !comm.test("sudo hostname | grep '#{name}'") comm.sudo("hostnamectl set-hostname #{name}") comm.sudo("sed -i 's@^\\(127[.]0[.]0[.]1[[:space:]]\\+\\)@\\1#{name} @' /etc/hosts") end end end end end end end vagrant-1.4.3/plugins/guests/arch/cap/configure_networks.rb000066400000000000000000000040611226132634600241040ustar00rootroot00000000000000# -*- coding: utf-8 -*- require "tempfile" require "vagrant/util/template_renderer" module VagrantPlugins module GuestArch module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) networks.each do |network| entry = TemplateRenderer.render("guests/arch/network_#{network[:type]}", :options => network) temp = Tempfile.new("vagrant") temp.binmode temp.write(entry) temp.close machine.communicate.upload(temp.path, "/tmp/vagrant_network") machine.communicate.sudo("ln -sf /dev/null /etc/udev/rules.d/80-net-name-slot.rules") machine.communicate.sudo("mv /tmp/vagrant_network /etc/netctl/eth#{network[:interface]}") # Only consider nth line of sed's output below. There's always an # offset of two lines in the below sed command given the current # interface number -> 1: lo, 2: nat device, snth = network[:interface] + 2 # A hack not to rely on udev rule 80-net-name-slot.rules masking # (ln -sf /dev/null /etc/udev/80-net-name-slot.rules). # I assume this to be the most portable solution because # otherwise we would need to rely on the Virtual Machine implementation # to provide details on the configured interfaces, e.g mac address # to write a custom udev rule. Templating the netcfg files and # replacing the correct interface name within ruby seems more # complicted too (I'm far from being a ruby expert though). machine.communicate.sudo("sed -i \"s/eth#{network[:interface]}/`ip link | sed -n 's/.*:\\s\\(.*\\): <.*/\\1/p' | sed -n #{snth}p`/g\" /etc/netctl/eth#{network[:interface]}") machine.communicate.sudo("ip link set eth#{network[:interface]} down") machine.communicate.sudo("netctl start eth#{network[:interface]}") end end end end end end vagrant-1.4.3/plugins/guests/arch/guest.rb000066400000000000000000000003331226132634600205510ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestArch class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/arch-release") end end end end vagrant-1.4.3/plugins/guests/arch/plugin.rb000066400000000000000000000011061226132634600207170ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestArch class Plugin < Vagrant.plugin("2") name "Arch guest" description "Arch guest support." guest("arch", "linux") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("arch", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("arch", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end end end end vagrant-1.4.3/plugins/guests/coreos/000077500000000000000000000000001226132634600174535ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/coreos/cap/000077500000000000000000000000001226132634600202165ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/coreos/cap/change_host_name.rb000066400000000000000000000005651226132634600240330ustar00rootroot00000000000000module VagrantPlugins module GuestCoreOS module Cap class ChangeHostName def self.change_host_name(machine, name) machine.communicate.tap do |comm| if !comm.test("sudo hostname --fqdn | grep '#{name}'") comm.sudo("hostname #{name.split('.')[0]}") end end end end end end end vagrant-1.4.3/plugins/guests/coreos/cap/configure_networks.rb000066400000000000000000000044121226132634600244610ustar00rootroot00000000000000require "tempfile" require "vagrant/util/template_renderer" module VagrantPlugins module GuestCoreOS module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) machine.communicate.tap do |comm| # Disable default etcd comm.sudo("systemctl stop etcd") # Read network interface names interfaces = [] comm.sudo("ifconfig | grep enp0 | cut -f1 -d:") do |_, result| interfaces = result.split("\n") end # Configure interfaces # FIXME: fix matching of interfaces with IP adresses networks.each do |network| comm.sudo("ifconfig #{interfaces[network[:interface].to_i]} #{network[:ip]} netmask #{network[:netmask]}") end primary_machine_config = machine.env.active_machines.first primary_machine = machine.env.machine(*primary_machine_config, true) get_ip = lambda do |machine| ip = nil machine.config.vm.networks.each do |type, opts| if type == :private_network && opts[:ip] ip = opts[:ip] break end end ip end primary_machine_ip = get_ip.(primary_machine) current_ip = get_ip.(machine) if current_ip == primary_machine_ip entry = TemplateRenderer.render("guests/coreos/etcd.service", :options => { :my_ip => current_ip }) else connection_string = "#{primary_machine_ip}:7001" entry = TemplateRenderer.render("guests/coreos/etcd.service", :options => { :connection_string => connection_string, :my_ip => current_ip }) end Tempfile.open("vagrant") do |temp| temp.binmode temp.write(entry) temp.close comm.upload(temp.path, "/tmp/etcd-cluster.service") end comm.sudo("mv /tmp/etcd-cluster.service /media/state/units/") comm.sudo("systemctl restart local-enable.service") end end end end end end vagrant-1.4.3/plugins/guests/coreos/guest.rb000066400000000000000000000003321226132634600211250ustar00rootroot00000000000000module VagrantPlugins module GuestCoreOS class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/gentoo-release | grep CoreOS") end end end end vagrant-1.4.3/plugins/guests/coreos/plugin.rb000066400000000000000000000011221226132634600212720ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestCoreOS class Plugin < Vagrant.plugin("2") name "CoreOS guest" description "CoreOS guest support." guest("coreos", "linux") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("coreos", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("coreos", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end end end end vagrant-1.4.3/plugins/guests/darwin/000077500000000000000000000000001226132634600174455ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/darwin/cap/000077500000000000000000000000001226132634600202105ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/darwin/cap/change_host_name.rb000066400000000000000000000006461226132634600240250ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class ChangeHostName def self.change_host_name(machine, name) if !machine.communicate.test("hostname -f | grep '^#{name}$' || hostname -s | grep '^#{name}$'") machine.communicate.sudo("scutil --set HostName #{name}") machine.communicate.sudo("hostname #{name}") end end end end end end vagrant-1.4.3/plugins/guests/darwin/cap/configure_networks.rb000066400000000000000000000035251226132634600244570ustar00rootroot00000000000000require "tempfile" require "vagrant/util/template_renderer" module VagrantPlugins module GuestDarwin module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) # Slightly different than other plugins, using the template to build commands # rather than templating the files. machine.communicate.sudo("networksetup -detectnewhardware") machine.communicate.sudo("networksetup -listnetworkserviceorder > /tmp/vagrant.interfaces") tmpints = File.join(Dir.tmpdir, File.basename("#{machine.id}.interfaces")) machine.communicate.download("/tmp/vagrant.interfaces",tmpints) devlist = [] ints = IO.read(tmpints) ints.split(/\n\n/m).each do |i| if i.match(/Hardware/) and not i.match(/Ethernet/).nil? devmap = {} # Ethernet, should be 2 lines, # (3) Thunderbolt Ethernet # (Hardware Port: Thunderbolt Ethernet, Device: en1) # multiline, should match "Thunderbolt Ethernet", "en1" devicearry = i.match(/\([0-9]+\) (.+)\n.*Device: (.+)\)/m) devmap[:interface] = devicearry[2] devmap[:service] = devicearry[1] devlist << devmap end end File.delete(tmpints) networks.each do |network| service_name = devlist[network[:interface]][:service] if network[:type].to_sym == :static command = "networksetup -setmanual \"#{service_name}\" #{network[:ip]} #{network[:netmask]}" elsif network[:type].to_sym == :dhcp command = "networksetup -setdhcp \"#{service_name}\"" end machine.communicate.sudo(command) end end end end end end vagrant-1.4.3/plugins/guests/darwin/cap/halt.rb000066400000000000000000000005771226132634600214760ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class Halt def self.halt(machine) begin machine.communicate.sudo("shutdown -h now") rescue IOError # Do nothing because SSH connection closed and it probably # means the VM just shut down really fast. end end end end end end vagrant-1.4.3/plugins/guests/darwin/cap/mount_nfs_folder.rb000066400000000000000000000016671226132634600241120ustar00rootroot00000000000000require "vagrant/util/retryable" module VagrantPlugins module GuestDarwin module Cap class MountNFSFolder extend Vagrant::Util::Retryable def self.mount_nfs_folder(machine, ip, folders) folders.each do |name, opts| # Expand the guest path so we can handle things like "~/vagrant" expanded_guest_path = machine.guest.capability( :shell_expand_guest_path, opts[:guestpath]) machine.communicate.sudo("if [ ! -d #{expanded_guest_path} ]; then mkdir -p #{expanded_guest_path};fi") mount_command = "mount -t nfs '#{ip}:#{opts[:hostpath]}' '#{expanded_guest_path}'" retryable(:on => Vagrant::Errors::DarwinNFSMountFailed, :tries => 10, :sleep => 5) do machine.communicate.sudo(mount_command, :error_class => Vagrant::Errors::DarwinNFSMountFailed) end end end end end end end vagrant-1.4.3/plugins/guests/darwin/cap/mount_vmware_shared_folder.rb000066400000000000000000000023741226132634600261470ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class MountVmwareSharedFolder # we seem to be unable to ask 'mount -t vmhgfs' to mount the roots # of specific shares, so instead we symlink from what is already # mounted by the guest tools # (ie. the behaviour of the VMware_fusion provider prior to 0.8.x) def self.mount_vmware_shared_folder(machine, name, guestpath, options) machine.communicate.tap do |comm| # clear prior symlink if comm.test("test -L \"#{guestpath}\"", :sudo => true) comm.sudo("rm \"#{guestpath}\"") end # clear prior directory if exists if comm.test("test -d \"#{guestpath}\"", :sudo => true) comm.sudo("rm -Rf \"#{guestpath}\"") end # create intermediate directories if needed intermediate_dir = File.dirname(guestpath) if !comm.test("test -d \"#{intermediate_dir}\"", :sudo => true) comm.sudo("mkdir -p \"#{intermediate_dir}\"") end # finally make the symlink comm.sudo("ln -s \"/Volumes/VMware Shared Folders/#{name}\" \"#{guestpath}\"") end end end end end end vagrant-1.4.3/plugins/guests/darwin/cap/shell_expand_guest_path.rb000066400000000000000000000013321226132634600254250ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class ShellExpandGuestPath def self.shell_expand_guest_path(machine, path) real_path = nil machine.communicate.execute("printf #{path}") do |type, data| if type == :stdout real_path ||= "" real_path += data end end if !real_path # If no real guest path was detected, this is really strange # and we raise an exception because this is a bug. raise DarwinShellExpandFailed end # Chomp the string so that any trailing newlines are killed return real_path.chomp end end end end end vagrant-1.4.3/plugins/guests/darwin/cap/verify_vmware_hgfs.rb000066400000000000000000000005001226132634600244240ustar00rootroot00000000000000module VagrantPlugins module GuestDarwin module Cap class VerifyVmwareHgfs def self.verify_vmware_hgfs(machine) kext_bundle_id = "com.vmware.kext.vmhgfs" machine.communicate.test("kextstat -b #{kext_bundle_id} -l | grep #{kext_bundle_id}") end end end end end vagrant-1.4.3/plugins/guests/darwin/guest.rb000066400000000000000000000006641226132634600211270ustar00rootroot00000000000000require 'vagrant/util/template_renderer' module VagrantPlugins module GuestDarwin # A general Vagrant system implementation for OS X (ie. "Darwin"). # # Contributed by: - Brian Johnson # - Tim Sutton class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep 'Darwin'") end end end end vagrant-1.4.3/plugins/guests/darwin/plugin.rb000066400000000000000000000024521226132634600212730ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestDarwin class Plugin < Vagrant.plugin("2") name "Darwin guest" description "Darwin guest support." guest("darwin") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("darwin", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("darwin", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("darwin", "halt") do require_relative "cap/halt" Cap::Halt end guest_capability("darwin", "mount_nfs_folder") do require_relative "cap/mount_nfs_folder" Cap::MountNFSFolder end guest_capability("darwin", "mount_vmware_shared_folder") do require_relative "cap/mount_vmware_shared_folder" Cap::MountVmwareSharedFolder end guest_capability("darwin", "shell_expand_guest_path") do require_relative "cap/shell_expand_guest_path" Cap::ShellExpandGuestPath end guest_capability("darwin", "verify_vmware_hgfs") do require_relative "cap/verify_vmware_hgfs" Cap::VerifyVmwareHgfs end end end end vagrant-1.4.3/plugins/guests/debian/000077500000000000000000000000001226132634600174035ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/debian/cap/000077500000000000000000000000001226132634600201465ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/debian/cap/change_host_name.rb000066400000000000000000000037341226132634600237640ustar00rootroot00000000000000module VagrantPlugins module GuestDebian module Cap class ChangeHostName def self.change_host_name(machine, name) new(machine, name).change! end attr_reader :machine, :new_hostname def initialize(machine, new_hostname) @machine = machine @new_hostname = new_hostname end def change! return unless should_change? update_etc_hostname update_etc_hosts refresh_hostname_service update_mailname renew_dhcp end def should_change? new_hostname != current_hostname end def current_hostname @current_hostname ||= get_current_hostname end def get_current_hostname sudo "hostname -f" do |type, data| return data.chomp if type == :stdout end '' end def update_etc_hostname sudo("echo '#{short_hostname}' > /etc/hostname") end # /etc/hosts should resemble: # 127.0.0.1 localhost # 127.0.1.1 host.fqdn.com host.fqdn host def update_etc_hosts ip_address = '([0-9]{1,3}\.){3}[0-9]{1,3}' search = "^(#{ip_address})\\s+#{Regexp.escape(current_hostname)}(\\s.*)?$" replace = "\\1 #{fqdn} #{short_hostname}" expression = ['s', search, replace, 'g'].join('@') sudo("sed -ri '#{expression}' /etc/hosts") end def refresh_hostname_service sudo("hostname -F /etc/hostname") end def update_mailname sudo("hostname --fqdn > /etc/mailname") end def renew_dhcp sudo("ifdown -a; ifup -a; ifup eth0") end def fqdn new_hostname end def short_hostname new_hostname.split('.').first end def sudo(cmd, &block) machine.communicate.sudo(cmd, &block) end end end end end vagrant-1.4.3/plugins/guests/debian/cap/configure_networks.rb000066400000000000000000000043641226132634600244170ustar00rootroot00000000000000require 'set' require 'tempfile' require "vagrant/util/template_renderer" module VagrantPlugins module GuestDebian module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) machine.communicate.tap do |comm| # First, remove any previous network modifications # from the interface file. comm.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/network/interfaces > /tmp/vagrant-network-interfaces") comm.sudo("su -c 'cat /tmp/vagrant-network-interfaces > /etc/network/interfaces'") comm.sudo("rm /tmp/vagrant-network-interfaces") # Accumulate the configurations to add to the interfaces file as # well as what interfaces we're actually configuring since we use that # later. interfaces = Set.new entries = [] networks.each do |network| interfaces.add(network[:interface]) entry = TemplateRenderer.render("guests/debian/network_#{network[:type]}", :options => network) entries << entry end # Perform the careful dance necessary to reconfigure # the network interfaces temp = Tempfile.new("vagrant") temp.binmode temp.write(entries.join("\n")) temp.close comm.upload(temp.path, "/tmp/vagrant-network-entry") # Bring down all the interfaces we're reconfiguring. By bringing down # each specifically, we avoid reconfiguring eth0 (the NAT interface) so # SSH never dies. interfaces.each do |interface| comm.sudo("/sbin/ifdown eth#{interface} 2> /dev/null") comm.sudo("/sbin/ip addr flush dev eth#{interface} 2> /dev/null") end comm.sudo("cat /tmp/vagrant-network-entry >> /etc/network/interfaces") comm.sudo("rm /tmp/vagrant-network-entry") # Bring back up each network interface, reconfigured interfaces.each do |interface| comm.sudo("/sbin/ifup eth#{interface}") end end end end end end end vagrant-1.4.3/plugins/guests/debian/guest.rb000066400000000000000000000003231226132634600210550ustar00rootroot00000000000000module VagrantPlugins module GuestDebian class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/issue | grep 'Debian'") end end end end vagrant-1.4.3/plugins/guests/debian/plugin.rb000066400000000000000000000011221226132634600212220ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestDebian class Plugin < Vagrant.plugin("2") name "Debian guest" description "Debian guest support." guest("debian", "linux") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("debian", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("debian", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end end end end vagrant-1.4.3/plugins/guests/esxi/000077500000000000000000000000001226132634600171315ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/esxi/cap/000077500000000000000000000000001226132634600176745ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/esxi/cap/change_host_name.rb000066400000000000000000000005471226132634600235110ustar00rootroot00000000000000module VagrantPlugins module GuestEsxi module Cap class ChangeHostName def self.change_host_name(machine, name) if !machine.communicate.test("localcli system hostname get | grep '#{name}'") machine.communicate.execute("localcli system hostname set -H '#{name}'") end end end end end end vagrant-1.4.3/plugins/guests/esxi/cap/configure_networks.rb000066400000000000000000000031311226132634600241340ustar00rootroot00000000000000module VagrantPlugins module GuestEsxi module Cap class ConfigureNetworks def self.configure_networks(machine, networks) networks.each do |network| ix = network[:interface] switch = "vSwitch#{ix}" pg = "VagrantNetwork#{ix}" vmnic = "vmnic#{ix}" device = "vmk#{ix}" if machine.communicate.test("localcli network ip interface ipv4 get -i #{device}") machine.communicate.execute("localcli network ip interface remove -i #{device}") end if machine.communicate.test("localcli network vswitch standard list -v #{switch}") machine.communicate.execute("localcli network vswitch standard remove -v #{switch}") end machine.communicate.execute("localcli network vswitch standard add -v #{switch}") machine.communicate.execute("localcli network vswitch standard uplink add -v #{switch} -u #{vmnic}") machine.communicate.execute("localcli network vswitch standard portgroup add -v #{switch} -p #{pg}") machine.communicate.execute("localcli network ip interface add -i #{device} -p #{pg}") ifconfig = "localcli network ip interface ipv4 set -i #{device}" if network[:type].to_sym == :static machine.communicate.execute("#{ifconfig} -t static --ipv4 #{network[:ip]} --netmask #{network[:netmask]}") elsif network[:type].to_sym == :dhcp machine.communicate.execute("#{ifconfig} -t dhcp") end end end end end end end vagrant-1.4.3/plugins/guests/esxi/cap/halt.rb000066400000000000000000000005411226132634600211510ustar00rootroot00000000000000module VagrantPlugins module GuestEsxi module Cap class Halt def self.halt(machine) begin machine.communicate.execute("/bin/halt -d 0") rescue IOError # Ignore, this probably means connection closed because it # shut down. end end end end end end vagrant-1.4.3/plugins/guests/esxi/cap/mount_nfs_folder.rb000066400000000000000000000026771226132634600236000ustar00rootroot00000000000000module VagrantPlugins module GuestEsxi module Cap class MountNFSFolder extend Vagrant::Util::Retryable def self.mount_nfs_folder(machine, ip, folders) folders.each do |name, opts| guestpath = opts[:guestpath] volume = guestpath.gsub("/", "_") machine.communicate.tap do |comm| if comm.test("localcli storage nfs list | grep '^#{volume}'") comm.execute("localcli storage nfs remove -v #{volume}") end mount_command = "localcli storage nfs add -H #{ip} -s '#{opts[:hostpath]}' -v '#{volume}'" retryable(:on => Vagrant::Errors::LinuxNFSMountFailed, :tries => 5, :sleep => 2) do comm.execute(mount_command, :error_class => Vagrant::Errors::LinuxNFSMountFailed) end # symlink vmfs volume to :guestpath if comm.test("test -L '#{guestpath}'") comm.execute("rm '#{guestpath}'") end if comm.test("test -d '#{guestpath}'") comm.execute("rmdir '#{guestpath}'") end dir = File.dirname(guestpath) if !comm.test("test -d '#{dir}'") comm.execute("mkdir -p '#{dir}'") end comm.execute("ln -s '/vmfs/volumes/#{volume}' '#{guestpath}'") end end end end end end end vagrant-1.4.3/plugins/guests/esxi/guest.rb000066400000000000000000000003361226132634600206070ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestEsxi class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep VMkernel") end end end end vagrant-1.4.3/plugins/guests/esxi/plugin.rb000066400000000000000000000014671226132634600207640ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestEsxi class Plugin < Vagrant.plugin("2") name "ESXi guest." description "ESXi guest support." guest("esxi") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("esxi", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("esxi", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("esxi", "mount_nfs_folder") do require_relative "cap/mount_nfs_folder" Cap::MountNFSFolder end guest_capability("esxi", "halt") do require_relative "cap/halt" Cap::Halt end end end end vagrant-1.4.3/plugins/guests/fedora/000077500000000000000000000000001226132634600174215ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/fedora/cap/000077500000000000000000000000001226132634600201645ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/fedora/cap/configure_networks.rb000066400000000000000000000050771226132634600244370ustar00rootroot00000000000000require "set" require "tempfile" require "vagrant/util/retryable" require "vagrant/util/template_renderer" module VagrantPlugins module GuestFedora module Cap class ConfigureNetworks extend Vagrant::Util::Retryable include Vagrant::Util def self.configure_networks(machine, networks) network_scripts_dir = machine.guest.capability("network_scripts_dir") # Accumulate the configurations to add to the interfaces file as well # as what interfaces we're actually configuring since we use that later. interfaces = Set.new networks.each do |network| interfaces.add(network[:interface]) # Remove any previous vagrant configuration in this network # interface's configuration files. machine.communicate.sudo("touch #{network_scripts_dir}/ifcfg-p7p#{network[:interface]}") machine.communicate.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' #{network_scripts_dir}/ifcfg-p7p#{network[:interface]} > /tmp/vagrant-ifcfg-p7p#{network[:interface]}") machine.communicate.sudo("cat /tmp/vagrant-ifcfg-p7p#{network[:interface]} > #{network_scripts_dir}/ifcfg-p7p#{network[:interface]}") machine.communicate.sudo("rm /tmp/vagrant-ifcfg-p7p#{network[:interface]}") # Render and upload the network entry file to a deterministic # temporary location. entry = TemplateRenderer.render("guests/fedora/network_#{network[:type]}", :options => network) temp = Tempfile.new("vagrant") temp.binmode temp.write(entry) temp.close machine.communicate.upload(temp.path, "/tmp/vagrant-network-entry_#{network[:interface]}") end # Bring down all the interfaces we're reconfiguring. By bringing down # each specifically, we avoid reconfiguring p7p (the NAT interface) so # SSH never dies. interfaces.each do |interface| retryable(:on => Vagrant::Errors::VagrantError, :tries => 3, :sleep => 2) do machine.communicate.sudo("/sbin/ifdown p7p#{interface} 2> /dev/null", :error_check => false) machine.communicate.sudo("cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-p7p#{interface}") machine.communicate.sudo("/sbin/ifup p7p#{interface} 2> /dev/null") end machine.communicate.sudo("rm /tmp/vagrant-network-entry_#{interface}") end end end end end end vagrant-1.4.3/plugins/guests/fedora/cap/network_scripts_dir.rb000066400000000000000000000007451226132634600246150ustar00rootroot00000000000000module VagrantPlugins module GuestFedora module Cap class NetworkScriptsDir # The path to the directory with the network configuration scripts. # This is pulled out into its own directory since there are other # operating systems (SuSE) which behave similarly but with a different # path to the network scripts. def self.network_scripts_dir(machine) "/etc/sysconfig/network-scripts" end end end end end vagrant-1.4.3/plugins/guests/fedora/guest.rb000066400000000000000000000003701226132634600210750ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestFedora class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("grep 'Fedora release 1[678]' /etc/redhat-release") end end end end vagrant-1.4.3/plugins/guests/fedora/plugin.rb000066400000000000000000000011341226132634600212430ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestFedora class Plugin < Vagrant.plugin("2") name "Fedora guest" description "Fedora guest support." guest("fedora", "redhat") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("fedora", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("fedora", "network_scripts_dir") do require_relative "cap/network_scripts_dir" Cap::NetworkScriptsDir end end end end vagrant-1.4.3/plugins/guests/freebsd/000077500000000000000000000000001226132634600175735ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/freebsd/cap/000077500000000000000000000000001226132634600203365ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/freebsd/cap/change_host_name.rb000066400000000000000000000007721226132634600241530ustar00rootroot00000000000000module VagrantPlugins module GuestFreeBSD module Cap class ChangeHostName def self.change_host_name(machine, name) if !machine.communicate.test("hostname -f | grep '^#{name}$' || hostname -s | grep '^#{name}$'", {:shell => "sh"}) machine.communicate.sudo("sed -i '' 's/^hostname=.*$/hostname=#{name}/' /etc/rc.conf", {:shell => "sh"}) machine.communicate.sudo("hostname #{name}", {:shell => "sh"}) end end end end end end vagrant-1.4.3/plugins/guests/freebsd/cap/configure_networks.rb000066400000000000000000000027311226132634600246030ustar00rootroot00000000000000require "tempfile" require "vagrant/util/template_renderer" module VagrantPlugins module GuestFreeBSD module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) # Remove any previous network additions to the configuration file. machine.communicate.sudo("sed -i '' -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/rc.conf", {:shell => "sh"}) networks.each do |network| entry = TemplateRenderer.render("guests/freebsd/network_#{network[:type]}", :options => network) # Write the entry to a temporary location temp = Tempfile.new("vagrant") temp.binmode temp.write(entry) temp.close machine.communicate.upload(temp.path, "/tmp/vagrant-network-entry") machine.communicate.sudo("su -m root -c 'cat /tmp/vagrant-network-entry >> /etc/rc.conf'", {:shell => "sh"}) machine.communicate.sudo("rm /tmp/vagrant-network-entry", {:shell => "sh"}) if network[:type].to_sym == :static machine.communicate.sudo("ifconfig em#{network[:interface]} inet #{network[:ip]} netmask #{network[:netmask]}", {:shell => "sh"}) elsif network[:type].to_sym == :dhcp machine.communicate.sudo("dhclient em#{network[:interface]}", {:shell => "sh"}) end end end end end end end vagrant-1.4.3/plugins/guests/freebsd/cap/halt.rb000066400000000000000000000006221226132634600216130ustar00rootroot00000000000000module VagrantPlugins module GuestFreeBSD module Cap class Halt def self.halt(machine) begin machine.communicate.sudo("shutdown -p now", {:shell => "sh"}) rescue IOError # Do nothing because SSH connection closed and it probably # means the VM just shut down really fast. end end end end end end vagrant-1.4.3/plugins/guests/freebsd/cap/mount_nfs_folder.rb000066400000000000000000000006721226132634600242330ustar00rootroot00000000000000module VagrantPlugins module GuestFreeBSD module Cap class MountNFSFolder def self.mount_nfs_folder(machine, ip, folders) folders.each do |name, opts| machine.communicate.sudo("mkdir -p #{opts[:guestpath]}", {:shell => "sh"}) machine.communicate.sudo("mount -t nfs '#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}'", {:shell => "sh"}) end end end end end end vagrant-1.4.3/plugins/guests/freebsd/guest.rb000066400000000000000000000006201226132634600212450ustar00rootroot00000000000000require 'vagrant/util/template_renderer' module VagrantPlugins module GuestFreeBSD # A general Vagrant system implementation for "freebsd". # # Contributed by Kenneth Vestergaard class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep 'FreeBSD'", {:shell => "sh"}) end end end end vagrant-1.4.3/plugins/guests/freebsd/plugin.rb000066400000000000000000000015161226132634600214210ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestFreeBSD class Plugin < Vagrant.plugin("2") name "FreeBSD guest" description "FreeBSD guest support." guest("freebsd") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("freebsd", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("freebsd", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("freebsd", "halt") do require_relative "cap/halt" Cap::Halt end guest_capability("freebsd", "mount_nfs_folder") do require_relative "cap/mount_nfs_folder" Cap::MountNFSFolder end end end end vagrant-1.4.3/plugins/guests/gentoo/000077500000000000000000000000001226132634600174545ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/gentoo/cap/000077500000000000000000000000001226132634600202175ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/gentoo/cap/change_host_name.rb000066400000000000000000000011061226132634600240240ustar00rootroot00000000000000module VagrantPlugins module GuestGentoo module Cap class ChangeHostName def self.change_host_name(machine, name) machine.communicate.tap do |comm| if !comm.test("sudo hostname --fqdn | grep '#{name}'") comm.sudo("echo 'hostname=#{name.split('.')[0]}' > /etc/conf.d/hostname") comm.sudo("sed -i 's@^\\(127[.]0[.]0[.]1[[:space:]]\\+\\)@\\1#{name} #{name.split('.')[0]} @' /etc/hosts") comm.sudo("hostname #{name.split('.')[0]}") end end end end end end end vagrant-1.4.3/plugins/guests/gentoo/cap/configure_networks.rb000066400000000000000000000031271226132634600244640ustar00rootroot00000000000000require "tempfile" require "vagrant/util/template_renderer" module VagrantPlugins module GuestGentoo module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) machine.communicate.tap do |comm| # Remove any previous host only network additions to the interface file comm.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' /etc/conf.d/net > /tmp/vagrant-network-interfaces") comm.sudo("cat /tmp/vagrant-network-interfaces > /etc/conf.d/net") comm.sudo("rm /tmp/vagrant-network-interfaces") # Configure each network interface networks.each do |network| entry = TemplateRenderer.render("guests/gentoo/network_#{network[:type]}", :options => network) # Upload the entry to a temporary location temp = Tempfile.new("vagrant") temp.binmode temp.write(entry) temp.close comm.upload(temp.path, "/tmp/vagrant-network-entry") # Configure the interface comm.sudo("ln -fs /etc/init.d/net.lo /etc/init.d/net.eth#{network[:interface]}") comm.sudo("/etc/init.d/net.eth#{network[:interface]} stop 2> /dev/null") comm.sudo("cat /tmp/vagrant-network-entry >> /etc/conf.d/net") comm.sudo("rm /tmp/vagrant-network-entry") comm.sudo("/etc/init.d/net.eth#{network[:interface]} start") end end end end end end end vagrant-1.4.3/plugins/guests/gentoo/guest.rb000066400000000000000000000003241226132634600211270ustar00rootroot00000000000000module VagrantPlugins module GuestGentoo class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("grep Gentoo /etc/gentoo-release") end end end end vagrant-1.4.3/plugins/guests/gentoo/plugin.rb000066400000000000000000000011221226132634600212730ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestGentoo class Plugin < Vagrant.plugin("2") name "Gentoo guest" description "Gentoo guest support." guest("gentoo", "linux") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("gentoo", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("gentoo", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end end end end vagrant-1.4.3/plugins/guests/linux/000077500000000000000000000000001226132634600173205ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/linux/cap/000077500000000000000000000000001226132634600200635ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/linux/cap/halt.rb000066400000000000000000000005651226132634600213460ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap class Halt def self.halt(machine) begin machine.communicate.sudo("shutdown -h now") rescue IOError # Do nothing, because it probably means the machine shut down # and SSH connection was lost. end end end end end end vagrant-1.4.3/plugins/guests/linux/cap/mount_nfs.rb000066400000000000000000000031111226132634600224140ustar00rootroot00000000000000require "vagrant/util/retryable" module VagrantPlugins module GuestLinux module Cap class MountNFS extend Vagrant::Util::Retryable def self.mount_nfs_folder(machine, ip, folders) folders.each do |name, opts| # Expand the guest path so we can handle things like "~/vagrant" expanded_guest_path = machine.guest.capability( :shell_expand_guest_path, opts[:guestpath]) # Do the actual creating and mounting machine.communicate.sudo("mkdir -p #{expanded_guest_path}") # Mount hostpath = opts[:hostpath].dup hostpath.gsub!("'", "'\\\\''") # Figure out any options mount_opts = ["vers=#{opts[:nfs_version]}"] mount_opts << "udp" if opts[:nfs_udp] if opts[:mount_options] mount_opts = opts[:mount_options].dup end mount_command = "mount -o '#{mount_opts.join(",")}' #{ip}:'#{hostpath}' #{expanded_guest_path}" retryable(:on => Vagrant::Errors::LinuxNFSMountFailed, :tries => 8, :sleep => 3) do machine.communicate.sudo(mount_command, :error_class => Vagrant::Errors::LinuxNFSMountFailed) end # Emit an upstart event if we can if machine.communicate.test("test -x /sbin/initctl") machine.communicate.sudo( "/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{expanded_guest_path}") end end end end end end end vagrant-1.4.3/plugins/guests/linux/cap/mount_virtualbox_shared_folder.rb000066400000000000000000000057541226132634600267250ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap class MountVirtualBoxSharedFolder def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) expanded_guest_path = machine.guest.capability( :shell_expand_guest_path, guestpath) mount_commands = [] if options[:owner].is_a? Integer mount_uid = options[:owner] else mount_uid = "`id -u #{options[:owner]}`" end if options[:group].is_a? Integer mount_gid = options[:group] mount_gid_old = options[:group] else mount_gid = "`getent group #{options[:group]} | cut -d: -f3`" mount_gid_old = "`id -g #{options[:group]}`" end # First mount command uses getent to get the group mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}" mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options] mount_commands << "mount -t vboxsf #{mount_options} #{name} #{expanded_guest_path}" # Second mount command uses the old style `id -g` mount_options = "-o uid=#{mount_uid},gid=#{mount_gid_old}" mount_options += ",#{options[:mount_options].join(",")}" if options[:mount_options] mount_commands << "mount -t vboxsf #{mount_options} #{name} #{expanded_guest_path}" # Create the guest path if it doesn't exist machine.communicate.sudo("mkdir -p #{expanded_guest_path}") # Attempt to mount the folder. We retry here a few times because # it can fail early on. attempts = 0 while true success = true mount_commands.each do |command| no_such_device = false status = machine.communicate.sudo(command, error_check: false) do |type, data| no_such_device = true if type == :stderr && data =~ /No such device/i end success = status == 0 && !no_such_device break if success end break if success attempts += 1 if attempts > 10 raise Vagrant::Errors::LinuxMountFailed, command: mount_commands.join("\n") end sleep 2 end # Chown the directory to the proper user chown_commands = [] chown_commands << "chown #{mount_uid}:#{mount_gid} #{expanded_guest_path}" chown_commands << "chown #{mount_uid}:#{mount_gid_old} #{expanded_guest_path}" exit_status = machine.communicate.sudo(chown_commands[0], error_check: false) machine.communicate.sudo(chown_commands[1]) if exit_status != 0 # Emit an upstart event if we can if machine.communicate.test("test -x /sbin/initctl") machine.communicate.sudo( "/sbin/initctl emit --no-wait vagrant-mounted MOUNTPOINT=#{expanded_guest_path}") end end end end end end vagrant-1.4.3/plugins/guests/linux/cap/read_ip_address.rb000066400000000000000000000007151226132634600235230ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap class ReadIPAddress def self.read_ip_address(machine) command = "ifconfig | grep 'inet addr:'| grep -v '127.0.0.1' | cut -d: -f2 | awk '{ print $1 }'" result = "" machine.communicate.execute(command) do |type, data| result << data if type == :stdout end result.chomp.split("\n").first end end end end end vagrant-1.4.3/plugins/guests/linux/cap/shell_expand_guest_path.rb000066400000000000000000000015121226132634600253000ustar00rootroot00000000000000module VagrantPlugins module GuestLinux module Cap class ShellExpandGuestPath def self.shell_expand_guest_path(machine, path) real_path = nil machine.communicate.execute("echo; printf #{path}") do |type, data| if type == :stdout real_path ||= "" real_path += data end end # The last line is the path we care about real_path = real_path.split("\n").last.chomp if !real_path # If no real guest path was detected, this is really strange # and we raise an exception because this is a bug. raise LinuxShellExpandFailed end # Chomp the string so that any trailing newlines are killed return real_path.chomp end end end end end vagrant-1.4.3/plugins/guests/linux/guest.rb000066400000000000000000000003361226132634600207760ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestLinux class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep 'Linux'") end end end end vagrant-1.4.3/plugins/guests/linux/plugin.rb000066400000000000000000000017631226132634600211520ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestLinux class Plugin < Vagrant.plugin("2") name "Linux guest." description "Linux guest support." guest("linux") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("linux", "halt") do require_relative "cap/halt" Cap::Halt end guest_capability("linux", "shell_expand_guest_path") do require_relative "cap/shell_expand_guest_path" Cap::ShellExpandGuestPath end guest_capability("linux", "mount_nfs_folder") do require_relative "cap/mount_nfs" Cap::MountNFS end guest_capability("linux", "mount_virtualbox_shared_folder") do require_relative "cap/mount_virtualbox_shared_folder" Cap::MountVirtualBoxSharedFolder end guest_capability("linux", "read_ip_address") do require_relative "cap/read_ip_address" Cap::ReadIPAddress end end end end vagrant-1.4.3/plugins/guests/omnios/000077500000000000000000000000001226132634600174655ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/omnios/cap/000077500000000000000000000000001226132634600202305ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/omnios/cap/change_host_name.rb000066400000000000000000000010401226132634600240320ustar00rootroot00000000000000module VagrantPlugins module GuestOmniOS module Cap class ChangeHostName def self.change_host_name(machine, name) su_cmd = machine.config.solaris.suexec_cmd # Only do this if the hostname is not already set if !machine.communicate.test("#{su_cmd} hostname | grep '#{name}'") machine.communicate.execute("#{su_cmd} sh -c \"echo '#{name}' > /etc/nodename\"") machine.communicate.execute("#{su_cmd} hostname #{name}") end end end end end end vagrant-1.4.3/plugins/guests/omnios/guest.rb000066400000000000000000000003511226132634600211400ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestOmniOS class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/release | grep -i OmniOS") end end end end vagrant-1.4.3/plugins/guests/omnios/plugin.rb000066400000000000000000000007001226132634600213050ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestOmniOS class Plugin < Vagrant.plugin("2") name "OmniOS guest." description "OmniOS guest support." guest("omnios", "solaris") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("omnios", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end end end end vagrant-1.4.3/plugins/guests/openbsd/000077500000000000000000000000001226132634600176135ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/openbsd/cap/000077500000000000000000000000001226132634600203565ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/openbsd/cap/change_host_name.rb000066400000000000000000000006131226132634600241650ustar00rootroot00000000000000module VagrantPlugins module GuestOpenBSD module Cap class ChangeHostName def self.change_host_name(machine, name) if !machine.communicate.test("hostname | grep '^#{name}$'") machine.communicate.sudo("sh -c \"echo '#{name}' > /etc/myname\"") machine.communicate.sudo("hostname #{name}") end end end end end end vagrant-1.4.3/plugins/guests/openbsd/cap/configure_networks.rb000066400000000000000000000025131226132634600246210ustar00rootroot00000000000000require "tempfile" require "vagrant/util/template_renderer" module VagrantPlugins module GuestOpenBSD module Cap class ConfigureNetworks include Vagrant::Util def self.configure_networks(machine, networks) networks.each do |network| entry = TemplateRenderer.render("guests/openbsd/network_#{network[:type]}", :options => network) temp = Tempfile.new("vagrant") temp.binmode temp.write(entry) temp.close ifname = "em#{network[:interface]}" machine.communicate.upload(temp.path, "/tmp/vagrant-network-entry") machine.communicate.sudo("mv /tmp/vagrant-network-entry /etc/hostname.#{ifname}") # remove old configurations machine.communicate.sudo("sudo ifconfig #{ifname} inet delete", { :error_check => false }) machine.communicate.sudo("pkill -f 'dhclient: #{ifname}'", { :error_check => false }) if network[:type].to_sym == :static machine.communicate.sudo("ifconfig #{ifname} inet #{network[:ip]} netmask #{network[:netmask]}") elsif network[:type].to_sym == :dhcp machine.communicate.sudo("dhclient #{ifname}") end end end end end end end vagrant-1.4.3/plugins/guests/openbsd/cap/halt.rb000066400000000000000000000005721226132634600216370ustar00rootroot00000000000000module VagrantPlugins module GuestOpenBSD module Cap class Halt def self.halt(machine) begin machine.communicate.sudo("shutdown -p -h now") rescue IOError # Do nothing, because it probably means the machine shut down # and SSH connection was lost. end end end end end end vagrant-1.4.3/plugins/guests/openbsd/cap/mount_nfs_folder.rb000066400000000000000000000006171226132634600242520ustar00rootroot00000000000000module VagrantPlugins module GuestOpenBSD module Cap class MountNFSFolder def self.mount_nfs_folder(machine, ip, folders) folders.each do |name, opts| machine.communicate.sudo("mkdir -p #{opts[:guestpath]}") machine.communicate.sudo("mount '#{ip}:#{opts[:hostpath]}' '#{opts[:guestpath]}'") end end end end end end vagrant-1.4.3/plugins/guests/openbsd/guest.rb000066400000000000000000000003421226132634600212660ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestOpenBSD class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep 'OpenBSD'") end end end end vagrant-1.4.3/plugins/guests/openbsd/plugin.rb000066400000000000000000000015261226132634600214420ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestOpenBSD class Plugin < Vagrant.plugin("2") name "OpenBSD guest" description "OpenBSD guest support." guest("openbsd", "linux") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("openbsd", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("openbsd", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("openbsd", "halt") do require_relative "cap/halt" Cap::Halt end guest_capability("openbsd", "mount_nfs_folder") do require_relative "cap/mount_nfs_folder" Cap::MountNFSFolder end end end end vagrant-1.4.3/plugins/guests/pld/000077500000000000000000000000001226132634600167405ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/pld/cap/000077500000000000000000000000001226132634600175035ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/pld/cap/change_host_name.rb000066400000000000000000000014371226132634600233170ustar00rootroot00000000000000module VagrantPlugins module GuestPld module Cap class ChangeHostName def self.change_host_name(machine, name) machine.communicate.tap do |comm| # Only do this if the hostname is not already set if !comm.test("sudo hostname | grep --line-regexp '#{name}'") comm.sudo("sed -i 's/\\(HOSTNAME=\\).*/\\1#{name}/' /etc/sysconfig/network") comm.sudo("hostname #{name}") comm.sudo("sed -i 's@^\\(127[.]0[.]0[.]1[[:space:]]\\+\\)@\\1#{name} #{name.split('.')[0]} @' /etc/hosts") comm.sudo("sed -i 's/\\(DHCP_HOSTNAME=\\).*/\\1\"#{name}\"/' /etc/sysconfig/interfaces/ifcfg-*") comm.sudo("service network restart") end end end end end end end vagrant-1.4.3/plugins/guests/pld/cap/network_scripts_dir.rb000066400000000000000000000003211226132634600241220ustar00rootroot00000000000000module VagrantPlugins module GuestPld module Cap class NetworkScriptsDir def self.network_scripts_dir(machine) "/etc/sysconfig/interfaces" end end end end end vagrant-1.4.3/plugins/guests/pld/guest.rb000066400000000000000000000003311226132634600204110ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestPld class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/pld-release") end end end end vagrant-1.4.3/plugins/guests/pld/plugin.rb000066400000000000000000000011171226132634600205630ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestPld class Plugin < Vagrant.plugin("2") name "PLD Linux guest" description "PLD Linux guest support." guest("pld", "redhat") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("pld", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("pld", "network_scripts_dir") do require_relative "cap/network_scripts_dir" Cap::NetworkScriptsDir end end end end vagrant-1.4.3/plugins/guests/redhat/000077500000000000000000000000001226132634600174305ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/redhat/cap/000077500000000000000000000000001226132634600201735ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/redhat/cap/change_host_name.rb000066400000000000000000000052621226132634600240070ustar00rootroot00000000000000module VagrantPlugins module GuestRedHat module Cap class ChangeHostName def self.change_host_name(machine, name) new(machine, name).change! end attr_reader :machine, :new_hostname def initialize(machine, new_hostname) @machine = machine @new_hostname = new_hostname end def change! return unless should_change? update_sysconfig update_hostname update_etc_hosts update_dhcp_hostnames restart_networking end def should_change? new_hostname != current_hostname end def current_hostname @current_hostname ||= get_current_hostname end def get_current_hostname hostname = '' block = lambda do |type, data| if type == :stdout hostname += data.chomp end end execute 'hostname -f', error_check: false, &block execute 'hostname',&block if hostname.empty? /localhost(\..*)?/.match(hostname) ? '' : hostname end def update_sysconfig sudo "sed -i 's/\\(HOSTNAME=\\).*/\\1#{fqdn}/' /etc/sysconfig/network" end def update_hostname sudo "hostname #{short_hostname}" end # /etc/hosts should resemble: # 127.0.0.1 host.fqdn.com host localhost ... def update_etc_hosts s = '[[:space:]]' current_fqdn = Regexp.escape(current_hostname) current_short = Regexp.escape(current_hostname.split('.').first.to_s) currents = "\\(#{current_fqdn}#{s}\\+\\|#{current_short}#{s}\\+\\)*" unless current_hostname.empty? local_ip = '127[.]0[.]0[.]1' search = "^\\(#{local_ip}#{s}\\+\\)#{currents}" replace = "\\1#{fqdn} " replace = "#{replace}#{short_hostname} " unless fqdn == short_hostname expression = ['s', search, replace,''].join('@') sudo "sed -i '#{expression}' /etc/hosts" end def update_dhcp_hostnames sudo "sed -i 's/\\(DHCP_HOSTNAME=\\).*/\\1\"#{short_hostname}\"/' /etc/sysconfig/network-scripts/ifcfg-*" end def restart_networking sudo 'service network restart' end def fqdn new_hostname end def short_hostname new_hostname.split('.').first end def execute(cmd, opts=nil, &block) machine.communicate.execute(cmd, opts, &block) end def sudo(cmd, opts=nil, &block) machine.communicate.sudo(cmd, opts, &block) end end end end end vagrant-1.4.3/plugins/guests/redhat/cap/configure_networks.rb000066400000000000000000000061101226132634600244330ustar00rootroot00000000000000require "set" require "tempfile" require "vagrant/util/retryable" require "vagrant/util/template_renderer" module VagrantPlugins module GuestRedHat module Cap class ConfigureNetworks extend Vagrant::Util::Retryable include Vagrant::Util def self.configure_networks(machine, networks) network_scripts_dir = machine.guest.capability("network_scripts_dir") # Accumulate the configurations to add to the interfaces file as # well as what interfaces we're actually configuring since we use that # later. interfaces = Set.new networks.each do |network| interfaces.add(network[:interface]) # Down the interface before munging the config file. This might fail # if the interface is not actually set up yet so ignore errors. machine.communicate.sudo( "/sbin/ifdown eth#{network[:interface]} 2> /dev/null", error_check: false) # Remove any previous vagrant configuration in this network interface's # configuration files. machine.communicate.sudo("touch #{network_scripts_dir}/ifcfg-eth#{network[:interface]}") machine.communicate.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' #{network_scripts_dir}/ifcfg-eth#{network[:interface]} > /tmp/vagrant-ifcfg-eth#{network[:interface]}") machine.communicate.sudo("cat /tmp/vagrant-ifcfg-eth#{network[:interface]} > #{network_scripts_dir}/ifcfg-eth#{network[:interface]}") machine.communicate.sudo("rm /tmp/vagrant-ifcfg-eth#{network[:interface]}") # Render and upload the network entry file to a deterministic # temporary location. entry = TemplateRenderer.render("guests/redhat/network_#{network[:type]}", :options => network) temp = Tempfile.new("vagrant") temp.binmode temp.write(entry) temp.close machine.communicate.upload(temp.path, "/tmp/vagrant-network-entry_#{network[:interface]}") end # Bring down all the interfaces we're reconfiguring. By bringing down # each specifically, we avoid reconfiguring eth0 (the NAT interface) so # SSH never dies. interfaces.each do |interface| retryable(:on => Vagrant::Errors::VagrantError, :tries => 3, :sleep => 2) do # The interface should already be down so this probably # won't do anything, so we run it with error_check false. machine.communicate.sudo( "/sbin/ifdown eth#{interface} 2> /dev/null", error_check: false) # Add the new interface and bring it up machine.communicate.sudo("cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-eth#{interface}") machine.communicate.sudo("ARPCHECK=no /sbin/ifup eth#{interface} 2> /dev/null") end machine.communicate.sudo("rm /tmp/vagrant-network-entry_#{interface}") end end end end end end vagrant-1.4.3/plugins/guests/redhat/cap/network_scripts_dir.rb000066400000000000000000000003311226132634600246130ustar00rootroot00000000000000module VagrantPlugins module GuestRedHat module Cap class NetworkScriptsDir def self.network_scripts_dir(machine) "/etc/sysconfig/network-scripts" end end end end end vagrant-1.4.3/plugins/guests/redhat/guest.rb000066400000000000000000000003371226132634600211070ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestRedHat class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/redhat-release") end end end end vagrant-1.4.3/plugins/guests/redhat/plugin.rb000066400000000000000000000013521226132634600212540ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestRedHat class Plugin < Vagrant.plugin("2") name "RedHat guest" description "RedHat guest support." guest("redhat", "linux") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("redhat", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("redhat", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("redhat", "network_scripts_dir") do require_relative "cap/network_scripts_dir" Cap::NetworkScriptsDir end end end end vagrant-1.4.3/plugins/guests/solaris/000077500000000000000000000000001226132634600176355ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/solaris/cap/000077500000000000000000000000001226132634600204005ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/solaris/cap/change_host_name.rb000066400000000000000000000010411226132634600242030ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris module Cap class ChangeHostName def self.change_host_name(machine, name) su_cmd = machine.config.solaris.suexec_cmd # Only do this if the hostname is not already set if !machine.communicate.test("#{su_cmd} hostname | grep '#{name}'") machine.communicate.execute("#{su_cmd} sh -c \"echo '#{name}' > /etc/nodename\"") machine.communicate.execute("#{su_cmd} uname -S #{name}") end end end end end end vagrant-1.4.3/plugins/guests/solaris/cap/configure_networks.rb000066400000000000000000000017151226132634600246460ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris module Cap class ConfigureNetworks def self.configure_networks(machine, networks) networks.each do |network| device = "#{machine.config.solaris.device}#{network[:interface]}" su_cmd = machine.config.solaris.suexec_cmd ifconfig_cmd = "#{su_cmd} /sbin/ifconfig #{device}" machine.communicate.execute("#{ifconfig_cmd} plumb") if network[:type].to_sym == :static machine.communicate.execute("#{ifconfig_cmd} inet #{network[:ip]} netmask #{network[:netmask]}") machine.communicate.execute("#{ifconfig_cmd} up") machine.communicate.execute("#{su_cmd} sh -c \"echo '#{network[:ip]}' > /etc/hostname.#{device}\"") elsif network[:type].to_sym == :dhcp machine.communicate.execute("#{ifconfig_cmd} dhcp start") end end end end end end end vagrant-1.4.3/plugins/guests/solaris/cap/halt.rb000066400000000000000000000011501226132634600216520ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris module Cap class Halt def self.halt(machine) # There should be an exception raised if the line # # vagrant::::profiles=Primary Administrator # # does not exist in /etc/user_attr. TODO begin machine.communicate.execute( "#{machine.config.solaris.suexec_cmd} /usr/sbin/shutdown -y -i5 -g0") rescue IOError # Ignore, this probably means connection closed because it # shut down. end end end end end end vagrant-1.4.3/plugins/guests/solaris/cap/mount_virtualbox_shared_folder.rb000066400000000000000000000027311226132634600272320ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris module Cap class MountVirtualBoxSharedFolder def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) # These are just far easier to use than the full options syntax owner = options[:owner] group = options[:group] # Create the shared folder machine.communicate.execute("#{machine.config.solaris.suexec_cmd} mkdir -p #{guestpath}") if owner.is_a? Integer mount_uid = owner else # We have to use this `id` command instead of `/usr/bin/id` since this # one accepts the "-u" and "-g" flags. mount_uid = "`/usr/xpg4/bin/id -u #{owner}`" end if group.is_a? Integer mount_gid = group else mount_gid = "`/usr/xpg4/bin/id -g #{group}`" end # Mount the folder with the proper owner/group mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}" if options[:mount_options] mount_options += ",#{options[:mount_options].join(",")}" end machine.communicate.execute("#{machine.config.solaris.suexec_cmd} /sbin/mount -F vboxfs #{mount_options} #{name} #{guestpath}") # chown the folder to the proper owner/group machine.communicate.execute("#{machine.config.solaris.suexec_cmd} chown #{mount_uid}:#{mount_gid} #{guestpath}") end end end end end vagrant-1.4.3/plugins/guests/solaris/config.rb000066400000000000000000000007371226132634600214360ustar00rootroot00000000000000module VagrantPlugins module GuestSolaris class Config < Vagrant.plugin("2", :config) attr_accessor :halt_timeout attr_accessor :halt_check_interval # This sets the command to use to execute items as a superuser. sudo is default attr_accessor :suexec_cmd attr_accessor :device def initialize @halt_timeout = 30 @halt_check_interval = 1 @suexec_cmd = 'sudo' @device = "e1000g" end end end end vagrant-1.4.3/plugins/guests/solaris/guest.rb000066400000000000000000000005311226132634600213100ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSolaris # A general Vagrant system implementation for "solaris". # # Contributed by Blake Irvin class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep SunOS") end end end end vagrant-1.4.3/plugins/guests/solaris/plugin.rb000066400000000000000000000017451226132634600214670ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSolaris class Plugin < Vagrant.plugin("2") name "Solaris guest." description "Solaris guest support." config("solaris") do require File.expand_path("../config", __FILE__) Config end guest("solaris") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("solaris", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("solaris", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("solaris", "halt") do require_relative "cap/halt" Cap::Halt end guest_capability("solaris", "mount_virtualbox_shared_folder") do require_relative "cap/mount_virtualbox_shared_folder" Cap::MountVirtualBoxSharedFolder end end end end vagrant-1.4.3/plugins/guests/solaris11/000077500000000000000000000000001226132634600177775ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/solaris11/cap/000077500000000000000000000000001226132634600205425ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/solaris11/cap/change_host_name.rb000066400000000000000000000022441226132634600243530ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung module VagrantPlugins module GuestSolaris11 module Cap class ChangeHostName def self.change_host_name(machine, name) su_cmd = machine.config.solaris11.suexec_cmd # Only do this if the hostname is not already set if !machine.communicate.test("/usr/sbin/svccfg -s system/identity:node listprop config/nodename | /usr/bin/grep '#{name}'") #machine.communicate.execute("#{su_cmd} sh -c \"echo '#{name}' > /etc/nodename\"") #machine.communicate.execute("#{su_cmd} uname -S #{name}") machine.communicate.execute("#{su_cmd} /usr/sbin/svccfg -s system/identity:node setprop config/nodename=\"#{name}\"") machine.communicate.execute("#{su_cmd} /usr/sbin/svccfg -s system/identity:node setprop config/loopback=\"#{name}\"") machine.communicate.execute("#{su_cmd} /usr/sbin/svccfg -s system/identity:node refresh ") machine.communicate.execute("#{su_cmd} /usr/sbin/svcadm restart system/identity:node ") end end end end end end vagrant-1.4.3/plugins/guests/solaris11/cap/configure_networks.rb000066400000000000000000000030021226132634600247770ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung module VagrantPlugins module GuestSolaris11 module Cap class ConfigureNetworks def self.configure_networks(machine, networks) networks.each do |network| device = "#{machine.config.solaris11.device}#{network[:interface]}" su_cmd = machine.config.solaris11.suexec_cmd mask = "#{network[:netmask]}" cidr = mask.split(".").map { |e| e.to_i.to_s(2).rjust(8, "0") }.join.count("1").to_s #ifconfig_cmd = "#{su_cmd} /sbin/ifconfig #{device}" #machine.communicate.execute("#{ifconfig_cmd} plumb") if network[:type].to_sym == :static #machine.communicate.execute("#{ifconfig_cmd} inet #{network[:ip]} netmask #{network[:netmask]}") #machine.communicate.execute("#{ifconfig_cmd} up") #machine.communicate.execute("#{su_cmd} sh -c \"echo '#{network[:ip]}' > /etc/hostname.#{device}\"") # ipadm create-addr -T static -a local=172.16.10.15/24 net2/v4 machine.communicate.execute("#{su_cmd} ipadm create-addr -T static -a #{network[:ip]}/#{cidr} #{device}/v4") elsif network[:type].to_sym == :dhcp #machine.communicate.execute("#{ifconfig_cmd} dhcp start") machine.communicate.execute("#{su_cmd} ipadm create-addr -T addrconf #{device}/v4") end end end end end end end vagrant-1.4.3/plugins/guests/solaris11/cap/halt.rb000066400000000000000000000013421226132634600220170ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung module VagrantPlugins module GuestSolaris11 module Cap class Halt def self.halt(machine) # There should be an exception raised if the line # # vagrant::::profiles=Primary Administrator # # does not exist in /etc/user_attr. TODO begin machine.communicate.execute( "#{machine.config.solaris11.suexec_cmd} /usr/sbin/shutdown -y -i5 -g0") rescue IOError # Ignore, this probably means connection closed because it # shut down. end end end end end end vagrant-1.4.3/plugins/guests/solaris11/cap/mount_virtualbox_shared_folder.rb000066400000000000000000000031271226132634600273740ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung module VagrantPlugins module GuestSolaris11 module Cap class MountVirtualBoxSharedFolder def self.mount_virtualbox_shared_folder(machine, name, guestpath, options) # These are just far easier to use than the full options syntax owner = options[:owner] group = options[:group] # Create the shared folder machine.communicate.execute("#{machine.config.solaris11.suexec_cmd} mkdir -p #{guestpath}") if owner.is_a? Integer mount_uid = owner else # We have to use this `id` command instead of `/usr/bin/id` since this # one accepts the "-u" and "-g" flags. mount_uid = "`/usr/xpg4/bin/id -u #{owner}`" end if group.is_a? Integer mount_gid = group else mount_gid = "`/usr/xpg4/bin/id -g #{group}`" end # Mount the folder with the proper owner/group mount_options = "-o uid=#{mount_uid},gid=#{mount_gid}" if options[:mount_options] mount_options += ",#{options[:mount_options].join(",")}" end machine.communicate.execute("#{machine.config.solaris11.suexec_cmd} /sbin/mount -F vboxfs #{mount_options} #{name} #{guestpath}") # chown the folder to the proper owner/group machine.communicate.execute("#{machine.config.solaris11.suexec_cmd} chown #{mount_uid}:#{mount_gid} #{guestpath}") end end end end end vagrant-1.4.3/plugins/guests/solaris11/config.rb000066400000000000000000000011241226132634600215670ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung module VagrantPlugins module GuestSolaris11 class Config < Vagrant.plugin("2", :config) attr_accessor :halt_timeout attr_accessor :halt_check_interval # This sets the command to use to execute items as a superuser. sudo is default attr_accessor :suexec_cmd attr_accessor :device def initialize @halt_timeout = 30 @halt_check_interval = 1 @suexec_cmd = 'sudo' @device = "net" end end end end vagrant-1.4.3/plugins/guests/solaris11/guest.rb000066400000000000000000000005261226132634600214560ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung require "vagrant" module VagrantPlugins module GuestSolaris11 class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("uname -s | grep SunOS") end end end end vagrant-1.4.3/plugins/guests/solaris11/plugin.rb000066400000000000000000000021571226132634600216270ustar00rootroot00000000000000# A general Vagrant system implementation for "solaris 11". # # Contributed by Jan Thomas Moldung require "vagrant" module VagrantPlugins module GuestSolaris11 class Plugin < Vagrant.plugin("2") name "Solaris 11 guest." description "Solaris 11 guest support." config("solaris11") do require File.expand_path("../config", __FILE__) Config end guest("solaris11") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("solaris11", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("solaris11", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("solaris11", "halt") do require_relative "cap/halt" Cap::Halt end guest_capability("solaris11", "mount_virtualbox_shared_folder") do require_relative "cap/mount_virtualbox_shared_folder" Cap::MountVirtualBoxSharedFolder end end end end vagrant-1.4.3/plugins/guests/suse/000077500000000000000000000000001226132634600171405ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/suse/cap/000077500000000000000000000000001226132634600177035ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/suse/cap/change_host_name.rb000066400000000000000000000011151226132634600235100ustar00rootroot00000000000000module VagrantPlugins module GuestSuse module Cap class ChangeHostName def self.change_host_name(machine, name) machine.communicate.tap do |comm| # Only do this if the hostname is not already set if !comm.test("sudo hostname | grep '#{name}'") comm.sudo("echo #{name} > /etc/HOSTNAME") comm.sudo("hostname #{name}") comm.sudo("sed -i 's@^\\(127[.]0[.]0[.]1[[:space:]]\\+\\)@\\1#{name} #{name.split('.')[0]} @' /etc/hosts") end end end end end end end vagrant-1.4.3/plugins/guests/suse/cap/configure_networks.rb000066400000000000000000000051101226132634600241420ustar00rootroot00000000000000require "set" require "tempfile" require "vagrant/util/retryable" require "vagrant/util/template_renderer" module VagrantPlugins module GuestSuse module Cap class ConfigureNetworks extend Vagrant::Util::Retryable include Vagrant::Util def self.configure_networks(machine, networks) network_scripts_dir = machine.guest.capability("network_scripts_dir") # Accumulate the configurations to add to the interfaces file as # well as what interfaces we're actually configuring since we use that # later. interfaces = Set.new networks.each do |network| interfaces.add(network[:interface]) # Remove any previous vagrant configuration in this network interface's # configuration files. machine.communicate.sudo("touch #{network_scripts_dir}/ifcfg-eth#{network[:interface]}") machine.communicate.sudo("sed -e '/^#VAGRANT-BEGIN/,/^#VAGRANT-END/ d' #{network_scripts_dir}/ifcfg-eth#{network[:interface]} > /tmp/vagrant-ifcfg-eth#{network[:interface]}") machine.communicate.sudo("cat /tmp/vagrant-ifcfg-eth#{network[:interface]} > #{network_scripts_dir}/ifcfg-eth#{network[:interface]}") machine.communicate.sudo("rm /tmp/vagrant-ifcfg-eth#{network[:interface]}") # Render and upload the network entry file to a deterministic # temporary location. entry = TemplateRenderer.render("guests/suse/network_#{network[:type]}", :options => network) temp = Tempfile.new("vagrant") temp.binmode temp.write(entry) temp.close machine.communicate.upload(temp.path, "/tmp/vagrant-network-entry_#{network[:interface]}") end # Bring down all the interfaces we're reconfiguring. By bringing down # each specifically, we avoid reconfiguring eth0 (the NAT interface) so # SSH never dies. interfaces.each do |interface| retryable(:on => Vagrant::Errors::VagrantError, :tries => 3, :sleep => 2) do machine.communicate.sudo("/sbin/ifdown eth#{interface} 2> /dev/null", :error_check => false) machine.communicate.sudo("cat /tmp/vagrant-network-entry_#{interface} >> #{network_scripts_dir}/ifcfg-eth#{interface}") machine.communicate.sudo("/sbin/ifup eth#{interface} 2> /dev/null") end machine.communicate.sudo("rm /tmp/vagrant-network-entry_#{interface}") end end end end end end vagrant-1.4.3/plugins/guests/suse/cap/network_scripts_dir.rb000066400000000000000000000003201226132634600243210ustar00rootroot00000000000000module VagrantPlugins module GuestSuse module Cap class NetworkScriptsDir def self.network_scripts_dir(machine) "/etc/sysconfig/network/" end end end end end vagrant-1.4.3/plugins/guests/suse/guest.rb000066400000000000000000000003331226132634600206130ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSuse class Guest < Vagrant.plugin("2", :guest) def detect?(machine) machine.communicate.test("cat /etc/SuSE-release") end end end end vagrant-1.4.3/plugins/guests/suse/plugin.rb000066400000000000000000000013351226132634600207650ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestSuse class Plugin < Vagrant.plugin("2") name "SUSE guest" description "SUSE guest support." guest("suse", "redhat") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("suse", "change_host_name") do require_relative "cap/change_host_name" Cap::ChangeHostName end guest_capability("suse", "configure_networks") do require_relative "cap/configure_networks" Cap::ConfigureNetworks end guest_capability("suse", "network_scripts_dir") do require_relative "cap/network_scripts_dir" Cap::NetworkScriptsDir end end end end vagrant-1.4.3/plugins/guests/ubuntu/000077500000000000000000000000001226132634600175035ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/ubuntu/cap/000077500000000000000000000000001226132634600202465ustar00rootroot00000000000000vagrant-1.4.3/plugins/guests/ubuntu/cap/change_host_name.rb000066400000000000000000000013061226132634600240550ustar00rootroot00000000000000module VagrantPlugins module GuestUbuntu module Cap class ChangeHostName < VagrantPlugins::GuestDebian::Cap::ChangeHostName def self.change_host_name(machine, name) super end def refresh_hostname_service if hardy? # hostname.sh returns 1, so use `true` to get a 0 exitcode sudo("/etc/init.d/hostname.sh start; true") else sudo("service hostname start") end end def hardy? machine.communicate.test("[ `lsb_release -c -s` = hardy ]") end def renew_dhcp sudo("ifdown -a; ifup -a; ifup -a --allow=hotplug") end end end end end vagrant-1.4.3/plugins/guests/ubuntu/guest.rb000066400000000000000000000004561226132634600211640ustar00rootroot00000000000000require "vagrant" require Vagrant.source_root.join("plugins/guests/debian/guest") module VagrantPlugins module GuestUbuntu class Guest < VagrantPlugins::GuestDebian::Guest def detect?(machine) machine.communicate.test("cat /etc/issue | grep 'Ubuntu'") end end end end vagrant-1.4.3/plugins/guests/ubuntu/plugin.rb000066400000000000000000000011151226132634600213240ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module GuestUbuntu class Plugin < Vagrant.plugin("2") name "Ubuntu guest" description "Ubuntu guest support." guest("ubuntu", "debian") do require File.expand_path("../guest", __FILE__) Guest end guest_capability("ubuntu", "change_host_name") do # ubuntu is just just a specialization of the debian code for this capability require_relative "../debian/cap/change_host_name" require_relative "cap/change_host_name" Cap::ChangeHostName end end end end vagrant-1.4.3/plugins/hosts/000077500000000000000000000000001226132634600160075ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/arch/000077500000000000000000000000001226132634600167245ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/arch/host.rb000066400000000000000000000025441226132634600202330ustar00rootroot00000000000000require "vagrant" require Vagrant.source_root.join("plugins/hosts/linux/host") module VagrantPlugins module HostArch class Host < VagrantPlugins::HostLinux::Host def self.match? File.exist?("/etc/arch-release") end def self.nfs? # HostLinux checks for nfsd which returns false unless the # services are actively started. This leads to a misleading # error message. Checking for nfs (no d) seems to work # regardless. Also fixes useless use of cat, regex, and # redirection. Kernel.system("grep -Fq nfs /proc/filesystems") end # Normal, mid-range precedence. def self.precedence 5 end def initialize(*args) super if systemd? @nfs_check_command = "/usr/sbin/systemctl status nfsd" @nfs_start_command = "/usr/sbin/systemctl start nfsd rpc-idmapd rpc-mountd rpcbind" else @nfs_check_command = "/etc/rc.d/nfs-server status" @nfs_start_command = "sh -c 'for s in {rpcbind,nfs-common,nfs-server}; do /etc/rc.d/$s start; done'" end end protected # This tests to see if systemd is used on the system. This is used # in newer versions of Arch, and requires a change in behavior. def systemd? `ps -o comm= 1`.chomp == 'systemd' end end end end vagrant-1.4.3/plugins/hosts/arch/plugin.rb000066400000000000000000000004261226132634600205510ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostArch class Plugin < Vagrant.plugin("2") name "Arch host" description "Arch host support." host("arch") do require File.expand_path("../host", __FILE__) Host end end end end vagrant-1.4.3/plugins/hosts/bsd/000077500000000000000000000000001226132634600165575ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/bsd/host.rb000066400000000000000000000132461226132634600200670ustar00rootroot00000000000000require 'log4r' require "vagrant" require 'vagrant/util/platform' require "vagrant/util/subprocess" module VagrantPlugins module HostBSD # Represents a BSD host, such as FreeBSD and Darwin (Mac OS X). class Host < Vagrant.plugin("2", :host) include Vagrant::Util include Vagrant::Util::Retryable def self.match? Vagrant::Util::Platform.darwin? || Vagrant::Util::Platform.bsd? end def self.precedence # Set a lower precedence because this is a generic OS. We # want specific distros to match first. 2 end def initialize(*args) super @logger = Log4r::Logger.new("vagrant::hosts::bsd") @nfs_restart_command = "sudo nfsd restart" @nfs_exports_template = "nfs/exports" end def nfs? retryable(:tries => 10, :on => TypeError) do system("which nfsd > /dev/null 2>&1") end end def nfs_export(id, ips, folders) nfs_checkexports! if File.file?("/etc/exports") # We need to build up mapping of directories that are enclosed # within each other because the exports file has to have subdirectories # of an exported directory on the same line. e.g.: # # "/foo" "/foo/bar" ... # "/bar" # # We build up this mapping within the following hash. @logger.debug("Compiling map of sub-directories for NFS exports...") dirmap = {} folders.each do |_, opts| hostpath = opts[:hostpath].dup hostpath.gsub!('"', '\"') found = false dirmap.each do |dirs, diropts| dirs.each do |dir| if dir.start_with?(hostpath) || hostpath.start_with?(dir) # TODO: verify opts and diropts are _identical_, raise an error # if not. NFS mandates subdirectories have identical options. dirs << hostpath found = true break end end break if found end if !found dirmap[[hostpath]] = opts.dup end end # Sort all the keys by length so that the directory closest to # the root is exported first. dirmap.each do |dirs, _| dirs.sort_by! { |d| d.length } end # Setup the NFS options dirmap.each do |dirs, opts| if !opts[:bsd__nfs_options] opts[:bsd__nfs_options] = ["alldirs"] end hasmapall = false opts[:bsd__nfs_options].each do |opt| # mapall/maproot are mutually exclusive, so we have to check # for both here. if opt =~ /^mapall=/ || opt =~ /^maproot=/ hasmapall = true break end end if !hasmapall opts[:bsd__nfs_options] << "mapall=#{opts[:map_uid]}:#{opts[:map_gid]}" end opts[:bsd__compiled_nfs_options] = opts[:bsd__nfs_options].map do |opt| "-#{opt}" end.join(" ") end @logger.info("Exporting the following for NFS...") dirmap.each do |dirs, opts| @logger.info("NFS DIR: #{dirs.inspect}") @logger.info("NFS OPTS: #{opts.inspect}") end output = TemplateRenderer.render(@nfs_exports_template, :uuid => id, :ips => ips, :folders => dirmap, :user => Process.uid) # The sleep ensures that the output is truly flushed before any `sudo` # commands are issued. @ui.info I18n.t("vagrant.hosts.bsd.nfs_export") sleep 0.5 # First, clean up the old entry nfs_cleanup(id) # Output the rendered template into the exports output.split("\n").each do |line| line.gsub!('"', '\"') line.gsub!("'", "'\\\\''") system(%Q[sudo -s -- "echo '#{line}' >> /etc/exports"]) end # We run restart here instead of "update" just in case nfsd # is not starting system(@nfs_restart_command) end def nfs_prune(valid_ids) return if !File.exist?("/etc/exports") @logger.info("Pruning invalid NFS entries...") output = false user = Process.uid File.read("/etc/exports").lines.each do |line| if id = line[/^# VAGRANT-BEGIN:( #{user})? ([A-Za-z0-9-]+?)$/, 2] if valid_ids.include?(id) @logger.debug("Valid ID: #{id}") else if !output # We want to warn the user but we only want to output once @ui.info I18n.t("vagrant.hosts.bsd.nfs_prune") output = true end @logger.info("Invalid ID, pruning: #{id}") nfs_cleanup(id) end end end rescue Errno::EACCES raise Vagrant::Errors::NFSCantReadExports end protected def nfs_checkexports! r = Subprocess.execute("nfsd", "checkexports") if r.exit_code != 0 raise Vagrant::Errors::NFSBadExports, output: r.stderr end end def nfs_cleanup(id) return if !File.exist?("/etc/exports") # Escape sed-sensitive characters: id = id.gsub("/", "\\/") id = id.gsub(".", "\\.") user = Process.uid # Use sed to just strip out the block of code which was inserted # by Vagrant, and restart NFS. system("sudo sed -E -e '/^# VAGRANT-BEGIN:( #{user})? #{id}/,/^# VAGRANT-END:( #{user})? #{id}/ d' -ibak /etc/exports") end end end end vagrant-1.4.3/plugins/hosts/bsd/plugin.rb000066400000000000000000000004221226132634600204000ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostBSD class Plugin < Vagrant.plugin("2") name "BSD host" description "BSD host support." host("bsd") do require File.expand_path("../host", __FILE__) Host end end end end vagrant-1.4.3/plugins/hosts/freebsd/000077500000000000000000000000001226132634600174215ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/freebsd/host.rb000066400000000000000000000017361226132634600207320ustar00rootroot00000000000000require "vagrant" require 'vagrant/util/platform' require Vagrant.source_root.join("plugins/hosts/bsd/host") module VagrantPlugins module HostFreeBSD class Host < VagrantPlugins::HostBSD::Host class FreeBSDHostError < Vagrant::Errors::VagrantError error_namespace("vagrant.hosts.freebsd") end include Vagrant::Util include Vagrant::Util::Retryable def self.match? Vagrant::Util::Platform.freebsd? end # Normal, mid-range precedence. def self.precedence 5 end def nfs_export(id, ip, folders) folders.each do |folder_name, folder_values| if folder_values[:hostpath] =~ /\s+/ raise FreeBSDHostError, :_key => :nfs_whitespace end end super end def initialize(*args) super @nfs_restart_command = "sudo /etc/rc.d/mountd onereload" @nfs_exports_template = "nfs/exports_freebsd" end end end end vagrant-1.4.3/plugins/hosts/freebsd/plugin.rb000066400000000000000000000004421226132634600212440ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostFreeBSD class Plugin < Vagrant.plugin("2") name "FreeBSD host" description "FreeBSD host support." host("freebsd") do require File.expand_path("../host", __FILE__) Host end end end end vagrant-1.4.3/plugins/hosts/gentoo/000077500000000000000000000000001226132634600173025ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/gentoo/host.rb000066400000000000000000000020121226132634600205770ustar00rootroot00000000000000require "vagrant" require "vagrant/util/subprocess" require Vagrant.source_root.join("plugins/hosts/linux/host") module VagrantPlugins module HostGentoo class Host < VagrantPlugins::HostLinux::Host def self.match? return File.exists?("/etc/gentoo-release") end # Normal, mid-range precedence. def self.precedence 5 end def initialize(*args) super @nfs_apply_command = "/usr/sbin/exportfs -r" if systemd? @nfs_check_command = "/usr/bin/systemctl status nfsd" @nfs_start_command = "/usr/bin/systemctl start nfsd rpc-mountd rpcbind" else @nfs_check_command = "/etc/init.d/nfs status" @nfs_start_command = "/etc/init.d/nfs restart" end end protected # Check for systemd presence from current processes. def systemd? result = Vagrant::Util::Subprocess.execute("ps", "-o", "comm=", "1") return result.stdout.chomp == "systemd" end end end end vagrant-1.4.3/plugins/hosts/gentoo/plugin.rb000066400000000000000000000004361226132634600211300ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostGentoo class Plugin < Vagrant.plugin("2") name "Gentoo host" description "Gentoo host support." host("gentoo") do require File.expand_path("../host", __FILE__) Host end end end end vagrant-1.4.3/plugins/hosts/linux/000077500000000000000000000000001226132634600171465ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/linux/host.rb000066400000000000000000000075051226132634600204570ustar00rootroot00000000000000require 'log4r' require "vagrant" require 'vagrant/util/platform' module VagrantPlugins module HostLinux # Represents a Linux based host, such as Ubuntu. class Host < Vagrant.plugin("2", :host) include Vagrant::Util include Vagrant::Util::Retryable def self.match? Vagrant::Util::Platform.linux? end def self.precedence # Set a lower precedence because this is a generic OS. We # want specific distros to match first. 2 end def initialize(*args) super @logger = Log4r::Logger.new("vagrant::hosts::linux") @nfs_apply_command = "/usr/sbin/exportfs -r" @nfs_check_command = "/etc/init.d/nfs-kernel-server status" @nfs_start_command = "/etc/init.d/nfs-kernel-server start" end def nfs? retryable(:tries => 10, :on => TypeError) do # Check procfs to see if NFSd is a supported filesystem system("cat /proc/filesystems | grep nfsd > /dev/null 2>&1") end end def nfs_opts_setup(folders) folders.each do |k, opts| if !opts[:linux__nfs_options] opts[:linux__nfs_options] ||= ["rw", "no_subtree_check", "all_squash"] end # Only automatically set anonuid/anongid if they weren't # explicitly set by the user. hasgid = false hasuid = false opts[:linux__nfs_options].each do |opt| hasgid = !!(opt =~ /^anongid=/) if !hasgid hasuid = !!(opt =~ /^anonuid=/) if !hasuid end opts[:linux__nfs_options] << "anonuid=#{opts[:map_uid]}" if !hasuid opts[:linux__nfs_options] << "anongid=#{opts[:map_gid]}" if !hasgid opts[:linux__nfs_options] << "fsid=#{opts[:uuid]}" end end def nfs_export(id, ips, folders) nfs_opts_setup(folders) output = TemplateRenderer.render('nfs/exports_linux', :uuid => id, :ips => ips, :folders => folders, :user => Process.uid) @ui.info I18n.t("vagrant.hosts.linux.nfs_export") sleep 0.5 nfs_cleanup(id) output.split("\n").each do |line| # This should only ask for administrative permission once, even # though its executed in multiple subshells. system(%Q[sudo su root -c "echo '#{line}' >> /etc/exports"]) end if nfs_running? system("sudo #{@nfs_apply_command}") else system("sudo #{@nfs_start_command}") end end def nfs_prune(valid_ids) return if !File.exist?("/etc/exports") @logger.info("Pruning invalid NFS entries...") output = false user = Process.uid File.read("/etc/exports").lines.each do |line| if id = line[/^# VAGRANT-BEGIN:( #{user})? ([A-Za-z0-9-]+?)$/, 2] if valid_ids.include?(id) @logger.debug("Valid ID: #{id}") else if !output # We want to warn the user but we only want to output once @ui.info I18n.t("vagrant.hosts.linux.nfs_prune") output = true end @logger.info("Invalid ID, pruning: #{id}") nfs_cleanup(id) end end end end protected def nfs_running? system("#{@nfs_check_command}") end def nfs_cleanup(id) return if !File.exist?("/etc/exports") user = Process.uid # Use sed to just strip out the block of code which was inserted # by Vagrant system("sudo sed -r -e '/^# VAGRANT-BEGIN:( #{user})? #{id}/,/^# VAGRANT-END:( #{user})? #{id}/ d' -ibak /etc/exports") end end end end vagrant-1.4.3/plugins/hosts/linux/plugin.rb000066400000000000000000000004321226132634600207700ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostLinux class Plugin < Vagrant.plugin("2") name "Linux host" description "Linux host support." host("linux") do require File.expand_path("../host", __FILE__) Host end end end end vagrant-1.4.3/plugins/hosts/opensuse/000077500000000000000000000000001226132634600176505ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/opensuse/host.rb000066400000000000000000000013671226132634600211610ustar00rootroot00000000000000require "pathname" require "vagrant" require Vagrant.source_root.join("plugins/hosts/linux/host") module VagrantPlugins module HostOpenSUSE class Host < VagrantPlugins::HostLinux::Host def self.match? release_file = Pathname.new("/etc/SuSE-release") if release_file.exist? release_file.open("r") do |f| return true if f.gets =~ /^openSUSE/ end end false end # Normal, mid-range precedence. def self.precedence 5 end def initialize(*args) super @nfs_apply_command = "/usr/sbin/exportfs -r" @nfs_check_command = "service nfsserver status" @nfs_start_command = "service nfsserver start" end end end end vagrant-1.4.3/plugins/hosts/opensuse/plugin.rb000066400000000000000000000004461226132634600214770ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostOpenSUSE class Plugin < Vagrant.plugin("2") name "OpenSUSE host" description "OpenSUSE host support." host("opensuse") do require File.expand_path("../host", __FILE__) Host end end end end vagrant-1.4.3/plugins/hosts/redhat/000077500000000000000000000000001226132634600172565ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/redhat/host.rb000066400000000000000000000037131226132634600205640ustar00rootroot00000000000000require "pathname" require "vagrant" require Vagrant.source_root.join("plugins/hosts/linux/host") module VagrantPlugins module HostRedHat class Host < VagrantPlugins::HostLinux::Host def self.match? release_file = Pathname.new("/etc/redhat-release") if release_file.exist? release_file.open("r:ISO-8859-1:UTF-8") do |f| contents = f.gets return true if contents =~ /^Fedora/ # Fedora return true if contents =~ /^CentOS/ # CentOS return true if contents =~ /^Enterprise Linux Enterprise Linux/ # Oracle Linux < 5.3 return true if contents =~ /^Red Hat Enterprise Linux/ # Red Hat Enterprise Linux and Oracle Linux >= 5.3 end end false end # Normal, mid-range precedence. def self.precedence 5 end def initialize(*args) super nfs_server_binary = "/etc/init.d/nfs" # On Fedora 16+, systemd replaced init.d, so we have to use the # proper NFS binary. This checks to see if we need to do that. release_file = Pathname.new("/etc/redhat-release") begin release_file.open("r:ISO-8859-1:UTF-8") do |f| fedora_match = /Fedora.* release ([0-9]+)/.match(f.gets) if fedora_match version_number = fedora_match[1].to_i if version_number >= 16 # "service nfs-server" will redirect properly to systemctl # when "service nfs-server restart" is called. nfs_server_binary = "/usr/sbin/service nfs-server" end end end rescue Errno::ENOENT # File doesn't exist, not a big deal, assume we're on a # lower version. end @nfs_apply_command = "/usr/sbin/exportfs -r" @nfs_check_command = "#{nfs_server_binary} status" @nfs_start_command = "#{nfs_server_binary} start" end end end end vagrant-1.4.3/plugins/hosts/redhat/plugin.rb000066400000000000000000000004401226132634600210770ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostRedHat class Plugin < Vagrant.plugin("2") name "Red Hat host" description "Red Hat host support." host("redhat") do require File.expand_path("../host", __FILE__) Host end end end end vagrant-1.4.3/plugins/hosts/slackware/000077500000000000000000000000001226132634600177635ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/slackware/host.rb000066400000000000000000000011651226132634600212700ustar00rootroot00000000000000require "vagrant" require Vagrant.source_root.join("plugins/hosts/linux/host") module VagrantPlugins module HostSlackware class Host < VagrantPlugins::HostLinux::Host def self.match? return File.exists?("/etc/slackware-release") || Dir.glob("/usr/lib/setup/Plamo-*").length > 0 end # Normal, mid-range precedence. def self.precedence 5 end def initialize(*args) super @nfs_apply_command = "/usr/sbin/exportfs -r" @nfs_check_command = "pidof nfsd > /dev/null" @nfs_start_command = "/etc/rc.d/rc.nfsd start" end end end end vagrant-1.4.3/plugins/hosts/slackware/plugin.rb000066400000000000000000000004731226132634600216120ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostSlackware class Plugin < Vagrant.plugin("2") name "Slackware host" description "Slackware and derivertives host support." host("slackware") do require File.expand_path("../host", __FILE__) Host end end end end vagrant-1.4.3/plugins/hosts/windows/000077500000000000000000000000001226132634600175015ustar00rootroot00000000000000vagrant-1.4.3/plugins/hosts/windows/host.rb000066400000000000000000000004621226132634600210050ustar00rootroot00000000000000require "vagrant" require 'vagrant/util/platform' module VagrantPlugins module HostWindows class Host < Vagrant.plugin("2", :host) def self.match? Vagrant::Util::Platform.windows? end # Windows does not support NFS def nfs? false end end end end vagrant-1.4.3/plugins/hosts/windows/plugin.rb000066400000000000000000000004421226132634600213240ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module HostWindows class Plugin < Vagrant.plugin("2") name "Windows host" description "Windows host support." host("windows") do require File.expand_path("../host", __FILE__) Host end end end end vagrant-1.4.3/plugins/kernel_v1/000077500000000000000000000000001226132634600165355ustar00rootroot00000000000000vagrant-1.4.3/plugins/kernel_v1/config/000077500000000000000000000000001226132634600200025ustar00rootroot00000000000000vagrant-1.4.3/plugins/kernel_v1/config/nfs.rb000066400000000000000000000006671226132634600211260ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 class NFSConfig < Vagrant.plugin("1", :config) attr_accessor :map_uid attr_accessor :map_gid def initialize @map_uid = UNSET_VALUE @map_gid = UNSET_VALUE end def upgrade(new) new.nfs.map_uid = @map_uid if @map_uid != UNSET_VALUE new.nfs.map_gid = @map_gid if @map_gid != UNSET_VALUE end end end end vagrant-1.4.3/plugins/kernel_v1/config/package.rb000066400000000000000000000004661226132634600217300ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 class PackageConfig < Vagrant.plugin("1", :config) attr_accessor :name def initialize @name = UNSET_VALUE end def upgrade(new) new.package.name = @name if @name != UNSET_VALUE end end end end vagrant-1.4.3/plugins/kernel_v1/config/ssh.rb000066400000000000000000000031341226132634600211250ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 class SSHConfig < Vagrant.plugin("1", :config) attr_accessor :username attr_accessor :password attr_accessor :host attr_accessor :port attr_accessor :guest_port attr_accessor :max_tries attr_accessor :timeout attr_accessor :private_key_path attr_accessor :forward_agent attr_accessor :forward_x11 attr_accessor :shell def initialize @username = UNSET_VALUE @password = UNSET_VALUE @host = UNSET_VALUE @port = UNSET_VALUE @guest_port = UNSET_VALUE @max_tries = UNSET_VALUE @timeout = UNSET_VALUE @private_key_path = UNSET_VALUE @forward_agent = UNSET_VALUE @forward_x11 = UNSET_VALUE @shell = UNSET_VALUE end def upgrade(new) new.ssh.username = @username if @username != UNSET_VALUE new.ssh.host = @host if @host != UNSET_VALUE new.ssh.port = @port if @port != UNSET_VALUE new.ssh.guest_port = @guest_port if @guest_port != UNSET_VALUE new.ssh.private_key_path = @private_key_path if @private_key_path != UNSET_VALUE new.ssh.forward_agent = @forward_agent if @forward_agent != UNSET_VALUE new.ssh.forward_x11 = @forward_x11 if @forward_x11 != UNSET_VALUE new.ssh.shell = @shell if @shell != UNSET_VALUE # TODO: Warn that max_tries and timeout are gone end end end end vagrant-1.4.3/plugins/kernel_v1/config/vagrant.rb000066400000000000000000000012351226132634600217720ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 class VagrantConfig < Vagrant.plugin("1", :config) attr_accessor :dotfile_name attr_accessor :host def initialize @dotfile_name = UNSET_VALUE @host = UNSET_VALUE end def finalize! @dotfile_name = nil if @dotfile_name == UNSET_VALUE @host = nil if @host == UNSET_VALUE end def upgrade(new) new.vagrant.host = @host if @host.nil? warnings = [] if @dotfile_name warnings << "`config.vm.dotfile_name` has no effect anymore." end [warnings, []] end end end end vagrant-1.4.3/plugins/kernel_v1/config/vm.rb000066400000000000000000000136321226132634600207560ustar00rootroot00000000000000module VagrantPlugins module Kernel_V1 # This is the Version 1.0.x Vagrant VM configuration. This is # _outdated_ and exists purely to be upgraded over to the new V2 # format. class VMConfig < Vagrant.plugin("1", :config) DEFAULT_VM_NAME = :default attr_accessor :name attr_accessor :auto_port_range attr_accessor :base_mac attr_accessor :boot_mode attr_accessor :box attr_accessor :box_url attr_accessor :guest attr_accessor :host_name attr_reader :customizations attr_reader :networks attr_reader :provisioners attr_reader :shared_folders def initialize @shared_folders = {} @networks = [] @provisioners = [] @customizations = [] @define_calls = [] end def forward_port(guestport, hostport, options=nil) options ||= {} # Build up the network options for V2 network_options = {} network_options[:virtualbox__adapter] = options[:adapter] network_options[:virtualbox__protocol] = options[:protocol] # Just append the forwarded port to the networks @networks << [:forwarded_port, [guestport, hostport, network_options]] end def share_folder(name, guestpath, hostpath, opts=nil) @shared_folders[name] = { :guestpath => guestpath.to_s, :hostpath => hostpath.to_s, :create => false, :owner => nil, :group => nil, :nfs => false, :transient => false, :extra => nil }.merge(opts || {}) end def network(type, *args) # Convert to symbol so we can allow most anything... type = type.to_sym if type if type == :hostonly @networks << [:private_network, args] elsif type == :bridged @networks << [:public_network, args] else @networks << [:unknown, type] end end def provision(name, options=nil, &block) @provisioners << [name, options, block] end # This argument is nil only because the old style was deprecated and # we didn't want to break Vagrantfiles. This was never removed and # since we've moved onto V2 configuration, we might as well keep this # around forever. def customize(command=nil) @customizations << command if command end def define(name, options=nil, &block) # Force the V1 config on these calls options ||= {} options[:config_version] = "1" @define_calls << [name, options, block] end def finalize! # If we haven't defined a single VM, then we need to define a # default VM which just inherits the rest of the configuration. define(DEFAULT_VM_NAME) if defined_vm_keys.empty? end # Upgrade to a V2 configuration def upgrade(new) warnings = [] new.vm.base_mac = self.base_mac if self.base_mac new.vm.box = self.box if self.box new.vm.box_url = self.box_url if self.box_url new.vm.guest = self.guest if self.guest new.vm.hostname = self.host_name if self.host_name new.vm.usable_port_range = self.auto_port_range if self.auto_port_range if self.boot_mode new.vm.provider :virtualbox do |vb| # Enable the GUI if the boot mode is GUI. vb.gui = (self.boot_mode.to_s == "gui") end end # If we have VM customizations, then we enable them on the # VirtualBox provider on the new VM. if !self.customizations.empty? warnings << "`config.vm.customize` calls are VirtualBox-specific. If you're\n" + "using any other provider, you'll have to use config.vm.provider in a\n" + "v2 configuration block." new.vm.provider :virtualbox do |vb| self.customizations.each do |customization| vb.customize(customization) end end end # Re-define all networks. self.networks.each do |type, args| if type == :unknown warnings << "Unknown network type '#{args}' will be ignored." next end options = {} options = args.pop.dup if args.last.is_a?(Hash) # Determine the extra options we need to set for each type if type == :forwarded_port options[:guest] = args[0] options[:host] = args[1] elsif type == :private_network options[:ip] = args[0] end new.vm.network(type, options) end # Provisioners self.provisioners.each do |name, options, block| new.vm.provision(name, options, &block) end # Shared folders self.shared_folders.each do |name, sf| options = sf.dup options[:id] = name guestpath = options.delete(:guestpath) hostpath = options.delete(:hostpath) # This was the name of the old default /vagrant shared folder. # We warn the use that this changed, but also silently change # it to try to make things work properly. if options[:id] == "v-root" warnings << "The 'v-root' shared folders have been renamed to 'vagrant-root'.\n" + "Assuming you meant 'vagrant-root'..." options[:id] = "vagrant-root" end new.vm.synced_folder(hostpath, guestpath, options) end # Defined sub-VMs @define_calls.each do |name, options, block| new.vm.define(name, options, &block) end # If name is used, warn that it has no effect anymore if @name warnings << "`config.vm.name` has no effect anymore. Names are derived\n" + "directly from any `config.vm.define` calls." end [warnings, []] end end end end vagrant-1.4.3/plugins/kernel_v1/plugin.rb000066400000000000000000000023751226132634600203670ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V1 # This is the "kernel" of Vagrant and contains the configuration classes # that make up the core of Vagrant. class Plugin < Vagrant.plugin("1") name "kernel" description <<-DESC The kernel of Vagrant. This plugin contains required items for even basic functionality of Vagrant version 1. DESC # Core configuration keys provided by the kernel. Note that all # the kernel configuration classes are marked as _upgrade safe_ (the # true 2nd param). This means that these can be loaded in ANY version # of the core of Vagrant. config("ssh", true) do require File.expand_path("../config/ssh", __FILE__) SSHConfig end config("nfs", true) do require File.expand_path("../config/nfs", __FILE__) NFSConfig end config("package", true) do require File.expand_path("../config/package", __FILE__) PackageConfig end config("vagrant", true) do require File.expand_path("../config/vagrant", __FILE__) VagrantConfig end config("vm", true) do require File.expand_path("../config/vm", __FILE__) VMConfig end end end end vagrant-1.4.3/plugins/kernel_v2/000077500000000000000000000000001226132634600165365ustar00rootroot00000000000000vagrant-1.4.3/plugins/kernel_v2/config/000077500000000000000000000000001226132634600200035ustar00rootroot00000000000000vagrant-1.4.3/plugins/kernel_v2/config/package.rb000066400000000000000000000003131226132634600217200ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V2 class PackageConfig < Vagrant.plugin("2", :config) attr_accessor :name def to_s "Package" end end end end vagrant-1.4.3/plugins/kernel_v2/config/ssh.rb000066400000000000000000000033701226132634600211300ustar00rootroot00000000000000require "vagrant" require_relative "ssh_connect" module VagrantPlugins module Kernel_V2 class SSHConfig < SSHConnectConfig attr_accessor :forward_agent attr_accessor :forward_x11 attr_accessor :guest_port attr_accessor :keep_alive attr_accessor :shell attr_accessor :proxy_command attr_accessor :pty attr_reader :default def initialize super @forward_agent = UNSET_VALUE @forward_x11 = UNSET_VALUE @guest_port = UNSET_VALUE @keep_alive = UNSET_VALUE @proxy_command = UNSET_VALUE @pty = UNSET_VALUE @shell = UNSET_VALUE @default = SSHConnectConfig.new end def merge(other) super.tap do |result| merged_defaults = @default.merge(other.default) result.instance_variable_set(:@default, merged_defaults) end end def finalize! super @forward_agent = false if @forward_agent == UNSET_VALUE @forward_x11 = false if @forward_x11 == UNSET_VALUE @guest_port = nil if @guest_port == UNSET_VALUE @keep_alive = false if @keep_alive == UNSET_VALUE @proxy_command = nil if @proxy_command == UNSET_VALUE @pty = false if @pty == UNSET_VALUE @shell = nil if @shell == UNSET_VALUE @default.finalize! end def to_s "SSH" end def validate(machine) errors = super # Return the errors result = { to_s => errors } # Figure out the errors for the defaults default_errors = @default.validate(machine) result["SSH Defaults"] = default_errors if !default_errors.empty? result end end end end vagrant-1.4.3/plugins/kernel_v2/config/ssh_connect.rb000066400000000000000000000027301226132634600226400ustar00rootroot00000000000000module VagrantPlugins module Kernel_V2 class SSHConnectConfig < Vagrant.plugin("2", :config) attr_accessor :host attr_accessor :port attr_accessor :private_key_path attr_accessor :username def initialize @host = UNSET_VALUE @port = UNSET_VALUE @private_key_path = UNSET_VALUE @username = UNSET_VALUE end def finalize! @host = nil if @host == UNSET_VALUE @port = nil if @port == UNSET_VALUE @private_key_path = nil if @private_key_path == UNSET_VALUE @username = nil if @username == UNSET_VALUE if @private_key_path && !@private_key_path.is_a?(Array) @private_key_path = [@private_key_path] end end # NOTE: This is _not_ a valid config validation method, since it # returns an _array_ of strings rather than a Hash. This is meant to # be used with a subclass that handles this. # # @return [Array] def validate(machine) errors = _detected_errors if @private_key_path @private_key_path.each do |raw_path| path = File.expand_path(raw_path, machine.env.root_path) if !File.file?(path) errors << I18n.t( "vagrant.config.ssh.private_key_missing", path: raw_path) end end end errors end end end end vagrant-1.4.3/plugins/kernel_v2/config/vagrant.rb000066400000000000000000000003131226132634600217670ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V2 class VagrantConfig < Vagrant.plugin("2", :config) attr_accessor :host def to_s "Vagrant" end end end end vagrant-1.4.3/plugins/kernel_v2/config/vm.rb000066400000000000000000000456161226132634600207660ustar00rootroot00000000000000require "pathname" require "securerandom" require "set" require "vagrant" require "vagrant/config/v2/util" require "vagrant/util/platform" require File.expand_path("../vm_provisioner", __FILE__) require File.expand_path("../vm_subvm", __FILE__) module VagrantPlugins module Kernel_V2 class VMConfig < Vagrant.plugin("2", :config) DEFAULT_VM_NAME = :default attr_accessor :base_mac attr_accessor :boot_timeout attr_accessor :box attr_accessor :box_url attr_accessor :box_download_ca_cert attr_accessor :box_download_checksum attr_accessor :box_download_checksum_type attr_accessor :box_download_client_cert attr_accessor :box_download_insecure attr_accessor :graceful_halt_timeout attr_accessor :guest attr_accessor :hostname attr_accessor :usable_port_range attr_reader :provisioners def initialize @boot_timeout = UNSET_VALUE @box_download_ca_cert = UNSET_VALUE @box_download_checksum = UNSET_VALUE @box_download_checksum_type = UNSET_VALUE @box_download_client_cert = UNSET_VALUE @box_download_insecure = UNSET_VALUE @box_url = UNSET_VALUE @graceful_halt_timeout = UNSET_VALUE @guest = UNSET_VALUE @hostname = UNSET_VALUE @provisioners = [] # Internal state @__compiled_provider_configs = {} @__defined_vm_keys = [] @__defined_vms = {} @__finalized = false @__networks = {} @__providers = {} @__provider_overrides = {} @__synced_folders = {} end # This was from V1, but we just kept it here as an alias for hostname # because too many people mess this up. def host_name=(value) @hostname = value end # Custom merge method since some keys here are merged differently. def merge(other) super.tap do |result| other_networks = other.instance_variable_get(:@__networks) result.instance_variable_set(:@__networks, @__networks.merge(other_networks)) result.instance_variable_set(:@provisioners, @provisioners + other.provisioners) # Merge defined VMs by first merging the defined VM keys, # preserving the order in which they were defined. other_defined_vm_keys = other.instance_variable_get(:@__defined_vm_keys) other_defined_vm_keys -= @__defined_vm_keys new_defined_vm_keys = @__defined_vm_keys + other_defined_vm_keys # Merge the actual defined VMs. other_defined_vms = other.instance_variable_get(:@__defined_vms) new_defined_vms = {} @__defined_vms.each do |key, subvm| new_defined_vms[key] = subvm.clone end other_defined_vms.each do |key, subvm| if !new_defined_vms.has_key?(key) new_defined_vms[key] = subvm.clone else new_defined_vms[key].config_procs.concat(subvm.config_procs) new_defined_vms[key].options.merge!(subvm.options) end end # Merge the providers by prepending any configuration blocks we # have for providers onto the new configuration. other_providers = other.instance_variable_get(:@__providers) new_providers = @__providers.dup other_providers.each do |key, blocks| new_providers[key] ||= [] new_providers[key] += blocks end # Merge the provider overrides by appending them... other_overrides = other.instance_variable_get(:@__provider_overrides) new_overrides = @__provider_overrides.dup other_overrides.each do |key, blocks| new_overrides[key] ||= [] new_overrides[key] += blocks end # Merge synced folders. other_folders = other.instance_variable_get(:@__synced_folders) new_folders = {} @__synced_folders.each do |key, value| new_folders[key] = value.dup end other_folders.each do |id, options| new_folders[id] ||= {} new_folders[id].merge!(options) end result.instance_variable_set(:@__defined_vm_keys, new_defined_vm_keys) result.instance_variable_set(:@__defined_vms, new_defined_vms) result.instance_variable_set(:@__providers, new_providers) result.instance_variable_set(:@__provider_overrides, new_overrides) result.instance_variable_set(:@__synced_folders, new_folders) end end # Defines a synced folder pair. This pair of folders will be synced # to/from the machine. Note that if the machine you're using doesn't # support multi-directional syncing (perhaps an rsync backed synced # folder) then the host is always synced to the guest but guest data # may not be synced back to the host. # # @param [String] hostpath Path to the host folder to share. If this # is a relative path, it is relative to the location of the # Vagrantfile. # @param [String] guestpath Path on the guest to mount the shared # folder. # @param [Hash] options Additional options. def synced_folder(hostpath, guestpath, options=nil) if Vagrant::Util::Platform.windows? # On Windows, Ruby just uses normal '/' for path seps, so # just replace normal Windows style seps with Unix ones. hostpath = hostpath.to_s.gsub("\\", "/") end options ||= {} options = options.dup options[:guestpath] = guestpath.to_s.gsub(/\/$/, '') options[:hostpath] = hostpath @__synced_folders[options[:guestpath]] = options end # Define a way to access the machine via a network. This exposes a # high-level abstraction for networking that may not directly map # 1-to-1 for every provider. For example, AWS has no equivalent to # "port forwarding." But most providers will attempt to implement this # in a way that behaves similarly. # # `type` can be one of: # # * `:forwarded_port` - A port that is accessible via localhost # that forwards into the machine. # * `:private_network` - The machine gets an IP that is not directly # publicly accessible, but ideally accessible from this machine. # * `:public_network` - The machine gets an IP on a shared network. # # @param [Symbol] type Type of network # @param [Hash] options Options for the network. def network(type, options=nil) options ||= {} options = options.dup options[:protocol] ||= "tcp" if !options[:id] default_id = nil if type == :forwarded_port # For forwarded ports, set the default ID to the # host port so that host ports overwrite each other. default_id = "#{options[:protocol]}#{options[:host]}" end options[:id] = default_id || SecureRandom.uuid end # Scope the ID by type so that different types can share IDs id = options[:id] id = "#{type}-#{id}" # Merge in the previous settings if we have them. if @__networks.has_key?(id) options = @__networks[id][1].merge(options) end # Merge in the latest settings and set the internal state @__networks[id] = [type.to_sym, options] end # Configures a provider for this VM. # # @param [Symbol] name The name of the provider. def provider(name, &block) name = name.to_sym @__providers[name] ||= [] @__provider_overrides[name] ||= [] if block_given? @__providers[name] << block if block_given? # If this block takes two arguments, then we curry it and store # the configuration override for use later. if block.arity == 2 @__provider_overrides[name] << block.curry[Vagrant::Config::V2::DummyConfig.new] end end end def provision(name, options=nil, &block) @provisioners << VagrantConfigProvisioner.new(name.to_sym, options, &block) end def defined_vms @__defined_vms end # This returns the keys of the sub-vms in the order they were # defined. def defined_vm_keys @__defined_vm_keys end def define(name, options=nil, &block) name = name.to_sym options ||= {} options = options.dup options[:config_version] ||= "2" # Add the name to the array of VM keys. This array is used to # preserve the order in which VMs are defined. @__defined_vm_keys << name if !@__defined_vm_keys.include?(name) # Add the SubVM to the hash of defined VMs if !@__defined_vms[name] @__defined_vms[name] = VagrantConfigSubVM.new end @__defined_vms[name].options.merge!(options) @__defined_vms[name].config_procs << [options[:config_version], block] if block end #------------------------------------------------------------------- # Internal methods, don't call these. #------------------------------------------------------------------- def finalize! # Defaults @boot_timeout = 300 if @boot_timeout == UNSET_VALUE @box_download_ca_cert = nil if @box_download_ca_cert == UNSET_VALUE @box_download_checksum = nil if @box_download_checksum == UNSET_VALUE @box_download_checksum_type = nil if @box_download_checksum_type == UNSET_VALUE @box_download_client_cert = nil if @box_download_client_cert == UNSET_VALUE @box_download_insecure = false if @box_download_insecure == UNSET_VALUE @box_url = nil if @box_url == UNSET_VALUE @graceful_halt_timeout = 300 if @graceful_halt_timeout == UNSET_VALUE @guest = nil if @guest == UNSET_VALUE @hostname = nil if @hostname == UNSET_VALUE @hostname = @hostname.to_s if @hostname # Make sure that the download checksum is a string and that # the type is a symbol @box_download_checksum = "" if !@box_download_checksum if @box_download_checksum_type @box_download_checksum_type = @box_download_checksum_type.to_sym end # Make sure the box URL is an array if it is set if @box_url && !@box_url.is_a?(Array) @box_url = [@box_url] end # Set the guest properly @guest = @guest.to_sym if @guest # If we haven't defined a single VM, then we need to define a # default VM which just inherits the rest of the configuration. define(DEFAULT_VM_NAME) if defined_vm_keys.empty? # Clean up some network configurations @__networks.each do |type, opts| if type == :forwarded_port opts[:guest] = opts[:guest].to_i if opts[:guest] opts[:host] = opts[:host].to_i if opts[:host] end end # Compile all the provider configurations @__providers.each do |name, blocks| # If we don't have any configuration blocks, then ignore it next if blocks.empty? # Find the configuration class for this provider config_class = Vagrant.plugin("2").manager.provider_configs[name] config_class ||= Vagrant::Config::V2::DummyConfig # Load it up config = config_class.new blocks.each do |b| b.call(config, Vagrant::Config::V2::DummyConfig.new) end config.finalize! # Store it for retrieval later @__compiled_provider_configs[name] = config end @__synced_folders.each do |id, options| if options[:nfs] options[:type] = :nfs end # Make sure the type is a symbol options[:type] = options[:type].to_sym if options[:type] # Ignore NFS on Windows if options[:type] == :nfs && Vagrant::Util::Platform.windows? options.delete(:type) end end # Flag that we finalized @__finalized = true end # This returns the compiled provider-specific configurationf or the # given provider. # # @param [Symbol] name Name of the provider. def get_provider_config(name) raise "Must finalize first." if !@__finalized result = @__compiled_provider_configs[name] # If no compiled configuration was found, then we try to just # use the default configuration from the plugin. if !result config_class = Vagrant.plugin("2").manager.provider_configs[name] if config_class result = config_class.new result.finalize! end end return result end # This returns a list of VM configurations that are overrides # for this provider. # # @param [Symbol] name Name of the provider # @return [Array] def get_provider_overrides(name) (@__provider_overrides[name] || []).map do |p| ["2", p] end end # This returns the list of networks configured. def networks @__networks.values end # This returns the list of synced folders def synced_folders @__synced_folders end def validate(machine) errors = _detected_errors errors << I18n.t("vagrant.config.vm.box_missing") if !box errors << I18n.t("vagrant.config.vm.box_not_found", :name => box) if \ box && !box_url && !machine.box errors << I18n.t("vagrant.config.vm.hostname_invalid_characters") if \ @hostname && @hostname !~ /^[a-z0-9][-.a-z0-9]+$/i if box_download_ca_cert path = Pathname.new(box_download_ca_cert). expand_path(machine.env.root_path) if !path.file? errors << I18n.t( "vagrant.config.vm.box_download_ca_cert_not_found", path: box_download_ca_cert) end end if box_download_checksum_type if box_download_checksum == "" errors << I18n.t("vagrant.config.vm.box_download_checksum_blank") end else if box_download_checksum != "" errors << I18n.t("vagrant.config.vm.box_download_checksum_notblank") end end has_nfs = false used_guest_paths = Set.new @__synced_folders.each do |id, options| # If the shared folder is disabled then don't worry about validating it next if options[:disabled] guestpath = Pathname.new(options[:guestpath]) hostpath = Pathname.new(options[:hostpath]).expand_path(machine.env.root_path) if guestpath.relative? && guestpath.to_s !~ /^\w+:/ errors << I18n.t("vagrant.config.vm.shared_folder_guestpath_relative", :path => options[:guestpath]) else if used_guest_paths.include?(options[:guestpath]) errors << I18n.t("vagrant.config.vm.shared_folder_guestpath_duplicate", :path => options[:guestpath]) end used_guest_paths.add(options[:guestpath]) end if !hostpath.directory? && !options[:create] errors << I18n.t("vagrant.config.vm.shared_folder_hostpath_missing", :path => options[:hostpath]) end if options[:type] == :nfs has_nfs = true if options[:owner] || options[:group] # Owner/group don't work with NFS errors << I18n.t("vagrant.config.vm.shared_folder_nfs_owner_group", :path => options[:hostpath]) end end if options[:mount_options] && !options[:mount_options].is_a?(Array) errors << I18n.t("vagrant.config.vm.shared_folder_mount_options_array") end # One day remove this probably. if options[:extra] errors << "The 'extra' flag on synced folders is now 'mount_options'" end end if has_nfs if !machine.env.host errors << I18n.t("vagrant.config.vm.nfs_requires_host") else errors << I18n.t("vagrant.config.vm.nfs_not_supported") if \ !machine.env.host.nfs? end end # Validate networks has_fp_port_error = false fp_used = Set.new valid_network_types = [:forwarded_port, :private_network, :public_network] networks.each do |type, options| if !valid_network_types.include?(type) errors << I18n.t("vagrant.config.vm.network_type_invalid", :type => type.to_s) end if type == :forwarded_port if !has_fp_port_error && (!options[:guest] || !options[:host]) errors << I18n.t("vagrant.config.vm.network_fp_requires_ports") has_fp_port_error = true end if options[:host] key = "#{options[:protocol]}#{options[:host]}" if fp_used.include?(key) errors << I18n.t("vagrant.config.vm.network_fp_host_not_unique", :host => options[:host].to_s, :protocol => options[:protocol].to_s) end fp_used.add(key) end end if type == :private_network if options[:type] != :dhcp if !options[:ip] errors << I18n.t("vagrant.config.vm.network_ip_required") end end if options[:ip] && options[:ip].end_with?(".1") errors << I18n.t("vagrant.config.vm.network_ip_ends_in_one") end end end # We're done with VM level errors so prepare the section errors = { "vm" => errors } # Validate only the _active_ provider if machine.provider_config provider_errors = machine.provider_config.validate(machine) if provider_errors errors = Vagrant::Config::V2::Util.merge_errors(errors, provider_errors) end end # Validate provisioners @provisioners.each do |vm_provisioner| if vm_provisioner.invalid? errors["vm"] << I18n.t("vagrant.config.vm.provisioner_not_found", :name => vm_provisioner.name) next end if vm_provisioner.config provisioner_errors = vm_provisioner.config.validate(machine) if provisioner_errors errors = Vagrant::Config::V2::Util.merge_errors(errors, provisioner_errors) end end end errors end end end end vagrant-1.4.3/plugins/kernel_v2/config/vm_provisioner.rb000066400000000000000000000030721226132634600234130ustar00rootroot00000000000000require 'log4r' module VagrantPlugins module Kernel_V2 # Represents a single configured provisioner for a VM. class VagrantConfigProvisioner # The name of the provisioner that should be registered # as a plugin. # # @return [Symbol] attr_reader :name # The configuration associated with the provisioner, if there is any. # # @return [Object] attr_reader :config def initialize(name, options=nil, &block) @logger = Log4r::Logger.new("vagrant::config::vm::provisioner") @logger.debug("Provisioner defined: #{name}") @config = nil @invalid = false @name = name # Attempt to find the provisioner... if !Vagrant.plugin("2").manager.provisioners[name] @logger.warn("Provisioner '#{name}' not found.") @invalid = true end # Attempt to find the configuration class for this provider # if it exists and load the configuration. config_class = Vagrant.plugin("2").manager.provisioner_configs[@name] if !config_class @logger.info("Provisioner config for '#{@name}' not found. Ignoring config.") return end @config = config_class.new @config.set_options(options) if options block.call(@config) if block @config.finalize! end # Returns whether the provisioner used was invalid or not. A provisioner # is invalid if it can't be found. # # @return [Boolean] def invalid? @invalid end end end end vagrant-1.4.3/plugins/kernel_v2/config/vm_subvm.rb000066400000000000000000000012211226132634600221620ustar00rootroot00000000000000require "vagrant/util/stacked_proc_runner" module VagrantPlugins module Kernel_V2 # Represents a single sub-VM in a multi-VM environment. class VagrantConfigSubVM include Vagrant::Util::StackedProcRunner # Returns an array of the configuration procs in [version, proc] # format. # # @return [Array] attr_reader :config_procs attr_reader :options def initialize @config_procs = [] @options = {} end def initialize_copy(other) super @config_procs = other.config_procs.clone @options = other.options.clone end end end end vagrant-1.4.3/plugins/kernel_v2/plugin.rb000066400000000000000000000021771226132634600203700ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Kernel_V2 # This is the "kernel" of Vagrant and contains the configuration classes # that make up the core of Vagrant for V2. class Plugin < Vagrant.plugin("2") name "kernel" description <<-DESC The kernel of Vagrant. This plugin contains required items for even basic functionality of Vagrant version 2. DESC # Core configuration keys provided by the kernel. Note that unlike # "kernel_v1", none of these configuration classes are upgradable. # This is by design, since we can't be sure if they're upgradable # until another version is available. config("ssh") do require File.expand_path("../config/ssh", __FILE__) SSHConfig end config("package") do require File.expand_path("../config/package", __FILE__) PackageConfig end config("vagrant") do require File.expand_path("../config/vagrant", __FILE__) VagrantConfig end config("vm") do require File.expand_path("../config/vm", __FILE__) VMConfig end end end end vagrant-1.4.3/plugins/providers/000077500000000000000000000000001226132634600166645ustar00rootroot00000000000000vagrant-1.4.3/plugins/providers/virtualbox/000077500000000000000000000000001226132634600210635ustar00rootroot00000000000000vagrant-1.4.3/plugins/providers/virtualbox/action.rb000066400000000000000000000273121226132634600226720ustar00rootroot00000000000000require "vagrant/action/builder" module VagrantPlugins module ProviderVirtualBox module Action autoload :Boot, File.expand_path("../action/boot", __FILE__) autoload :CheckAccessible, File.expand_path("../action/check_accessible", __FILE__) autoload :CheckCreated, File.expand_path("../action/check_created", __FILE__) autoload :CheckGuestAdditions, File.expand_path("../action/check_guest_additions", __FILE__) autoload :CheckRunning, File.expand_path("../action/check_running", __FILE__) autoload :CheckVirtualbox, File.expand_path("../action/check_virtualbox", __FILE__) autoload :CleanMachineFolder, File.expand_path("../action/clean_machine_folder", __FILE__) autoload :ClearForwardedPorts, File.expand_path("../action/clear_forwarded_ports", __FILE__) autoload :ClearNetworkInterfaces, File.expand_path("../action/clear_network_interfaces", __FILE__) autoload :Created, File.expand_path("../action/created", __FILE__) autoload :Customize, File.expand_path("../action/customize", __FILE__) autoload :Destroy, File.expand_path("../action/destroy", __FILE__) autoload :DestroyUnusedNetworkInterfaces, File.expand_path("../action/destroy_unused_network_interfaces", __FILE__) autoload :DiscardState, File.expand_path("../action/discard_state", __FILE__) autoload :Export, File.expand_path("../action/export", __FILE__) autoload :ForcedHalt, File.expand_path("../action/forced_halt", __FILE__) autoload :ForwardPorts, File.expand_path("../action/forward_ports", __FILE__) autoload :Import, File.expand_path("../action/import", __FILE__) autoload :IsPaused, File.expand_path("../action/is_paused", __FILE__) autoload :IsRunning, File.expand_path("../action/is_running", __FILE__) autoload :IsSaved, File.expand_path("../action/is_saved", __FILE__) autoload :MatchMACAddress, File.expand_path("../action/match_mac_address", __FILE__) autoload :MessageAlreadyRunning, File.expand_path("../action/message_already_running", __FILE__) autoload :MessageNotCreated, File.expand_path("../action/message_not_created", __FILE__) autoload :MessageNotRunning, File.expand_path("../action/message_not_running", __FILE__) autoload :MessageWillNotDestroy, File.expand_path("../action/message_will_not_destroy", __FILE__) autoload :Network, File.expand_path("../action/network", __FILE__) autoload :Package, File.expand_path("../action/package", __FILE__) autoload :PackageVagrantfile, File.expand_path("../action/package_vagrantfile", __FILE__) autoload :PrepareNFSSettings, File.expand_path("../action/prepare_nfs_settings", __FILE__) autoload :PrepareNFSValidIds, File.expand_path("../action/prepare_nfs_valid_ids", __FILE__) autoload :PrepareForwardedPortCollisionParams, File.expand_path("../action/prepare_forwarded_port_collision_params", __FILE__) autoload :Resume, File.expand_path("../action/resume", __FILE__) autoload :SaneDefaults, File.expand_path("../action/sane_defaults", __FILE__) autoload :SetName, File.expand_path("../action/set_name", __FILE__) autoload :SetupPackageFiles, File.expand_path("../action/setup_package_files", __FILE__) autoload :Suspend, File.expand_path("../action/suspend", __FILE__) # Include the built-in modules so that we can use them as top-level # things. include Vagrant::Action::Builtin # This action boots the VM, assuming the VM is in a state that requires # a bootup (i.e. not saved). def self.action_boot Vagrant::Action::Builder.new.tap do |b| b.use CheckAccessible b.use CleanMachineFolder b.use SetName b.use ClearForwardedPorts b.use Provision b.use EnvSet, :port_collision_repair => true b.use PrepareForwardedPortCollisionParams b.use HandleForwardedPortCollisions b.use PrepareNFSValidIds b.use SyncedFolderCleanup b.use SyncedFolders b.use PrepareNFSSettings b.use ClearNetworkInterfaces b.use Network b.use ForwardPorts b.use SetHostname b.use SaneDefaults b.use Customize, "pre-boot" b.use Boot b.use Customize, "post-boot" b.use WaitForCommunicator, [:starting, :running] b.use CheckGuestAdditions end end # This is the action that is primarily responsible for completely # freeing the resources of the underlying virtual machine. def self.action_destroy Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env1, b2| if !env1[:result] b2.use MessageNotCreated next end b2.use Call, DestroyConfirm do |env2, b3| if env2[:result] b3.use ConfigValidate b3.use CheckAccessible b3.use EnvSet, :force_halt => true b3.use action_halt b3.use Destroy b3.use CleanMachineFolder b3.use DestroyUnusedNetworkInterfaces b3.use ProvisionerCleanup b3.use PrepareNFSValidIds b3.use SyncedFolderCleanup else b3.use MessageWillNotDestroy end end end end end # This is the action that is primarily responsible for halting # the virtual machine, gracefully or by force. def self.action_halt Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env, b2| if env[:result] b2.use CheckAccessible b2.use DiscardState b2.use Call, IsPaused do |env2, b3| next if !env2[:result] b3.use Resume end b2.use Call, GracefulHalt, :poweroff, :running do |env2, b3| if !env2[:result] b3.use ForcedHalt end end else b2.use MessageNotCreated end end end end # This action packages the virtual machine into a single box file. def self.action_package Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env1, b2| if !env1[:result] b2.use MessageNotCreated next end b2.use SetupPackageFiles b2.use CheckAccessible b2.use action_halt b2.use ClearForwardedPorts b2.use PrepareNFSValidIds b2.use SyncedFolderCleanup b2.use Package b2.use Export b2.use PackageVagrantfile end end end # This action just runs the provisioners on the machine. def self.action_provision Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use ConfigValidate b.use Call, Created do |env1, b2| if !env1[:result] b2.use MessageNotCreated next end b2.use Call, IsRunning do |env2, b3| if !env2[:result] b3.use MessageNotRunning next end b3.use CheckAccessible b3.use Provision end end end end # This action is responsible for reloading the machine, which # brings it down, sucks in new configuration, and brings the # machine back up with the new configuration. def self.action_reload Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env1, b2| if !env1[:result] b2.use MessageNotCreated next end b2.use ConfigValidate b2.use action_halt b2.use action_start end end end # This is the action that is primarily responsible for resuming # suspended machines. def self.action_resume Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env, b2| if env[:result] b2.use CheckAccessible b2.use EnvSet, :port_collision_repair => false b2.use PrepareForwardedPortCollisionParams b2.use HandleForwardedPortCollisions b2.use Resume b2.use WaitForCommunicator, [:restoring, :running] else b2.use MessageNotCreated end end end end # This is the action that will exec into an SSH shell. def self.action_ssh Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use CheckCreated b.use CheckAccessible b.use CheckRunning b.use SSHExec end end # This is the action that will run a single SSH command. def self.action_ssh_run Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use CheckCreated b.use CheckAccessible b.use CheckRunning b.use SSHRun end end # This action starts a VM, assuming it is already imported and exists. # A precondition of this action is that the VM exists. def self.action_start Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use ConfigValidate b.use Call, IsRunning do |env, b2| # If the VM is running, then our work here is done, exit if env[:result] b2.use MessageAlreadyRunning next end b2.use Call, IsSaved do |env2, b3| if env2[:result] # The VM is saved, so just resume it b3.use action_resume next end b3.use Call, IsPaused do |env3, b4| if env3[:result] b4.use Resume next end # The VM is not saved, so we must have to boot it up # like normal. Boot! b4.use action_boot end end end end end # This is the action that is primarily responsible for suspending # the virtual machine. def self.action_suspend Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox b.use Call, Created do |env, b2| if env[:result] b2.use CheckAccessible b2.use Suspend else b2.use MessageNotCreated end end end end # This action brings the machine up from nothing, including importing # the box, configuring metadata, and booting. def self.action_up Vagrant::Action::Builder.new.tap do |b| b.use CheckVirtualbox # Handle box_url downloading early so that if the Vagrantfile # references any files in the box or something it all just # works fine. b.use Call, Created do |env, b2| if !env[:result] b2.use HandleBoxUrl end end b.use ConfigValidate b.use Call, Created do |env, b2| # If the VM is NOT created yet, then do the setup steps if !env[:result] b2.use CheckAccessible b2.use Customize, "pre-import" b2.use Import b2.use MatchMACAddress end end b.use action_start end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/000077500000000000000000000000001226132634600223405ustar00rootroot00000000000000vagrant-1.4.3/plugins/providers/virtualbox/action/boot.rb000066400000000000000000000010051226132634600236240ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Boot def initialize(app, env) @app = app end def call(env) @env = env boot_mode = @env[:machine].provider_config.gui ? "gui" : "headless" # Start up the VM and wait for it to boot. env[:ui].info I18n.t("vagrant.actions.vm.boot.booting") env[:machine].provider.driver.start(boot_mode) @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/check_accessible.rb000066400000000000000000000012021226132634600261120ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class CheckAccessible def initialize(app, env) @app = app end def call(env) if env[:machine].state.id == :inaccessible # The VM we are attempting to manipulate is inaccessible. This # is a very bad situation and can only be fixed by the user. It # also prohibits us from actually doing anything with the virtual # machine, so we raise an error. raise Vagrant::Errors::VMInaccessible end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/check_created.rb000066400000000000000000000010021226132634600254220ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action # This middleware checks that the VM is created, and raises an exception # if it is not, notifying the user that creation is required. class CheckCreated def initialize(app, env) @app = app end def call(env) if env[:machine].state.id == :not_created raise Vagrant::Errors::VMNotCreatedError end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/check_guest_additions.rb000066400000000000000000000026461226132634600272170ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class CheckGuestAdditions def initialize(app, env) @app = app end def call(env) # Use the raw interface for now, while the virtualbox gem # doesn't support guest properties (due to cross platform issues) version = env[:machine].provider.driver.read_guest_additions_version if !version env[:ui].warn I18n.t("vagrant.actions.vm.check_guest_additions.not_detected") else # Read the versions versions = [version, env[:machine].provider.driver.version] # Strip of any -OSE or _OSE and read only the first two parts # of the version such as "4.2" in "4.2.0" versions.map! do |v| v = v.gsub(/[-_]ose/i, '') match = /^(\d+\.\d+)\.(\d+)/.match(v) v = match[1] if match v end guest_version = versions[0] vb_version = versions[1] if guest_version != vb_version env[:ui].warn(I18n.t("vagrant.actions.vm.check_guest_additions.version_mismatch", :guest_version => version, :virtualbox_version => vb_version)) end end # Continue @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/check_running.rb000066400000000000000000000010001226132634600254710ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action # This middleware checks that the VM is running, and raises an exception # if it is not, notifying the user that the VM must be running. class CheckRunning def initialize(app, env) @app = app end def call(env) if env[:machine].state.id != :running raise Vagrant::Errors::VMNotRunningError end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/check_virtualbox.rb000066400000000000000000000011241226132634600262170ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action # Checks that VirtualBox is installed and ready to be used. class CheckVirtualbox def initialize(app, env) @app = app end def call(env) # This verifies that VirtualBox is installed and the driver is # ready to function. If not, then an exception will be raised # which will break us out of execution of the middleware sequence. Driver::Meta.new.verify! # Carry on. @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/clean_machine_folder.rb000066400000000000000000000025161226132634600267720ustar00rootroot00000000000000require "fileutils" module VagrantPlugins module ProviderVirtualBox module Action # Cleans up the VirtualBox machine folder for any ".xml-prev" # files which VirtualBox may have left over. This is a bug in # VirtualBox. As soon as this is fixed, this middleware can and # will be removed. class CleanMachineFolder def initialize(app, env) @app = app end def call(env) clean_machine_folder(env[:machine].provider.driver.read_machine_folder) @app.call(env) end def clean_machine_folder(machine_folder) folder = File.join(machine_folder, "*") # Small safeguard against potentially unwanted rm-rf, since the default # machine folder will typically always be greater than 10 characters long. # For users with it < 10, out of luck? return if folder.length < 10 Dir[folder].each do |f| next unless File.directory?(f) keep = Dir["#{f}/**/*"].find do |d| # Find a file that doesn't have ".xml-prev" as the suffix, # which signals that we want to keep this folder File.file?(d) && !(File.basename(d) =~ /\.vbox-prev$/) end FileUtils.rm_rf(f) if !keep end end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/clear_forwarded_ports.rb000066400000000000000000000006171226132634600272430ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class ClearForwardedPorts def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.actions.vm.clear_forward_ports.deleting") env[:machine].provider.driver.clear_forwarded_ports @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/clear_network_interfaces.rb000066400000000000000000000016301226132634600277270ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class ClearNetworkInterfaces def initialize(app, env) @app = app end def call(env) # Create the adapters array to make all adapters nothing. # We do adapters 2 to 8 because that is every built-in adapter # excluding the NAT adapter on port 1 which Vagrant always # expects to exist. adapters = [] 2.upto(env[:machine].provider.driver.max_network_adapters).each do |i| adapters << { :adapter => i, :type => :none } end # "Enable" all the adapters we setup. env[:ui].info I18n.t("vagrant.actions.vm.clear_network_interfaces.deleting") env[:machine].provider.driver.enable_adapters(adapters) @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/created.rb000066400000000000000000000010021226132634600242650ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Created def initialize(app, env) @app = app end def call(env) # Set the result to be true if the machine is created. env[:result] = env[:machine].state.id != :not_created # Call the next if we have one (but we shouldn't, since this # middleware is built to run with the Call-type middlewares) @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/customize.rb000066400000000000000000000023711226132634600247120ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Customize def initialize(app, env, event) @app = app @event = event end def call(env) customizations = [] env[:machine].provider_config.customizations.each do |event, command| if event == @event customizations << command end end if !customizations.empty? env[:ui].info I18n.t("vagrant.actions.vm.customize.running", event: @event) # Execute each customization command. customizations.each do |command| processed_command = command.collect do |arg| arg = env[:machine].id if arg == :id arg.to_s end begin env[:machine].provider.driver.execute_command( processed_command + [retryable: true]) rescue Vagrant::Errors::VBoxManageError => e raise Vagrant::Errors::VMCustomizationFailed, { :command => command, :error => e.inspect } end end end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/destroy.rb000066400000000000000000000006131226132634600243560ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Destroy def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.actions.vm.destroy.destroying") env[:machine].provider.driver.delete env[:machine].id = nil @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/destroy_unused_network_interfaces.rb000066400000000000000000000011421226132634600317130ustar00rootroot00000000000000require "log4r" module VagrantPlugins module ProviderVirtualBox module Action class DestroyUnusedNetworkInterfaces def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::plugins::virtualbox::destroy_unused_netifs") end def call(env) if env[:machine].provider_config.destroy_unused_network_interfaces @logger.info("Destroying unused network interfaces...") env[:machine].provider.driver.delete_unused_host_only_networks end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/discard_state.rb000066400000000000000000000007141226132634600255000ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class DiscardState def initialize(app, env) @app = app end def call(env) if env[:machine].provider.state.id == :saved env[:ui].info I18n.t("vagrant.actions.vm.discard_state.discarding") env[:machine].provider.driver.discard_saved_state end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/export.rb000066400000000000000000000016421226132634600242110ustar00rootroot00000000000000require "fileutils" module VagrantPlugins module ProviderVirtualBox module Action class Export def initialize(app, env) @app = app end def call(env) @env = env raise Vagrant::Errors::VMPowerOffToPackage if \ @env[:machine].provider.state.id != :poweroff export @app.call(env) end def export @env[:ui].info I18n.t("vagrant.actions.vm.export.exporting") @env[:machine].provider.driver.export(ovf_path) do |progress| @env[:ui].clear_line @env[:ui].report_progress(progress.percent, 100, false) end # Clear the line a final time so the next data can appear # alone on the line. @env[:ui].clear_line end def ovf_path File.join(@env["export.temp_dir"], "box.ovf") end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/forced_halt.rb000066400000000000000000000012121226132634600251330ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class ForcedHalt def initialize(app, env) @app = app end def call(env) current_state = env[:machine].state.id if current_state == :running || current_state == :gurumeditation env[:ui].info I18n.t("vagrant.actions.vm.halt.force") env[:machine].provider.driver.halt end # Sleep for a second to verify that the VM properly # cleans itself up. Silly VirtualBox. sleep 1 if !env["vagrant.test"] @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/forward_ports.rb000066400000000000000000000057761226132634600255770ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class ForwardPorts include Util::CompileForwardedPorts def initialize(app, env) @app = app end #-------------------------------------------------------------- # Execution #-------------------------------------------------------------- def call(env) @env = env # Get the ports we're forwarding env[:forwarded_ports] ||= compile_forwarded_ports(env[:machine].config) # Warn if we're port forwarding to any privileged ports... env[:forwarded_ports].each do |fp| if fp.host_port <= 1024 env[:ui].warn I18n.t("vagrant.actions.vm.forward_ports.privileged_ports") break end end env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding") forward_ports @app.call(env) end def forward_ports ports = [] interfaces = @env[:machine].provider.driver.read_network_interfaces @env[:forwarded_ports].each do |fp| message_attributes = { :adapter => fp.adapter, :guest_port => fp.guest_port, :host_port => fp.host_port } # Assuming the only reason to establish port forwarding is # because the VM is using Virtualbox NAT networking. Host-only # bridged networking don't require port-forwarding and establishing # forwarded ports on these attachment types has uncertain behaviour. @env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry", message_attributes)) # Verify we have the network interface to attach to if !interfaces[fp.adapter] raise Vagrant::Errors::ForwardPortAdapterNotFound, :adapter => fp.adapter.to_s, :guest => fp.guest_port.to_s, :host => fp.host_port.to_s end # Port forwarding requires the network interface to be a NAT interface, # so verify that that is the case. if interfaces[fp.adapter][:type] != :nat @env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.non_nat", message_attributes)) next end # Add the options to the ports array to send to the driver later ports << { :adapter => fp.adapter, :guestip => fp.guest_ip, :guestport => fp.guest_port, :hostip => fp.host_ip, :hostport => fp.host_port, :name => fp.id, :protocol => fp.protocol } end if !ports.empty? # We only need to forward ports if there are any to forward @env[:machine].provider.driver.forward_ports(ports) end end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/import.rb000066400000000000000000000035451226132634600242060ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Import def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.actions.vm.import.importing", :name => env[:machine].box.name) # Import the virtual machine ovf_file = env[:machine].box.directory.join("box.ovf").to_s env[:machine].id = env[:machine].provider.driver.import(ovf_file) do |progress| env[:ui].clear_line env[:ui].report_progress(progress, 100, false) end # Clear the line one last time since the progress meter doesn't disappear # immediately. env[:ui].clear_line # If we got interrupted, then the import could have been # interrupted and its not a big deal. Just return out. return if env[:interrupted] # Flag as erroneous and return if import failed raise Vagrant::Errors::VMImportFailure if !env[:machine].id # Import completed successfully. Continue the chain @app.call(env) end def recover(env) if env[:machine].provider.state.id != :not_created return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError) # If we're not supposed to destroy on error then just return return if !env[:destroy_on_error] # Interrupted, destroy the VM. We note that we don't want to # validate the configuration here, and we don't want to confirm # we want to destroy. destroy_env = env.clone destroy_env[:config_validate] = false destroy_env[:force_confirm_destroy] = true env[:action_runner].run(Action.action_destroy, destroy_env) end end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/is_paused.rb000066400000000000000000000007751226132634600246520ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class IsPaused def initialize(app, env) @app = app end def call(env) # Set the result to be true if the machine is paused. env[:result] = env[:machine].state.id == :paused # Call the next if we have one (but we shouldn't, since this # middleware is built to run with the Call-type middlewares) @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/is_running.rb000066400000000000000000000010001226132634600250270ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class IsRunning def initialize(app, env) @app = app end def call(env) # Set the result to be true if the machine is running. env[:result] = env[:machine].state.id == :running # Call the next if we have one (but we shouldn't, since this # middleware is built to run with the Call-type middlewares) @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/is_saved.rb000066400000000000000000000007721226132634600244700ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class IsSaved def initialize(app, env) @app = app end def call(env) # Set the result to be true if the machine is saved. env[:result] = env[:machine].state.id == :saved # Call the next if we have one (but we shouldn't, since this # middleware is built to run with the Call-type middlewares) @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/match_mac_address.rb000066400000000000000000000011111226132634600263000ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MatchMACAddress def initialize(app, env) @app = app end def call(env) raise Vagrant::Errors::VMBaseMacNotSpecified if !env[:machine].config.vm.base_mac # Create the proc which we want to use to modify the virtual machine env[:ui].info I18n.t("vagrant.actions.vm.match_mac.matching") env[:machine].provider.driver.set_mac_address(env[:machine].config.vm.base_mac) @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/message_already_running.rb000066400000000000000000000005151226132634600275530ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MessageAlreadyRunning def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.commands.common.vm_already_running") @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/message_not_created.rb000066400000000000000000000005051226132634600266600ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MessageNotCreated def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.commands.common.vm_not_created") @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/message_not_running.rb000066400000000000000000000005051226132634600267310ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MessageNotRunning def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.commands.common.vm_not_running") @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/message_will_not_destroy.rb000066400000000000000000000006071226132634600277740ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class MessageWillNotDestroy def initialize(app, env) @app = app end def call(env) env[:ui].info I18n.t("vagrant.commands.destroy.will_not_destroy", :name => env[:machine].name) @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/network.rb000066400000000000000000000412221226132634600243570ustar00rootroot00000000000000require "set" require "log4r" require "vagrant/util/network_ip" require "vagrant/util/scoped_hash_override" module VagrantPlugins module ProviderVirtualBox module Action # This middleware class sets up all networking for the VirtualBox # instance. This includes host only networks, bridged networking, # forwarded ports, etc. # # This handles all the `config.vm.network` configurations. class Network include Vagrant::Util::NetworkIP include Vagrant::Util::ScopedHashOverride def initialize(app, env) @logger = Log4r::Logger.new("vagrant::plugins::virtualbox::network") @app = app end def call(env) # TODO: Validate network configuration prior to anything below @env = env # Get the list of network adapters from the configuration network_adapters_config = env[:machine].provider_config.network_adapters.dup # Assign the adapter slot for each high-level network available_slots = Set.new(1..8) network_adapters_config.each do |slot, _data| available_slots.delete(slot) end @logger.debug("Available slots for high-level adapters: #{available_slots.inspect}") @logger.info("Determining network adapters required for high-level configuration...") available_slots = available_slots.to_a.sort env[:machine].config.vm.networks.each do |type, options| # We only handle private and public networks next if type != :private_network && type != :public_network options = scoped_hash_override(options, :virtualbox) # Figure out the slot that this adapter will go into slot = options[:adapter] if !slot if available_slots.empty? raise Vagrant::Errors::VirtualBoxNoRoomForHighLevelNetwork end slot = available_slots.shift end # Internal network is a special type if type == :private_network && options[:intnet] type = :internal_network end # Configure it data = nil if type == :private_network # private_network = hostonly data = [:hostonly, options] elsif type == :public_network # public_network = bridged data = [:bridged, options] elsif type == :internal_network data = [:intnet, options] end # Store it! @logger.info(" -- Slot #{slot}: #{data[0]}") network_adapters_config[slot] = data end @logger.info("Determining adapters and compiling network configuration...") adapters = [] networks = [] network_adapters_config.each do |slot, data| type = data[0] options = data[1] @logger.info("Network slot #{slot}. Type: #{type}.") # Get the normalized configuration for this type config = send("#{type}_config", options) config[:adapter] = slot @logger.debug("Normalized configuration: #{config.inspect}") # Get the VirtualBox adapter configuration adapter = send("#{type}_adapter", config) adapters << adapter @logger.debug("Adapter configuration: #{adapter.inspect}") # Get the network configuration network = send("#{type}_network_config", config) network[:auto_config] = config[:auto_config] networks << network end if !adapters.empty? # Enable the adapters @logger.info("Enabling adapters...") env[:ui].info I18n.t("vagrant.actions.vm.network.preparing") env[:machine].provider.driver.enable_adapters(adapters) end # Continue the middleware chain. @app.call(env) # If we have networks to configure, then we configure it now, since # that requires the machine to be up and running. if !adapters.empty? && !networks.empty? assign_interface_numbers(networks, adapters) # Only configure the networks the user requested us to configure networks_to_configure = networks.select { |n| n[:auto_config] } if !networks_to_configure.empty? env[:ui].info I18n.t("vagrant.actions.vm.network.configuring") env[:machine].guest.capability(:configure_networks, networks_to_configure) end end end def bridged_config(options) return { :auto_config => true, :bridge => nil, :mac => nil, :nic_type => nil, :use_dhcp_assigned_default_route => false }.merge(options || {}) end def bridged_adapter(config) # Find the bridged interfaces that are available bridgedifs = @env[:machine].provider.driver.read_bridged_interfaces bridgedifs.delete_if { |interface| interface[:status] == "Down" } # The name of the chosen bridge interface will be assigned to this # variable. chosen_bridge = nil if config[:bridge] @logger.debug("Bridge was directly specified in config, searching for: #{config[:bridge]}") # Search for a matching bridged interface bridgedifs.each do |interface| if interface[:name].downcase == config[:bridge].downcase @logger.debug("Specific bridge found as configured in the Vagrantfile. Using it.") chosen_bridge = interface[:name] break end end # If one wasn't found, then we notify the user here. if !chosen_bridge @env[:ui].info I18n.t("vagrant.actions.vm.bridged_networking.specific_not_found", :bridge => config[:bridge]) end end # If we still don't have a bridge chosen (this means that one wasn't # specified in the Vagrantfile, or the bridge specified in the Vagrantfile # wasn't found), then we fall back to the normal means of searchign for a # bridged network. if !chosen_bridge if bridgedifs.length == 1 # One bridgable interface? Just use it. chosen_bridge = bridgedifs[0][:name] @logger.debug("Only one bridged interface available. Using it by default.") else # More than one bridgable interface requires a user decision, so # show options to choose from. @env[:ui].info I18n.t("vagrant.actions.vm.bridged_networking.available", :prefix => false) bridgedifs.each_index do |index| interface = bridgedifs[index] @env[:ui].info("#{index + 1}) #{interface[:name]}", :prefix => false) end # The range of valid choices valid = Range.new(1, bridgedifs.length) # The choice that the user has chosen as the bridging interface choice = nil while !valid.include?(choice) choice = @env[:ui].ask("What interface should the network bridge to? ") choice = choice.to_i end chosen_bridge = bridgedifs[choice - 1][:name] end end @logger.info("Bridging adapter #{config[:adapter]} to #{chosen_bridge}") # Given the choice we can now define the adapter we're using return { :adapter => config[:adapter], :type => :bridged, :bridge => chosen_bridge, :mac_address => config[:mac], :nic_type => config[:nic_type] } end def bridged_network_config(config) if config[:ip] options = { :auto_config => true, :mac => nil, :netmask => "255.255.255.0", :type => :static }.merge(config) options[:type] = options[:type].to_sym return options end return { :type => :dhcp, :use_dhcp_assigned_default_route => config[:use_dhcp_assigned_default_route] } end def hostonly_config(options) options = { :auto_config => true, :mac => nil, :nic_type => nil, :netmask => "255.255.255.0", :type => :static }.merge(options) # Make sure the type is a symbol options[:type] = options[:type].to_sym # Default IP is in the 20-bit private network block for DHCP based networks options[:ip] = "172.28.128.1" if options[:type] == :dhcp && !options[:ip] # Calculate our network address for the given IP/netmask netaddr = network_address(options[:ip], options[:netmask]) # Verify that a host-only network subnet would not collide # with a bridged networking interface. # # If the subnets overlap in any way then the host only network # will not work because the routing tables will force the # traffic onto the real interface rather than the VirtualBox # interface. @env[:machine].provider.driver.read_bridged_interfaces.each do |interface| that_netaddr = network_address(interface[:ip], interface[:netmask]) raise Vagrant::Errors::NetworkCollision if \ netaddr == that_netaddr && interface[:status] != "Down" end # Split the IP address into its components ip_parts = netaddr.split(".").map { |i| i.to_i } # Calculate the adapter IP, which we assume is the IP ".1" at # the end usually. adapter_ip = ip_parts.dup adapter_ip[3] += 1 options[:adapter_ip] ||= adapter_ip.join(".") dhcp_options = {} if options[:type] == :dhcp # Calculate the DHCP server IP, which is the network address # with the final octet + 2. So "172.28.0.0" turns into "172.28.0.2" dhcp_ip = ip_parts.dup dhcp_ip[3] += 2 dhcp_options[:dhcp_ip] ||= dhcp_ip.join(".") # Calculate the lower and upper bound for the DHCP server dhcp_lower = ip_parts.dup dhcp_lower[3] += 3 dhcp_options[:dhcp_lower] ||= dhcp_lower.join(".") dhcp_upper = ip_parts.dup dhcp_upper[3] = 254 dhcp_options[:dhcp_upper] ||= dhcp_upper.join(".") end return { :adapter_ip => options[:adapter_ip], :auto_config => options[:auto_config], :ip => options[:ip], :mac => options[:mac], :netmask => options[:netmask], :nic_type => options[:nic_type], :type => options[:type] }.merge(dhcp_options) end def hostonly_adapter(config) @logger.info("Searching for matching hostonly network: #{config[:ip]}") interface = hostonly_find_matching_network(config) if !interface @logger.info("Network not found. Creating if we can.") # It is an error if a specific host only network name was specified # but the network wasn't found. if config[:name] raise Vagrant::Errors::NetworkNotFound, :name => config[:name] end # Create a new network interface = hostonly_create_network(config) @logger.info("Created network: #{interface[:name]}") end if config[:type] == :dhcp # Check that if there is a DHCP server attached on our interface, # then it is identical. Otherwise, we can't set it. if interface[:dhcp] valid = interface[:dhcp][:ip] == config[:dhcp_ip] && interface[:dhcp][:lower] == config[:dhcp_lower] && interface[:dhcp][:upper] == config[:dhcp_upper] raise Vagrant::Errors::NetworkDHCPAlreadyAttached if !valid @logger.debug("DHCP server already properly configured") else # Configure the DHCP server for the network. @logger.debug("Creating a DHCP server...") @env[:machine].provider.driver.create_dhcp_server(interface[:name], config) end end return { :adapter => config[:adapter], :hostonly => interface[:name], :mac => config[:mac], :nic_type => config[:nic_type], :type => :hostonly } end def hostonly_network_config(config) return { :type => config[:type], :adapter_ip => config[:adapter_ip], :ip => config[:ip], :netmask => config[:netmask] } end def intnet_config(options) return { :type => "static", :ip => nil, :netmask => "255.255.255.0", :adapter => nil, :mac => nil, :intnet => nil, :auto_config => true }.merge(options || {}) end def intnet_adapter(config) return { :adapter => config[:adapter], :type => :intnet, :mac_address => config[:mac], :nic_type => config[:nic_type], :intnet => config[:intnet] } end def intnet_network_config(config) return { :type => config[:type], :ip => config[:ip], :netmask => config[:netmask] } end def nat_config(options) return { :auto_config => false } end def nat_adapter(config) return { :adapter => config[:adapter], :type => :nat, } end def nat_network_config(config) return {} end #----------------------------------------------------------------- # Misc. helpers #----------------------------------------------------------------- # Assigns the actual interface number of a network based on the # enabled NICs on the virtual machine. # # This interface number is used by the guest to configure the # NIC on the guest VM. # # The networks are modified in place by adding an ":interface" # field to each. def assign_interface_numbers(networks, adapters) current = 0 adapter_to_interface = {} # Make a first pass to assign interface numbers by adapter location vm_adapters = @env[:machine].provider.driver.read_network_interfaces vm_adapters.sort.each do |number, adapter| if adapter[:type] != :none # Not used, so assign the interface number and increment adapter_to_interface[number] = current current += 1 end end # Make a pass through the adapters to assign the :interface # key to each network configuration. adapters.each_index do |i| adapter = adapters[i] network = networks[i] # Figure out the interface number by simple lookup network[:interface] = adapter_to_interface[adapter[:adapter]] end end #----------------------------------------------------------------- # Hostonly Helper Functions #----------------------------------------------------------------- # This creates a host only network for the given configuration. def hostonly_create_network(config) @env[:machine].provider.driver.create_host_only_network( :adapter_ip => config[:adapter_ip], :netmask => config[:netmask] ) end # This finds a matching host only network for the given configuration. def hostonly_find_matching_network(config) this_netaddr = network_address(config[:ip], config[:netmask]) @env[:machine].provider.driver.read_host_only_interfaces.each do |interface| return interface if config[:name] && config[:name] == interface[:name] return interface if this_netaddr == \ network_address(interface[:ip], interface[:netmask]) end nil end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/package.rb000066400000000000000000000022161226132634600242610ustar00rootroot00000000000000require 'fileutils' require 'vagrant/action/general/package' module VagrantPlugins module ProviderVirtualBox module Action class Package < Vagrant::Action::General::Package # Doing this so that we can test that the parent is properly # called in the unit tests. alias_method :general_call, :call def call(env) # Setup the temporary directory @temp_dir = env[:tmp_path].join(Time.now.to_i.to_s) env["export.temp_dir"] = @temp_dir FileUtils.mkpath(env["export.temp_dir"]) # Just match up a couple environmental variables so that # the superclass will do the right thing. Then, call the # superclass env["package.directory"] = env["export.temp_dir"] general_call(env) # Always call recover to clean up the temp dir clean_temp_dir end def recover(env) clean_temp_dir super end protected def clean_temp_dir if @temp_dir && File.exist?(@temp_dir) FileUtils.rm_rf(@temp_dir) end end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/package_vagrantfile.rb000066400000000000000000000016101226132634600266400ustar00rootroot00000000000000require 'vagrant/util/template_renderer' module VagrantPlugins module ProviderVirtualBox module Action class PackageVagrantfile # For TemplateRenderer include Vagrant::Util def initialize(app, env) @app = app end def call(env) @env = env create_vagrantfile @app.call(env) end # This method creates the auto-generated Vagrantfile at the root of the # box. This Vagrantfile contains the MAC address so that the user doesn't # have to worry about it. def create_vagrantfile File.open(File.join(@env["export.temp_dir"], "Vagrantfile"), "w") do |f| f.write(TemplateRenderer.render("package_Vagrantfile", { :base_mac => @env[:machine].provider.driver.read_mac_address })) end end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/prepare_forwarded_port_collision_params.rb000066400000000000000000000020711226132634600330420ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class PrepareForwardedPortCollisionParams def initialize(app, env) @app = app end def call(env) # Get the forwarded ports used by other virtual machines and # consider those in use as well. env[:port_collision_extra_in_use] = env[:machine].provider.driver.read_used_ports # Build the remap for any existing collision detections remap = {} env[:port_collision_remap] = remap env[:machine].provider.driver.read_forwarded_ports.each do |_nic, name, hostport, _guestport| env[:machine].config.vm.networks.each do |type, options| next if type != :forwarded_port # If the ID matches the name of the forwarded port, then # remap. if options[:id] == name remap[options[:host]] = hostport break end end end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/prepare_nfs_settings.rb000066400000000000000000000100261226132634600271100ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class PrepareNFSSettings include Vagrant::Util::Retryable def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::nfs") end def call(env) @machine = env[:machine] @app.call(env) if using_nfs? @logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP") add_ips_to_env!(env) end end # We're using NFS if we have any synced folder with NFS configured. If # we are not using NFS we don't need to do the extra work to # populate these fields in the environment. def using_nfs? @machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs } end # Extracts the proper host and guest IPs for NFS mounts and stores them # in the environment for the SyncedFolder action to use them in # mounting. # # The ! indicates that this method modifies its argument. def add_ips_to_env!(env) adapter, host_ip = find_host_only_adapter machine_ip = read_static_machine_ips || read_dynamic_machine_ip(adapter) raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip env[:nfs_host_ip] = host_ip env[:nfs_machine_ip] = machine_ip end # Finds first host only network adapter and returns its adapter number # and IP address # # @return [Integer, String] adapter number, ip address of found host-only adapter def find_host_only_adapter @machine.provider.driver.read_network_interfaces.each do |adapter, opts| if opts[:type] == :hostonly @machine.provider.driver.read_host_only_interfaces.each do |interface| if interface[:name] == opts[:hostonly] return adapter, interface[:ip] end end end end nil end # Returns the IP address(es) of the guest by looking for static IPs # given to host only adapters in the Vagrantfile # # @return [Array] Configured static IPs def read_static_machine_ips ips = [] @machine.config.vm.networks.each do |type, options| if type == :private_network && options[:ip].is_a?(String) ips << options[:ip] end end if ips.empty? return nil end ips end # Returns the IP address of the guest by looking at vbox guest property # for the appropriate guest adapter. # # For DHCP interfaces, the guest property will not be present until the # guest completes # # @param [Integer] adapter number to read IP for # @return [String] ip address of adapter def read_dynamic_machine_ip(adapter) return nil unless adapter # vbox guest properties are 0-indexed, while showvminfo network # interfaces are 1-indexed. go figure. guestproperty_adapter = adapter - 1 # we need to wait for the guest's IP to show up as a guest property. # retry thresholds are relatively high since we might need to wait # for DHCP, but even static IPs can take a second or two to appear. retryable(retry_options.merge(on: Vagrant::Errors::VirtualBoxGuestPropertyNotFound)) do @machine.provider.driver.read_guest_ip(guestproperty_adapter) end rescue Vagrant::Errors::VirtualBoxGuestPropertyNotFound # this error is more specific with a better error message directing # the user towards the fact that it's probably a reportable bug raise Vagrant::Errors::NFSNoGuestIP end # Separating these out so we can stub out the sleep in tests def retry_options {tries: 15, sleep: 1} end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/prepare_nfs_valid_ids.rb000066400000000000000000000006151226132634600272110ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class PrepareNFSValidIds def initialize(app, env) @app = app @logger = Log4r::Logger.new("vagrant::action::vm::nfs") end def call(env) env[:nfs_valid_ids] = env[:machine].provider.driver.read_vms.values @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/resume.rb000066400000000000000000000011701226132634600241640ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Resume def initialize(app, env) @app = app end def call(env) current_state = env[:machine].provider.state.id if current_state == :paused env[:ui].info I18n.t("vagrant.actions.vm.resume.unpausing") env[:machine].provider.driver.resume elsif current_state == :saved env[:ui].info I18n.t("vagrant.actions.vm.resume.resuming") env[:action_runner].run(Boot, env) end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/sane_defaults.rb000066400000000000000000000075501226132634600255110ustar00rootroot00000000000000require "log4r" module VagrantPlugins module ProviderVirtualBox module Action class SaneDefaults def initialize(app, env) @logger = Log4r::Logger.new("vagrant::action::vm::sanedefaults") @app = app end def call(env) # Set the env on an instance variable so we can access it in # helpers. @env = env # Enable the host IO cache on the sata controller. Note that # if this fails then its not a big deal, so we don't raise any # errors. The Host IO cache vastly improves disk IO performance # for VMs. command = [ "storagectl", env[:machine].id, "--name", "SATA Controller", "--hostiocache", "on" ] attempt_and_log(command, "Enabling the Host I/O cache on the SATA controller...") # Use rtcuseutc so that the VM sees UTC time. command = ["modifyvm", env[:machine].id, "--rtcuseutc", "on"] attempt_and_log(command, "Enabling rtcuseutc...") if env[:machine].provider_config.auto_nat_dns_proxy @logger.info("Automatically figuring out whether to enable/disable NAT DNS proxy...") # Enable/disable the NAT DNS proxy as necessary if enable_dns_proxy? command = ["modifyvm", env[:machine].id, "--natdnsproxy1", "on"] attempt_and_log(command, "Enable the NAT DNS proxy on adapter 1...") else command = ["modifyvm", env[:machine].id, "--natdnsproxy1", "off" ] attempt_and_log(command, "Disable the NAT DNS proxy on adapter 1...") command = ["modifyvm", env[:machine].id, "--natdnshostresolver1", "off" ] attempt_and_log(command, "Disable the NAT DNS resolver on adapter 1...") end else @logger.info("NOT trying to automatically manage NAT DNS proxy.") end @app.call(env) end protected # This is just a helper method that executes a single command, logs # the given string to the log, and also includes the exit status in # the log message. # # We assume every command is idempotent and pass along the `retryable` # flag. This is because VBoxManage is janky about running simultaneously # on the same box, and if we up multiple boxes at the same time, a bunch # of modifyvm commands get fired # # @param [Array] command Command to run # @param [String] log Log message to write. def attempt_and_log(command, log) begin @env[:machine].provider.driver.execute_command( command + [retryable: true]) rescue Vagrant::Errors::VBoxManageError => e @logger.info("#{log} (error = #{e.inspect})") end end # This uses some heuristics to determine if the NAT DNS proxy should # be enabled or disabled. See the comments within the function body # itself to see the checks it does. # # @return [Boolean] def enable_dns_proxy? begin contents = File.read("/etc/resolv.conf") if contents =~ /^nameserver 127\.0\.(0|1)\.1$/ # The use of both natdnsproxy and natdnshostresolver break on # Ubuntu 12.04 and 12.10 that uses resolvconf with localhost. When used # VirtualBox will give the client dns server 10.0.2.3, while # not binding to that address itself. Therefore disable this # feature if host uses the resolvconf server 127.0.0.1 or # 127.0.1.1 @logger.info("Disabling DNS proxy since resolv.conf contains 127.0.0.1 or 127.0.1.1") return false end rescue Errno::ENOENT; end return true end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/set_name.rb000066400000000000000000000034141226132634600244620ustar00rootroot00000000000000require "log4r" module VagrantPlugins module ProviderVirtualBox module Action class SetName def initialize(app, env) @logger = Log4r::Logger.new("vagrant::action::vm::setname") @app = app end def call(env) name = env[:machine].provider_config.name # If we already set the name before, then don't do anything sentinel = env[:machine].data_dir.join("action_set_name") if !name && sentinel.file? @logger.info("Default name was already set before, not doing it again.") return @app.call(env) end # If no name was manually set, then use a default if !name prefix = "#{env[:root_path].basename.to_s}_#{env[:machine].name}" prefix.gsub!(/[^-a-z0-9_]/i, "") # milliseconds + random number suffix to allow for simultaneous `vagrant up` of the same box in different dirs name = prefix + "_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" end # Verify the name is not taken vms = env[:machine].provider.driver.read_vms raise Vagrant::Errors::VMNameExists, :name => name if \ vms.has_key?(name) && vms[name] != env[:machine].id if vms.has_key?(name) @logger.info("Not setting the name because our name is already set.") else @logger.info("Setting the name of the VM: #{name}") env[:ui].info(I18n.t("vagrant.actions.vm.set_name.setting_name")) env[:machine].provider.driver.set_name(name) end # Create the sentinel sentinel.open("w") do |f| f.write(Time.now.to_i.to_s) end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/setup_package_files.rb000066400000000000000000000026101226132634600266610ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class SetupPackageFiles def initialize(app, env) @app = app env["package.include"] ||= [] env["package.vagrantfile"] ||= nil end def call(env) files = {} env["package.include"].each do |file| source = Pathname.new(file) dest = nil # If the source is relative then we add the file as-is to the include # directory. Otherwise, we copy only the file into the root of the # include directory. Kind of strange, but seems to match what people # expect based on history. if source.relative? dest = source else dest = source.basename end # Assign the mapping files[file] = dest end if env["package.vagrantfile"] # Vagrantfiles are treated special and mapped to a specific file files[env["package.vagrantfile"]] = "_Vagrantfile" end # Verify the mapping files.each do |from, _| raise Vagrant::Errors::PackageIncludeMissing, :file => from if !File.exist?(from) end # Save the mapping env["package.files"] = files @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/action/suspend.rb000066400000000000000000000006661226132634600243560ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Action class Suspend def initialize(app, env) @app = app end def call(env) if env[:machine].provider.state.id == :running env[:ui].info I18n.t("vagrant.actions.vm.suspend.suspending") env[:machine].provider.driver.suspend end @app.call(env) end end end end end vagrant-1.4.3/plugins/providers/virtualbox/config.rb000066400000000000000000000106511226132634600226600ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox class Config < Vagrant.plugin("2", :config) # Vagrant by default will make "smart" decisions to enable/disable # the NAT DNS proxy. If this is set to `true`, then the DNS proxy # will not be enabled, and it is up to the end user to do it. # # @return [Boolean] attr_accessor :auto_nat_dns_proxy # An array of customizations to make on the VM prior to booting it. # # @return [Array] attr_reader :customizations # If true, unused network interfaces will automatically be deleted. # This defaults to false because the detection does not work across # multiple users, and because on Windows this operation requires # administrative privileges. # # @return [Boolean] attr_accessor :destroy_unused_network_interfaces # If set to `true`, then VirtualBox will be launched with a GUI. # # @return [Boolean] attr_accessor :gui # This should be set to the name of the machine in the VirtualBox # GUI. # # @return [String] attr_accessor :name # The defined network adapters. # # @return [Hash] attr_reader :network_adapters def initialize @auto_nat_dns_proxy = UNSET_VALUE @customizations = [] @destroy_unused_network_interfaces = UNSET_VALUE @name = UNSET_VALUE @network_adapters = {} @gui = UNSET_VALUE # We require that network adapter 1 is a NAT device. network_adapter(1, :nat) end # Customize the VM by calling `VBoxManage` with the given # arguments. # # When called multiple times, the customizations will be applied # in the order given. # # The special `:name` parameter in the command will be replaced with # the unique ID or name of the virtual machine. This is useful for # parameters to `modifyvm` and the like. # # @param [Array] command An array of arguments to pass to # VBoxManage. def customize(*command) event = command.first.is_a?(String) ? command.shift : "pre-boot" command = command[0] @customizations << [event, command] end # This defines a network adapter that will be added to the VirtualBox # virtual machine in the given slot. # # @param [Integer] slot The slot for this network adapter. # @param [Symbol] type The type of adapter. def network_adapter(slot, type, *args) @network_adapters[slot] = [type, args] end # Shortcut for setting memory size for the virtual machine. # Calls #customize internally. # # @param size [Integer, String] the memory size in MB def memory=(size) customize("pre-boot", ["modifyvm", :id, "--memory", size.to_s]) end # This is the hook that is called to finalize the object before it # is put into use. def finalize! # Default is to auto the DNS proxy @auto_nat_dns_proxy = true if @auto_nat_dns_proxy == UNSET_VALUE if @destroy_unused_network_interfaces == UNSET_VALUE @destroy_unused_network_interfaces = false end # Default is to not show a GUI @gui = false if @gui == UNSET_VALUE # The default name is just nothing, and we default it @name = nil if @name == UNSET_VALUE end def validate(machine) errors = [] valid_events = ["pre-import", "pre-boot", "post-boot"] @customizations.each do |event, _| if !valid_events.include?(event) errors << I18n.t( "vagrant.virtualbox.config.invalid_event", event: event.to_s, valid_events: valid_events.join(", ")) end end @customizations.each do |event, command| if event == "pre-import" && command.index(:id) errors << I18n.t("vagrant.virtualbox.config.id_in_pre_import") end end # Verify that internal networks are only on private networks. machine.config.vm.networks.each do |type, data| if data[:virtualbox__intnet] && type != :private_network errors << I18n.t("vagrant.virtualbox.config.intnet_on_bad_type") break end end { "VirtualBox Provider" => errors } end def to_s "VirtualBox" end end end end vagrant-1.4.3/plugins/providers/virtualbox/driver/000077500000000000000000000000001226132634600223565ustar00rootroot00000000000000vagrant-1.4.3/plugins/providers/virtualbox/driver/base.rb000066400000000000000000000270561226132634600236270ustar00rootroot00000000000000require 'log4r' require 'vagrant/util/busy' require 'vagrant/util/platform' require 'vagrant/util/retryable' require 'vagrant/util/subprocess' module VagrantPlugins module ProviderVirtualBox module Driver # Base class for all VirtualBox drivers. # # This class provides useful tools for things such as executing # VBoxManage and handling SIGINTs and so on. class Base # Include this so we can use `Subprocess` more easily. include Vagrant::Util::Retryable def initialize @logger = Log4r::Logger.new("vagrant::provider::virtualbox::base") # This flag is used to keep track of interrupted state (SIGINT) @interrupted = false # Set the path to VBoxManage @vboxmanage_path = "VBoxManage" if Vagrant::Util::Platform.windows? || Vagrant::Util::Platform.cygwin? @logger.debug("Windows. Trying VBOX_INSTALL_PATH for VBoxManage") # On Windows, we use the VBOX_INSTALL_PATH environmental # variable to find VBoxManage. if ENV.has_key?("VBOX_INSTALL_PATH") # Get the path. path = ENV["VBOX_INSTALL_PATH"] @logger.debug("VBOX_INSTALL_PATH value: #{path}") # There can actually be multiple paths in here, so we need to # split by the separator ";" and see which is a good one. path.split(";").each do |single| # Make sure it ends with a \ single += "\\" if !single.end_with?("\\") # If the executable exists, then set it as the main path # and break out vboxmanage = "#{path}VBoxManage.exe" if File.file?(vboxmanage) @vboxmanage_path = Vagrant::Util::Platform.cygwin_windows_path(vboxmanage) break end end end end @logger.info("VBoxManage path: #{@vboxmanage_path}") end # Clears the forwarded ports that have been set on the virtual machine. def clear_forwarded_ports end # Clears the shared folders that have been set on the virtual machine. def clear_shared_folders end # Creates a DHCP server for a host only network. # # @param [String] network Name of the host-only network. # @param [Hash] options Options for the DHCP server. def create_dhcp_server(network, options) end # Creates a host only network with the given options. # # @param [Hash] options Options to create the host only network. # @return [Hash] The details of the host only network, including # keys `:name`, `:ip`, and `:netmask` def create_host_only_network(options) end # Deletes the virtual machine references by this driver. def delete end # Deletes any host only networks that aren't being used for anything. def delete_unused_host_only_networks end # Discards any saved state associated with this VM. def discard_saved_state end # Enables network adapters on the VM. # # The format of each adapter specification should be like so: # # { # :type => :hostonly, # :hostonly => "vboxnet0", # :mac_address => "tubes" # } # # This must support setting up both host only and bridged networks. # # @param [Array] adapters Array of adapters to enable. def enable_adapters(adapters) end # Execute a raw command straight through to VBoxManage. # # Accepts a :retryable => true option if the command should be retried # upon failure. # # Raises a VBoxManage error if it fails. # # @param [Array] command Command to execute. def execute_command(command) end # Exports the virtual machine to the given path. # # @param [String] path Path to the OVF file. # @yield [progress] Yields the block with the progress of the export. def export(path) end # Forwards a set of ports for a VM. # # This will not affect any previously set forwarded ports, # so be sure to delete those if you need to. # # The format of each port hash should be the following: # # { # :name => "foo", # :hostport => 8500, # :guestport => 80, # :adapter => 1, # :protocol => "tcp" # } # # Note that "adapter" and "protocol" are optional and will default # to 1 and "tcp" respectively. # # @param [Array] ports An array of ports to set. See documentation # for more information on the format. def forward_ports(ports) end # Halts the virtual machine (pulls the plug). def halt end # Imports the VM from an OVF file. # # @param [String] ovf Path to the OVF file. # @return [String] UUID of the imported VM. def import(ovf) end # Returns the maximum number of network adapters. def max_network_adapters 8 end # Returns a list of forwarded ports for a VM. # # @param [String] uuid UUID of the VM to read from, or `nil` if this # VM. # @param [Boolean] active_only If true, only VMs that are running will # be checked. # @return [Array] def read_forwarded_ports(uuid=nil, active_only=false) end # Returns a list of bridged interfaces. # # @return [Hash] def read_bridged_interfaces end # Returns the guest additions version that is installed on this VM. # # @return [String] def read_guest_additions_version end # Returns the value of a guest property on the current VM. # # @param [String] property the name of the guest property to read # @return [String] value of the guest property # @raise [VirtualBoxGuestPropertyNotFound] if the guest property does not have a value def read_guest_property(property) end # Returns a list of available host only interfaces. # # @return [Hash] def read_host_only_interfaces end # Returns the MAC address of the first network interface. # # @return [String] def read_mac_address end # Returns the folder where VirtualBox places it's VMs. # # @return [String] def read_machine_folder end # Returns a list of network interfaces of the VM. # # @return [Hash] def read_network_interfaces end # Returns the current state of this VM. # # @return [Symbol] def read_state end # Returns a list of all forwarded ports in use by active # virtual machines. # # @return [Array] def read_used_ports end # Returns a list of all UUIDs of virtual machines currently # known by VirtualBox. # # @return [Array] def read_vms end # Sets the MAC address of the first network adapter. # # @param [String] mac MAC address without any spaces/hyphens. def set_mac_address(mac) end # Share a set of folders on this VM. # # @param [Array] folders def share_folders(folders) end # Reads the SSH port of this VM. # # @param [Integer] expected Expected guest port of SSH. def ssh_port(expected) end # Starts the virtual machine. # # @param [String] mode Mode to boot the VM. Either "headless" # or "gui" def start(mode) end # Suspend the virtual machine. def suspend end # Verifies that the driver is ready to accept work. # # This should raise a VagrantError if things are not ready. def verify! end # Verifies that an image can be imported properly. # # @param [String] path Path to an OVF file. # @return [Boolean] def verify_image(path) end # Checks if a VM with the given UUID exists. # # @return [Boolean] def vm_exists?(uuid) end # Execute the given subcommand for VBoxManage and return the output. def execute(*command, &block) # Get the options hash if it exists opts = {} opts = command.pop if command.last.is_a?(Hash) tries = 0 tries = 3 if opts[:retryable] # Variable to store our execution result r = nil retryable(:on => Vagrant::Errors::VBoxManageError, :tries => tries, :sleep => 1) do # If there is an error with VBoxManage, this gets set to true errored = false # Execute the command r = raw(*command, &block) # If the command was a failure, then raise an exception that is # nicely handled by Vagrant. if r.exit_code != 0 if @interrupted @logger.info("Exit code != 0, but interrupted. Ignoring.") elsif r.exit_code == 126 # This exit code happens if VBoxManage is on the PATH, # but another executable it tries to execute is missing. # This is usually indicative of a corrupted VirtualBox install. raise Vagrant::Errors::VBoxManageNotFoundError else errored = true end else # Sometimes, VBoxManage fails but doesn't actual return a non-zero # exit code. For this we inspect the output and determine if an error # occurred. if r.stderr =~ /failed to open \/dev\/vboxnetctl/i # This catches an error message that only shows when kernel # drivers aren't properly installed. @logger.error("Error message about unable to open vboxnetctl") raise Vagrant::Errors::VirtualBoxKernelModuleNotLoaded end if r.stderr =~ /VBoxManage([.a-z]+?): error:/ # This catches the generic VBoxManage error case. @logger.info("VBoxManage error text found, assuming error.") errored = true end end # If there was an error running VBoxManage, show the error and the # output. if errored raise Vagrant::Errors::VBoxManageError, :command => command.inspect, :stderr => r.stderr end end # Return the output, making sure to replace any Windows-style # newlines with Unix-style. r.stdout.gsub("\r\n", "\n") end # Executes a command and returns the raw result object. def raw(*command, &block) int_callback = lambda do @interrupted = true @logger.info("Interrupted.") end # Append in the options for subprocess command << { :notify => [:stdout, :stderr] } Vagrant::Util::Busy.busy(int_callback) do Vagrant::Util::Subprocess.execute(@vboxmanage_path, *command, &block) end end end end end end vagrant-1.4.3/plugins/providers/virtualbox/driver/meta.rb000066400000000000000000000113301226132634600236270ustar00rootroot00000000000000require "forwardable" require "log4r" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver class Meta < Base # This is raised if the VM is not found when initializing a driver # with a UUID. class VMNotFound < StandardError; end # We use forwardable to do all our driver forwarding extend Forwardable # The UUID of the virtual machine we represent attr_reader :uuid # The version of virtualbox that is running. attr_reader :version def initialize(uuid=nil) # Setup the base super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox::meta") @uuid = uuid # Read and assign the version of VirtualBox we know which # specific driver to instantiate. begin @version = read_version || "" rescue Vagrant::Errors::CommandUnavailable, Vagrant::Errors::CommandUnavailableWindows # This means that VirtualBox was not found, so we raise this # error here. raise Vagrant::Errors::VirtualBoxNotDetected end # Instantiate the proper version driver for VirtualBox @logger.debug("Finding driver for VirtualBox version: #{@version}") driver_map = { "4.0" => Version_4_0, "4.1" => Version_4_1, "4.2" => Version_4_2, "4.3" => Version_4_3 } if @version.start_with?("4.2.14") # VirtualBox 4.2.14 just doesn't work with Vagrant, so show error raise Vagrant::Errors::VirtualBoxBrokenVersion040214 end driver_klass = nil driver_map.each do |key, klass| if @version.start_with?(key) driver_klass = klass break end end if !driver_klass supported_versions = driver_map.keys.sort.join(", ") raise Vagrant::Errors::VirtualBoxInvalidVersion, supported_versions: supported_versions end @logger.info("Using VirtualBox driver: #{driver_klass}") @driver = driver_klass.new(@uuid) if @uuid # Verify the VM exists, and if it doesn't, then don't worry # about it (mark the UUID as nil) raise VMNotFound if !@driver.vm_exists?(@uuid) end end def_delegators :@driver, :clear_forwarded_ports, :clear_shared_folders, :create_dhcp_server, :create_host_only_network, :delete, :delete_unused_host_only_networks, :discard_saved_state, :enable_adapters, :execute_command, :export, :forward_ports, :halt, :import, :read_forwarded_ports, :read_bridged_interfaces, :read_guest_additions_version, :read_guest_ip, :read_guest_property, :read_host_only_interfaces, :read_mac_address, :read_mac_addresses, :read_machine_folder, :read_network_interfaces, :read_state, :read_used_ports, :read_vms, :resume, :set_mac_address, :set_name, :share_folders, :ssh_port, :start, :suspend, :verify!, :verify_image, :vm_exists? protected # This returns the version of VirtualBox that is running. # # @return [String] def read_version # The version string is usually in one of the following formats: # # * 4.1.8r1234 # * 4.1.8r1234_OSE # * 4.1.8_MacPortsr1234 # # Below accounts for all of these. # Note: We split this into multiple lines because apparently "".split("_") # is [], so we have to check for an empty array in between. output = execute("--version") if output =~ /vboxdrv kernel module is not loaded/ || output =~ /VirtualBox kernel modules are not loaded/i raise Vagrant::Errors::VirtualBoxKernelModuleNotLoaded elsif output =~ /Please install/ # Check for installation incomplete warnings, for example: # "WARNING: The character device /dev/vboxdrv does not # exist. Please install the virtualbox-ose-dkms package and # the appropriate headers, most likely linux-headers-generic." raise Vagrant::Errors::VirtualBoxInstallIncomplete end parts = output.split("_") return nil if parts.empty? parts[0].split("r")[0] end end end end end vagrant-1.4.3/plugins/providers/virtualbox/driver/version_4_0.rb000066400000000000000000000366461226132634600250510ustar00rootroot00000000000000require 'log4r' require "vagrant/util/platform" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 4.0.x class Version_4_0 < Base def initialize(uuid) super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox_4_0") @uuid = uuid end def clear_forwarded_ports args = [] read_forwarded_ports(@uuid).each do |nic, name, _, _| args.concat(["--natpf#{nic}", "delete", name]) end execute("modifyvm", @uuid, *args) if !args.empty? end def clear_shared_folders info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if name = line[/^SharedFolderNameMachineMapping\d+="(.+?)"$/, 1] execute("sharedfolder", "remove", @uuid, "--name", name) end end end def create_dhcp_server(network, options) execute("dhcpserver", "add", "--ifname", network, "--ip", options[:dhcp_ip], "--netmask", options[:netmask], "--lowerip", options[:dhcp_lower], "--upperip", options[:dhcp_upper], "--enable") end def create_host_only_network(options) # Create the interface interface = execute("hostonlyif", "create") name = interface[/^Interface '(.+?)' was successfully created$/, 1] # Configure it execute("hostonlyif", "ipconfig", name, "--ip", options[:adapter_ip], "--netmask", options[:netmask]) # Return the details return { :name => name, :ip => options[:adapter_ip], :netmask => options[:netmask], :dhcp => nil } end def delete execute("unregistervm", @uuid, "--delete") end def delete_unused_host_only_networks networks = [] execute("list", "hostonlyifs").split("\n").each do |line| if network_name = line[/^Name:\s+(.+?)$/, 1] networks << network_name end end execute("list", "vms").split("\n").each do |line| if vm_name = line[/^".+?"\s+\{(.+?)\}$/, 1] info = execute("showvminfo", vm_name, "--machinereadable", :retryable => true) info.split("\n").each do |line| if network_name = line[/^hostonlyadapter\d+="(.+?)"$/, 1] networks.delete(network_name) end end end end networks.each do |name| # First try to remove any DHCP servers attached. We use `raw` because # it is okay if this fails. It usually means that a DHCP server was # never attached. raw("dhcpserver", "remove", "--ifname", name) # Delete the actual host only network interface. execute("hostonlyif", "remove", name) end end def discard_saved_state execute("discardstate", @uuid) end def enable_adapters(adapters) args = [] adapters.each do |adapter| args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s]) if adapter[:bridge] args.concat(["--bridgeadapter#{adapter[:adapter]}", adapter[:bridge]]) end if adapter[:hostonly] args.concat(["--hostonlyadapter#{adapter[:adapter]}", adapter[:hostonly]]) end if adapter[:mac_address] args.concat(["--macaddress#{adapter[:adapter]}", adapter[:mac_address]]) end if adapter[:nic_type] args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s]) end end execute("modifyvm", @uuid, *args) end def execute_command(command) execute(*command) end def export(path) # TODO: Progress execute("export", @uuid, "--output", path.to_s) end def forward_ports(ports) args = [] ports.each do |options| pf_builder = [options[:name], options[:protocol] || "tcp", options[:hostip] || "", options[:hostport], options[:guestip] || "", options[:guestport]] args.concat(["--natpf#{options[:adapter] || 1}", pf_builder.join(",")]) end execute("modifyvm", @uuid, *args) if !args.empty? end def halt execute("controlvm", @uuid, "poweroff") end def import(ovf) ovf = Vagrant::Util::Platform.cygwin_windows_path(ovf) output = "" total = "" last = 0 execute("import", ovf) do |type, data| if type == :stdout # Keep track of the stdout so that we can get the VM name output << data elsif type == :stderr # Append the data so we can see the full view total << data # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") if lines.include?("OK.") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. if current = lines.last[/.+(\d{2})%/, 1] current = current.to_i if current > last last = current yield current if block_given? end end end end end # Find the name of the VM name name = output[/Suggested VM name "(.+?)"/, 1] if !name @logger.error("Couldn't find VM name in the output.") return nil end output = execute("list", "vms") if existing_vm = output[/^"#{Regexp.escape(name)}" \{(.+?)\}$/, 1] return existing_vm end nil end def read_forwarded_ports(uuid=nil, active_only=false) uuid ||= @uuid @logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}") results = [] current_nic = nil info = execute("showvminfo", uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. if nic = line[/^nic(\d+)=".+?"$/, 1] current_nic = nic.to_i end # If we care about active VMs only, then we check the state # to verify the VM is running. if active_only && (state = line[/^VMState="(.+?)"$/, 1] and state != "running") return [] end # Parse out the forwarded port information if matcher = /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/.match(line) result = [current_nic, matcher[1], matcher[2].to_i, matcher[3].to_i] @logger.debug(" - #{result.inspect}") results << result end end results end def read_bridged_interfaces execute("list", "bridgedifs").split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if name = line[/^Name:\s+(.+?)$/, 1] info[:name] = name elsif ip = line[/^IPAddress:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif status = line[/^Status:\s+(.+?)$/, 1] info[:status] = status end end # Return the info to build up the results info end end def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", :retryable => true) if value = output[/^Value: (.+?)$/, 1] # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't # too important. return value.split("_").first end return nil end def read_guest_ip(adapter_number) read_guest_property("/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP") end def read_guest_property(property) output = execute("guestproperty", "get", @uuid, property) if output =~ /^Value: (.+?)$/ $1.to_s else raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, :guest_property => property end end def read_host_only_interfaces dhcp = {} execute("list", "dhcpservers", :retryable => true).split("\n\n").each do |block| info = {} block.split("\n").each do |line| if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] info[:network] = network elsif ip = line[/^IP:\s+(.+?)$/, 1] info[:ip] = ip elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] info[:lower] = lower elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] info[:upper] = upper end end # Set the DHCP info dhcp[info[:network]] = info end execute("list", "hostonlyifs", :retryable => true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if name = line[/^Name:\s+(.+?)$/, 1] info[:name] = name elsif ip = line[/^IPAddress:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif status = line[/^Status:\s+(.+?)$/, 1] info[:status] = status end end # Set the DHCP info if it exists info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]] info end end def read_mac_address info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if mac = line[/^macaddress1="(.+?)"$/, 1] return mac end end nil end def read_mac_addresses macs = {} info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if matcher = /^macaddress(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i mac = matcher[2].to_s macs[adapter] = mac end end macs end def read_machine_folder execute("list", "systemproperties", :retryable => true).split("\n").each do |line| if folder = line[/^Default machine folder:\s+(.+?)$/i, 1] return folder end end nil end def read_network_interfaces nics = {} info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if matcher = /^nic(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i type = matcher[2].to_sym nics[adapter] ||= {} nics[adapter][:type] = type elsif matcher = /^hostonlyadapter(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i network = matcher[2].to_s nics[adapter] ||= {} nics[adapter][:hostonly] = network elsif matcher = /^bridgeadapter(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i network = matcher[2].to_s nics[adapter] ||= {} nics[adapter][:bridge] = network end end nics end def read_state output = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) if output =~ /^name=""$/ return :inaccessible elsif state = output[/^VMState="(.+?)"$/, 1] return state.to_sym end nil end def read_used_ports ports = [] execute("list", "vms", :retryable => true).split("\n").each do |line| if uuid = line[/^".+?" \{(.+?)\}$/, 1] # Ignore our own used ports next if uuid == @uuid read_forwarded_ports(uuid, true).each do |_, _, hostport, _| ports << hostport end end end ports end def read_vms results = {} execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^"(.+?)" \{(.+?)\}$/ results[$1.to_s] = $2.to_s end end results end def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end def set_name(name) execute("modifyvm", @uuid, "--name", name) end def share_folders(folders) folders.each do |folder| args = ["--name", folder[:name], "--hostpath", folder[:hostpath]] args << "--transient" if folder.has_key?(:transient) && folder[:transient] execute("sharedfolder", "add", @uuid, *args) end end def ssh_port(expected_port) @logger.debug("Searching for SSH port: #{expected_port.inspect}") # Look for the forwarded port only by comparing the guest port read_forwarded_ports.each do |_, _, hostport, guestport| return hostport if guestport == expected_port end nil end def resume @logger.debug("Resuming paused VM...") execute("controlvm", @uuid, "resume") end def start(mode) command = ["startvm", @uuid, "--type", mode.to_s] r = raw(*command) if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/ # Some systems return an exit code 1 for some reason. For that # we depend on the output. return true end # If we reached this point then it didn't work out. raise Vagrant::Errors::VBoxManageError, :command => command.inspect end def suspend execute("controlvm", @uuid, "savestate") end def verify! # This command sometimes fails if kernel drivers aren't properly loaded # so we just run the command and verify that it succeeded. execute("list", "hostonlyifs") end def verify_image(path) r = raw("import", path.to_s, "--dry-run") return r.exit_code == 0 end def vm_exists?(uuid) raw("showvminfo", uuid).exit_code == 0 end end end end end vagrant-1.4.3/plugins/providers/virtualbox/driver/version_4_1.rb000066400000000000000000000405741226132634600250450ustar00rootroot00000000000000require 'log4r' require "vagrant/util/platform" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 4.1.x class Version_4_1 < Base def initialize(uuid) super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox_4_1") @uuid = uuid end def clear_forwarded_ports args = [] read_forwarded_ports(@uuid).each do |nic, name, _, _| args.concat(["--natpf#{nic}", "delete", name]) end execute("modifyvm", @uuid, *args) if !args.empty? end def clear_shared_folders info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if folder = line[/^SharedFolderNameMachineMapping\d+="(.+?)"$/, 1] execute("sharedfolder", "remove", @uuid, "--name", folder) end end end def create_dhcp_server(network, options) execute("dhcpserver", "add", "--ifname", network, "--ip", options[:dhcp_ip], "--netmask", options[:netmask], "--lowerip", options[:dhcp_lower], "--upperip", options[:dhcp_upper], "--enable") end def create_host_only_network(options) # Create the interface interface = execute("hostonlyif", "create") name = interface[/^Interface '(.+?)' was successfully created$/, 1] # Configure it execute("hostonlyif", "ipconfig", name, "--ip", options[:adapter_ip], "--netmask", options[:netmask]) # Return the details return { :name => name, :ip => options[:adapter_ip], :netmask => options[:netmask], :dhcp => nil } end def delete execute("unregistervm", @uuid, "--delete") end def delete_unused_host_only_networks networks = [] execute("list", "hostonlyifs").split("\n").each do |line| if network = line[/^Name:\s+(.+?)$/, 1] networks << network end end execute("list", "vms").split("\n").each do |line| if vm = line[/^".+?"\s+\{(.+?)\}$/, 1] info = execute("showvminfo", vm, "--machinereadable", :retryable => true) info.split("\n").each do |line| if adapter = line[/^hostonlyadapter\d+="(.+?)"$/, 1] networks.delete(adapter) end end end end networks.each do |name| # First try to remove any DHCP servers attached. We use `raw` because # it is okay if this fails. It usually means that a DHCP server was # never attached. raw("dhcpserver", "remove", "--ifname", name) # Delete the actual host only network interface. execute("hostonlyif", "remove", name) end end def discard_saved_state execute("discardstate", @uuid) end def enable_adapters(adapters) args = [] adapters.each do |adapter| args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s]) if adapter[:bridge] args.concat(["--bridgeadapter#{adapter[:adapter]}", adapter[:bridge]]) end if adapter[:hostonly] args.concat(["--hostonlyadapter#{adapter[:adapter]}", adapter[:hostonly]]) end if adapter[:intnet] args.concat(["--intnet#{adapter[:adapter]}", adapter[:intnet]]) end if adapter[:mac_address] args.concat(["--macaddress#{adapter[:adapter]}", adapter[:mac_address]]) end if adapter[:nic_type] args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s]) end end execute("modifyvm", @uuid, *args) end def execute_command(command) execute(*command) end def export(path) # TODO: Progress execute("export", @uuid, "--output", path.to_s) end def forward_ports(ports) args = [] ports.each do |options| pf_builder = [options[:name], options[:protocol] || "tcp", options[:hostip] || "", options[:hostport], options[:guestip] || "", options[:guestport]] args.concat(["--natpf#{options[:adapter] || 1}", pf_builder.join(",")]) end execute("modifyvm", @uuid, *args) if !args.empty? end def halt execute("controlvm", @uuid, "poweroff") end def import(ovf) ovf = Vagrant::Util::Platform.cygwin_windows_path(ovf) output = "" total = "" last = 0 execute("import", ovf) do |type, data| if type == :stdout # Keep track of the stdout so that we can get the VM name output << data elsif type == :stderr # Append the data so we can see the full view total << data # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") if lines.include?("OK.") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. if current = lines.last[/.+(\d{2})%/, 1] current = current.to_i if current > last last = current yield current if block_given? end end end end end # Find the name of the VM name name = output[/Suggested VM name "(.+?)"/, 1] if !name @logger.error("Couldn't find VM name in the output.") return nil end output = execute("list", "vms") if existing_vm = output[/^"#{Regexp.escape(name)}" \{(.+?)\}$/, 1] return existing_vm end nil end def read_forwarded_ports(uuid=nil, active_only=false) uuid ||= @uuid @logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}") results = [] current_nic = nil info = execute("showvminfo", uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. if nic = line[/^nic(\d+)=".+?"$/, 1] current_nic = nic.to_i end # If we care about active VMs only, then we check the state # to verify the VM is running. if active_only && (state = line[/^VMState="(.+?)"$/, 1] and state != "running") return [] end # Parse out the forwarded port information if matcher = /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/.match(line) result = [current_nic, matcher[1], matcher[2].to_i, matcher[3].to_i] @logger.debug(" - #{result.inspect}") results << result end end results end def read_bridged_interfaces execute("list", "bridgedifs").split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if name = line[/^Name:\s+(.+?)$/, 1] info[:name] = name elsif ip = line[/^IPAddress:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif status = line[/^Status:\s+(.+?)$/, 1] info[:status] = status end end # Return the info to build up the results info end end def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", :retryable => true) if value = output[/^Value: (.+?)$/, 1] # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't # too important. return value.split("_").first end return nil end def read_guest_ip(adapter_number) read_guest_property("/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP") end def read_guest_property(property) output = execute("guestproperty", "get", @uuid, property) if output =~ /^Value: (.+?)$/ $1.to_s else raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, :guest_property => property end end def read_host_only_interfaces dhcp = {} execute("list", "dhcpservers", :retryable => true).split("\n\n").each do |block| info = {} block.split("\n").each do |line| if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] info[:network] = network elsif ip = line[/^IP:\s+(.+?)$/, 1] info[:ip] = ip elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] info[:lower] = lower elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] info[:upper] = upper end end # Set the DHCP info dhcp[info[:network]] = info end execute("list", "hostonlyifs", :retryable => true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if name = line[/^Name:\s+(.+?)$/, 1] info[:name] = name elsif ip = line[/^IPAddress:\s+(.+?)$/, 1] info[:ip] = ip elsif netmask = line[/^NetworkMask:\s+(.+?)$/, 1] info[:netmask] = netmask elsif status = line[/^Status:\s+(.+?)$/, 1] info[:status] = status end end # Set the DHCP info if it exists info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]] info end end def read_mac_address info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if mac = line[/^macaddress1="(.+?)"$/, 1] return mac end end nil end def read_mac_addresses macs = {} info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if matcher = /^macaddress(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i mac = matcher[2].to_s macs[adapter] = mac end end macs end def read_machine_folder execute("list", "systemproperties", :retryable => true).split("\n").each do |line| if folder = line[/^Default machine folder:\s+(.+?)$/i, 1] return folder end end nil end def read_network_interfaces nics = {} info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if matcher = /^nic(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i type = matcher[2].to_sym nics[adapter] ||= {} nics[adapter][:type] = type elsif matcher = /^hostonlyadapter(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i network = matcher[2].to_s nics[adapter] ||= {} nics[adapter][:hostonly] = network elsif matcher = /^bridgeadapter(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i network = matcher[2].to_s nics[adapter] ||= {} nics[adapter][:bridge] = network end end nics end def read_state output = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) if output =~ /^name=""$/ return :inaccessible elsif state = output[/^VMState="(.+?)"$/, 1] return state.to_sym end nil end def read_used_ports ports = [] execute("list", "vms", :retryable => true).split("\n").each do |line| if uuid = line[/^".+?" \{(.+?)\}$/, 1] # Ignore our own used ports next if uuid == @uuid read_forwarded_ports(uuid, true).each do |_, _, hostport, _| ports << hostport end end end ports end def read_vms results = {} execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^"(.+?)" \{(.+?)\}$/ results[$1.to_s] = $2.to_s end end results end def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end def set_name(name) execute("modifyvm", @uuid, "--name", name) end def share_folders(folders) folders.each do |folder| args = ["--name", folder[:name], "--hostpath", folder[:hostpath]] args << "--transient" if folder.has_key?(:transient) && folder[:transient] # Add the shared folder execute("sharedfolder", "add", @uuid, *args) # Enable symlinks on the shared folder execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1") end end def ssh_port(expected_port) @logger.debug("Searching for SSH port: #{expected_port.inspect}") # Look for the forwarded port only by comparing the guest port read_forwarded_ports.each do |_, _, hostport, guestport| return hostport if guestport == expected_port end nil end def resume @logger.debug("Resuming paused VM...") execute("controlvm", @uuid, "resume") end def start(mode) command = ["startvm", @uuid, "--type", mode.to_s] r = raw(*command) if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/ # Some systems return an exit code 1 for some reason. For that # we depend on the output. return true end # If we reached this point then it didn't work out. raise Vagrant::Errors::VBoxManageError, :command => command.inspect end def suspend execute("controlvm", @uuid, "savestate") end def verify! # This command sometimes fails if kernel drivers aren't properly loaded # so we just run the command and verify that it succeeded. execute("list", "hostonlyifs") end def verify_image(path) r = raw("import", path.to_s, "--dry-run") return r.exit_code == 0 end def vm_exists?(uuid) 5.times do |i| result = raw("showvminfo", uuid) return true if result.exit_code == 0 # GH-2479: Sometimes this happens. In this case, retry. If # we don't see this text, the VM really probably doesn't exist. return false if !result.stderr.include?("CO_E_SERVER_EXEC_FAILURE") # Sleep a bit though to give VirtualBox time to fix itself sleep 2 end # If we reach this point, it means that we consistently got the # failure, do a standard vboxmanage now. This will raise an # exception if it fails again. execute("showvminfo", uuid) return true end end end end end vagrant-1.4.3/plugins/providers/virtualbox/driver/version_4_2.rb000066400000000000000000000423531226132634600250430ustar00rootroot00000000000000require 'log4r' require "vagrant/util/platform" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 4.2.x class Version_4_2 < Base def initialize(uuid) super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox_4_2") @uuid = uuid end def clear_forwarded_ports args = [] read_forwarded_ports(@uuid).each do |nic, name, _, _| args.concat(["--natpf#{nic}", "delete", name]) end execute("modifyvm", @uuid, *args) if !args.empty? end def clear_shared_folders info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/ execute("sharedfolder", "remove", @uuid, "--name", $1.to_s) end end end def create_dhcp_server(network, options) execute("dhcpserver", "add", "--ifname", network, "--ip", options[:dhcp_ip], "--netmask", options[:netmask], "--lowerip", options[:dhcp_lower], "--upperip", options[:dhcp_upper], "--enable") end def create_host_only_network(options) # Create the interface execute("hostonlyif", "create") =~ /^Interface '(.+?)' was successfully created$/ name = $1.to_s # Configure it execute("hostonlyif", "ipconfig", name, "--ip", options[:adapter_ip], "--netmask", options[:netmask]) # Return the details return { :name => name, :ip => options[:adapter_ip], :netmask => options[:netmask], :dhcp => nil } end def delete execute("unregistervm", @uuid, "--delete") end def delete_unused_host_only_networks networks = [] execute("list", "hostonlyifs").split("\n").each do |line| networks << $1.to_s if line =~ /^Name:\s+(.+?)$/ end execute("list", "vms").split("\n").each do |line| if line =~ /^".+?"\s+\{(.+?)\}$/ info = execute("showvminfo", $1.to_s, "--machinereadable", :retryable => true) info.split("\n").each do |inner_line| if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/ networks.delete($1.to_s) end end end end networks.each do |name| # First try to remove any DHCP servers attached. We use `raw` because # it is okay if this fails. It usually means that a DHCP server was # never attached. raw("dhcpserver", "remove", "--ifname", name) # Delete the actual host only network interface. execute("hostonlyif", "remove", name) end end def discard_saved_state execute("discardstate", @uuid) end def enable_adapters(adapters) args = [] adapters.each do |adapter| args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s]) if adapter[:bridge] args.concat(["--bridgeadapter#{adapter[:adapter]}", adapter[:bridge]]) end if adapter[:hostonly] args.concat(["--hostonlyadapter#{adapter[:adapter]}", adapter[:hostonly]]) end if adapter[:intnet] args.concat(["--intnet#{adapter[:adapter]}", adapter[:intnet]]) end if adapter[:mac_address] args.concat(["--macaddress#{adapter[:adapter]}", adapter[:mac_address]]) end if adapter[:nic_type] args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s]) end end execute("modifyvm", @uuid, *args) end def execute_command(command) execute(*command) end def export(path) # TODO: Progress execute("export", @uuid, "--output", path.to_s) end def forward_ports(ports) args = [] ports.each do |options| pf_builder = [options[:name], options[:protocol] || "tcp", options[:hostip] || "", options[:hostport], options[:guestip] || "", options[:guestport]] args.concat(["--natpf#{options[:adapter] || 1}", pf_builder.join(",")]) end execute("modifyvm", @uuid, *args) if !args.empty? end def halt execute("controlvm", @uuid, "poweroff") end def import(ovf) ovf = Vagrant::Util::Platform.cygwin_windows_path(ovf) output = "" total = "" last = 0 output = execute("import", "-n", ovf) output =~ /Suggested VM name "(.+?)"/ suggested_name = $1.to_s specified_name = "#{suggested_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" #Millisecond + Random #Build the specified name param list name_params = Array.new name_params << "--vsys" << "0" << "--vmname" << specified_name #Extract the disks list and build the disk target params disk_params = Array.new disks = output.scan(/(\d+): Hard disk image: source image=.+, target path=(.+),/) disks.each do |unit_num, path| disk_params << "--vsys" disk_params << "0" #Derive vsys num .. do we support OVF's with multiple machines? disk_params << "--unit" disk_params << unit_num disk_params << "--disk" disk_params << path.sub("/#{suggested_name}/", "/#{specified_name}/") end execute("import", ovf , *name_params, *disk_params) do |type, data| if type == :stdout # Keep track of the stdout so that we can get the VM name output << data elsif type == :stderr # Append the data so we can see the full view total << data # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") if lines.include?("OK.") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. if lines.last =~ /.+(\d{2})%/ current = $1.to_i if current > last last = current yield current if block_given? end end end end end output = execute("list", "vms") if output =~ /^"#{Regexp.escape(specified_name)}" \{(.+?)\}$/ return $1.to_s end nil end def max_network_adapters 36 end def read_forwarded_ports(uuid=nil, active_only=false) uuid ||= @uuid @logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}") results = [] current_nic = nil info = execute("showvminfo", uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. current_nic = $1.to_i if line =~ /^nic(\d+)=".+?"$/ # If we care about active VMs only, then we check the state # to verify the VM is running. if active_only && line =~ /^VMState="(.+?)"$/ && $1.to_s != "running" return [] end # Parse out the forwarded port information if line =~ /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/ result = [current_nic, $1.to_s, $2.to_i, $3.to_i] @logger.debug(" - #{result.inspect}") results << result end end results end def read_bridged_interfaces execute("list", "bridgedifs").split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end # Return the info to build up the results info end end def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", :retryable => true) if output =~ /^Value: (.+?)$/ # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't # too important. value = $1.to_s return value.split("_").first end # If we can't get the guest additions version by guest property, try # to get it from the VM info itself. info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| return $1.to_s if line =~ /^GuestAdditionsVersion="(.+?)"$/ end return nil end def read_guest_ip(adapter_number) read_guest_property("/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP") end def read_guest_property(property) output = execute("guestproperty", "get", @uuid, property) if output =~ /^Value: (.+?)$/ $1.to_s else raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, :guest_property => property end end def read_host_only_interfaces dhcp = {} execute("list", "dhcpservers", :retryable => true).split("\n\n").each do |block| info = {} block.split("\n").each do |line| if line =~ /^NetworkName:\s+HostInterfaceNetworking-(.+?)$/ info[:network] = $1.to_s elsif line =~ /^IP:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^lowerIPAddress:\s+(.+?)$/ info[:lower] = $1.to_s elsif line =~ /^upperIPAddress:\s+(.+?)$/ info[:upper] = $1.to_s end end # Set the DHCP info dhcp[info[:network]] = info end execute("list", "hostonlyifs", :retryable => true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end # Set the DHCP info if it exists info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]] info end end def read_mac_address info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| return $1.to_s if line =~ /^macaddress1="(.+?)"$/ end nil end def read_mac_addresses macs = {} info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if matcher = /^macaddress(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i mac = matcher[2].to_s macs[adapter] = mac end end macs end def read_machine_folder execute("list", "systemproperties", :retryable => true).split("\n").each do |line| if line =~ /^Default machine folder:\s+(.+?)$/i return $1.to_s end end nil end def read_network_interfaces nics = {} info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if line =~ /^nic(\d+)="(.+?)"$/ adapter = $1.to_i type = $2.to_sym nics[adapter] ||= {} nics[adapter][:type] = type elsif line =~ /^hostonlyadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:hostonly] = network elsif line =~ /^bridgeadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:bridge] = network end end nics end def read_state output = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) if output =~ /^name=""$/ return :inaccessible elsif output =~ /^VMState="(.+?)"$/ return $1.to_sym end nil end def read_used_ports ports = [] execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^".+?" \{(.+?)\}$/ uuid = $1.to_s # Ignore our own used ports next if uuid == @uuid read_forwarded_ports(uuid, true).each do |_, _, hostport, _| ports << hostport end end end ports end def read_vms results = {} execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^"(.+?)" \{(.+?)\}$/ results[$1.to_s] = $2.to_s end end results end def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end def set_name(name) execute("modifyvm", @uuid, "--name", name, :retryable => true) end def share_folders(folders) folders.each do |folder| args = ["--name", folder[:name], "--hostpath", folder[:hostpath]] args << "--transient" if folder.has_key?(:transient) && folder[:transient] # Add the shared folder execute("sharedfolder", "add", @uuid, *args) # Enable symlinks on the shared folder execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1") end end def ssh_port(expected_port) @logger.debug("Searching for SSH port: #{expected_port.inspect}") # Look for the forwarded port only by comparing the guest port read_forwarded_ports.each do |_, _, hostport, guestport| return hostport if guestport == expected_port end nil end def resume @logger.debug("Resuming paused VM...") execute("controlvm", @uuid, "resume") end def start(mode) command = ["startvm", @uuid, "--type", mode.to_s] r = raw(*command) if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/ # Some systems return an exit code 1 for some reason. For that # we depend on the output. return true end # If we reached this point then it didn't work out. raise Vagrant::Errors::VBoxManageError, :command => command.inspect end def suspend execute("controlvm", @uuid, "savestate") end def verify! # This command sometimes fails if kernel drivers aren't properly loaded # so we just run the command and verify that it succeeded. execute("list", "hostonlyifs") end def verify_image(path) r = raw("import", path.to_s, "--dry-run") return r.exit_code == 0 end def vm_exists?(uuid) 5.times do |i| result = raw("showvminfo", uuid) return true if result.exit_code == 0 # GH-2479: Sometimes this happens. In this case, retry. If # we don't see this text, the VM really probably doesn't exist. return false if !result.stderr.include?("CO_E_SERVER_EXEC_FAILURE") # Sleep a bit though to give VirtualBox time to fix itself sleep 2 end # If we reach this point, it means that we consistently got the # failure, do a standard vboxmanage now. This will raise an # exception if it fails again. execute("showvminfo", uuid) return true end end end end end vagrant-1.4.3/plugins/providers/virtualbox/driver/version_4_3.rb000066400000000000000000000430311226132634600250360ustar00rootroot00000000000000require 'log4r' require "vagrant/util/platform" require File.expand_path("../base", __FILE__) module VagrantPlugins module ProviderVirtualBox module Driver # Driver for VirtualBox 4.3.x class Version_4_3 < Base def initialize(uuid) super() @logger = Log4r::Logger.new("vagrant::provider::virtualbox_4_3") @uuid = uuid end def clear_forwarded_ports args = [] read_forwarded_ports(@uuid).each do |nic, name, _, _| args.concat(["--natpf#{nic}", "delete", name]) end execute("modifyvm", @uuid, *args) if !args.empty? end def clear_shared_folders info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/ execute("sharedfolder", "remove", @uuid, "--name", $1.to_s) end end end def create_dhcp_server(network, options) execute("dhcpserver", "add", "--ifname", network, "--ip", options[:dhcp_ip], "--netmask", options[:netmask], "--lowerip", options[:dhcp_lower], "--upperip", options[:dhcp_upper], "--enable") end def create_host_only_network(options) # Create the interface execute("hostonlyif", "create") =~ /^Interface '(.+?)' was successfully created$/ name = $1.to_s # Configure it execute("hostonlyif", "ipconfig", name, "--ip", options[:adapter_ip], "--netmask", options[:netmask]) # Return the details return { :name => name, :ip => options[:adapter_ip], :netmask => options[:netmask], :dhcp => nil } end def delete execute("unregistervm", @uuid, "--delete") end def delete_unused_host_only_networks networks = [] execute("list", "hostonlyifs").split("\n").each do |line| networks << $1.to_s if line =~ /^Name:\s+(.+?)$/ end execute("list", "vms").split("\n").each do |line| if line =~ /^".+?"\s+\{(.+?)\}$/ info = execute("showvminfo", $1.to_s, "--machinereadable", :retryable => true) info.split("\n").each do |inner_line| if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/ networks.delete($1.to_s) end end end end networks.each do |name| # First try to remove any DHCP servers attached. We use `raw` because # it is okay if this fails. It usually means that a DHCP server was # never attached. raw("dhcpserver", "remove", "--ifname", name) # Delete the actual host only network interface. execute("hostonlyif", "remove", name) end end def discard_saved_state execute("discardstate", @uuid) end def enable_adapters(adapters) args = [] adapters.each do |adapter| args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s]) if adapter[:bridge] args.concat(["--bridgeadapter#{adapter[:adapter]}", adapter[:bridge]]) end if adapter[:hostonly] args.concat(["--hostonlyadapter#{adapter[:adapter]}", adapter[:hostonly]]) end if adapter[:intnet] args.concat(["--intnet#{adapter[:adapter]}", adapter[:intnet]]) end if adapter[:mac_address] args.concat(["--macaddress#{adapter[:adapter]}", adapter[:mac_address]]) end if adapter[:nic_type] args.concat(["--nictype#{adapter[:adapter]}", adapter[:nic_type].to_s]) end end execute("modifyvm", @uuid, *args) end def execute_command(command) execute(*command) end def export(path) # TODO: Progress execute("export", @uuid, "--output", path.to_s) end def forward_ports(ports) args = [] ports.each do |options| pf_builder = [options[:name], options[:protocol] || "tcp", options[:hostip] || "", options[:hostport], options[:guestip] || "", options[:guestport]] args.concat(["--natpf#{options[:adapter] || 1}", pf_builder.join(",")]) end execute("modifyvm", @uuid, *args) if !args.empty? end def halt execute("controlvm", @uuid, "poweroff") end def import(ovf) ovf = Vagrant::Util::Platform.cygwin_windows_path(ovf) output = "" total = "" last = 0 # Dry-run the import to get the suggested name and path @logger.debug("Doing dry-run import to determine parallel-safe name...") output = execute("import", "-n", ovf) result = /Suggested VM name "(.+?)"/.match(output) suggested_name = result[1].to_s # Append millisecond plus a random to the path in case we're # importing the same box elsewhere. specified_name = "#{suggested_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" @logger.debug("-- Parallel safe name: #{specified_name}") # Build the specified name param list name_params = [ "--vsys", "0", "--vmname", specified_name, ] # Extract the disks list and build the disk target params disk_params = [] disks = output.scan(/(\d+): Hard disk image: source image=.+, target path=(.+),/) disks.each do |unit_num, path| disk_params << "--vsys" disk_params << "0" disk_params << "--unit" disk_params << unit_num disk_params << "--disk" disk_params << path.sub("/#{suggested_name}/", "/#{specified_name}/") end execute("import", ovf , *name_params, *disk_params) do |type, data| if type == :stdout # Keep track of the stdout so that we can get the VM name output << data elsif type == :stderr # Append the data so we can see the full view total << data # Break up the lines. We can't get the progress until we see an "OK" lines = total.split("\n") if lines.include?("OK.") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. match = /.+(\d{2})%/.match(lines.last) if match current = match[1].to_i if current > last last = current yield current if block_given? end end end end end output = execute("list", "vms") match = /^"#{Regexp.escape(specified_name)}" \{(.+?)\}$/.match(output) return match[1].to_s if match nil end def max_network_adapters 36 end def read_forwarded_ports(uuid=nil, active_only=false) uuid ||= @uuid @logger.debug("read_forward_ports: uuid=#{uuid} active_only=#{active_only}") results = [] current_nic = nil info = execute("showvminfo", uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| # This is how we find the nic that a FP is attached to, # since this comes first. current_nic = $1.to_i if line =~ /^nic(\d+)=".+?"$/ # If we care about active VMs only, then we check the state # to verify the VM is running. if active_only && line =~ /^VMState="(.+?)"$/ && $1.to_s != "running" return [] end # Parse out the forwarded port information if line =~ /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/ result = [current_nic, $1.to_s, $2.to_i, $3.to_i] @logger.debug(" - #{result.inspect}") results << result end end results end def read_bridged_interfaces execute("list", "bridgedifs").split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end # Return the info to build up the results info end end def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", :retryable => true) if output =~ /^Value: (.+?)$/ # Split the version by _ since some distro versions modify it # to look like this: 4.1.2_ubuntu, and the distro part isn't # too important. value = $1.to_s return value.split("_").first end # If we can't get the guest additions version by guest property, try # to get it from the VM info itself. info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| return $1.to_s if line =~ /^GuestAdditionsVersion="(.+?)"$/ end return nil end def read_guest_ip(adapter_number) read_guest_property("/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP") end def read_guest_property(property) output = execute("guestproperty", "get", @uuid, property) if output =~ /^Value: (.+?)$/ $1.to_s else raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, :guest_property => property end end def read_host_only_interfaces dhcp = {} execute("list", "dhcpservers", :retryable => true).split("\n\n").each do |block| info = {} block.split("\n").each do |line| if line =~ /^NetworkName:\s+HostInterfaceNetworking-(.+?)$/ info[:network] = $1.to_s elsif line =~ /^IP:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^lowerIPAddress:\s+(.+?)$/ info[:lower] = $1.to_s elsif line =~ /^upperIPAddress:\s+(.+?)$/ info[:upper] = $1.to_s end end # Set the DHCP info dhcp[info[:network]] = info end execute("list", "hostonlyifs", :retryable => true).split("\n\n").collect do |block| info = {} block.split("\n").each do |line| if line =~ /^Name:\s+(.+?)$/ info[:name] = $1.to_s elsif line =~ /^IPAddress:\s+(.+?)$/ info[:ip] = $1.to_s elsif line =~ /^NetworkMask:\s+(.+?)$/ info[:netmask] = $1.to_s elsif line =~ /^Status:\s+(.+?)$/ info[:status] = $1.to_s end end # Set the DHCP info if it exists info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]] info end end def read_mac_address info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| return $1.to_s if line =~ /^macaddress1="(.+?)"$/ end nil end def read_mac_addresses macs = {} info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if matcher = /^macaddress(\d+)="(.+?)"$/.match(line) adapter = matcher[1].to_i mac = matcher[2].to_s macs[adapter] = mac end end macs end def read_machine_folder execute("list", "systemproperties", :retryable => true).split("\n").each do |line| if line =~ /^Default machine folder:\s+(.+?)$/i return $1.to_s end end nil end def read_network_interfaces nics = {} info = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) info.split("\n").each do |line| if line =~ /^nic(\d+)="(.+?)"$/ adapter = $1.to_i type = $2.to_sym nics[adapter] ||= {} nics[adapter][:type] = type elsif line =~ /^hostonlyadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:hostonly] = network elsif line =~ /^bridgeadapter(\d+)="(.+?)"$/ adapter = $1.to_i network = $2.to_s nics[adapter] ||= {} nics[adapter][:bridge] = network end end nics end def read_state output = execute("showvminfo", @uuid, "--machinereadable", :retryable => true) if output =~ /^name=""$/ return :inaccessible elsif output =~ /^VMState="(.+?)"$/ return $1.to_sym end nil end def read_used_ports ports = [] execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^".+?" \{(.+?)\}$/ uuid = $1.to_s # Ignore our own used ports next if uuid == @uuid read_forwarded_ports(uuid, true).each do |_, _, hostport, _| ports << hostport end end end ports end def read_vms results = {} execute("list", "vms", :retryable => true).split("\n").each do |line| if line =~ /^"(.+?)" \{(.+?)\}$/ results[$1.to_s] = $2.to_s end end results end def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end def set_name(name) execute("modifyvm", @uuid, "--name", name, :retryable => true) end def share_folders(folders) folders.each do |folder| args = ["--name", folder[:name], "--hostpath", folder[:hostpath]] args << "--transient" if folder.has_key?(:transient) && folder[:transient] # Add the shared folder execute("sharedfolder", "add", @uuid, *args) # Enable symlinks on the shared folder execute("setextradata", @uuid, "VBoxInternal2/SharedFoldersEnableSymlinksCreate/#{folder[:name]}", "1") end end def ssh_port(expected_port) @logger.debug("Searching for SSH port: #{expected_port.inspect}") # Look for the forwarded port only by comparing the guest port read_forwarded_ports.each do |_, _, hostport, guestport| return hostport if guestport == expected_port end nil end def resume @logger.debug("Resuming paused VM...") execute("controlvm", @uuid, "resume") end def start(mode) command = ["startvm", @uuid, "--type", mode.to_s] r = raw(*command) if r.exit_code == 0 || r.stdout =~ /VM ".+?" has been successfully started/ # Some systems return an exit code 1 for some reason. For that # we depend on the output. return true end # If we reached this point then it didn't work out. raise Vagrant::Errors::VBoxManageError, :command => command.inspect end def suspend execute("controlvm", @uuid, "savestate") end def verify! # This command sometimes fails if kernel drivers aren't properly loaded # so we just run the command and verify that it succeeded. execute("list", "hostonlyifs") end def verify_image(path) r = raw("import", path.to_s, "--dry-run") return r.exit_code == 0 end def vm_exists?(uuid) 5.times do |i| result = raw("showvminfo", uuid) return true if result.exit_code == 0 # GH-2479: Sometimes this happens. In this case, retry. If # we don't see this text, the VM really probably doesn't exist. return false if !result.stderr.include?("CO_E_SERVER_EXEC_FAILURE") # Sleep a bit though to give VirtualBox time to fix itself sleep 2 end # If we reach this point, it means that we consistently got the # failure, do a standard vboxmanage now. This will raise an # exception if it fails again. execute("showvminfo", uuid) return true end end end end end vagrant-1.4.3/plugins/providers/virtualbox/model/000077500000000000000000000000001226132634600221635ustar00rootroot00000000000000vagrant-1.4.3/plugins/providers/virtualbox/model/forwarded_port.rb000066400000000000000000000037251226132634600255400ustar00rootroot00000000000000module VagrantPlugins module ProviderVirtualBox module Model # Represents a single forwarded port for VirtualBox. This has various # helpers and defaults for a forwarded port. class ForwardedPort # The NAT adapter on which to attach the forwarded port. # # @return [Integer] attr_reader :adapter # If true, this port should be auto-corrected. # # @return [Boolean] attr_reader :auto_correct # The unique ID for the forwarded port. # # @return [String] attr_reader :id # The protocol to forward. # # @return [String] attr_reader :protocol # The IP that the forwarded port will connect to on the guest machine. # # @return [String] attr_reader :guest_ip # The port on the guest to be exposed on the host. # # @return [Integer] attr_reader :guest_port # The IP that the forwarded port will bind to on the host machine. # # @return [String] attr_reader :host_ip # The port on the host used to access the port on the guest. # # @return [Integer] attr_reader :host_port def initialize(id, host_port, guest_port, options) @id = id @guest_port = guest_port @host_port = host_port options ||= {} @auto_correct = false @auto_correct = options[:auto_correct] if options.has_key?(:auto_correct) @adapter = (options[:adapter] || 1).to_i @guest_ip = options[:guest_ip] || nil @host_ip = options[:host_ip] || nil @protocol = options[:protocol] || "tcp" end # This corrects the host port and changes it to the given new port. # # @param [Integer] new_port The new port def correct_host_port(new_port) @host_port = new_port end end end end end vagrant-1.4.3/plugins/providers/virtualbox/plugin.rb000066400000000000000000000027441226132634600227150ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module ProviderVirtualBox class Plugin < Vagrant.plugin("2") name "VirtualBox provider" description <<-EOF The VirtualBox provider allows Vagrant to manage and control VirtualBox-based virtual machines. EOF provider(:virtualbox) do require File.expand_path("../provider", __FILE__) Provider end config(:virtualbox, :provider) do require File.expand_path("../config", __FILE__) Config end synced_folder(:virtualbox) do require File.expand_path("../synced_folder", __FILE__) SyncedFolder end end autoload :Action, File.expand_path("../action", __FILE__) # Drop some autoloads in here to optimize the performance of loading # our drivers only when they are needed. module Driver autoload :Meta, File.expand_path("../driver/meta", __FILE__) autoload :Version_4_0, File.expand_path("../driver/version_4_0", __FILE__) autoload :Version_4_1, File.expand_path("../driver/version_4_1", __FILE__) autoload :Version_4_2, File.expand_path("../driver/version_4_2", __FILE__) autoload :Version_4_3, File.expand_path("../driver/version_4_3", __FILE__) end module Model autoload :ForwardedPort, File.expand_path("../model/forwarded_port", __FILE__) end module Util autoload :CompileForwardedPorts, File.expand_path("../util/compile_forwarded_ports", __FILE__) end end end vagrant-1.4.3/plugins/providers/virtualbox/provider.rb000066400000000000000000000056271226132634600232540ustar00rootroot00000000000000require "log4r" module VagrantPlugins module ProviderVirtualBox class Provider < Vagrant.plugin("2", :provider) attr_reader :driver def initialize(machine) @logger = Log4r::Logger.new("vagrant::provider::virtualbox") @machine = machine # This method will load in our driver, so we call it now to # initialize it. machine_id_changed end # @see Vagrant::Plugin::V1::Provider#action def action(name) # Attempt to get the action method from the Action class if it # exists, otherwise return nil to show that we don't support the # given action. action_method = "action_#{name}" return Action.send(action_method) if Action.respond_to?(action_method) nil end # If the machine ID changed, then we need to rebuild our underlying # driver. def machine_id_changed id = @machine.id begin @logger.debug("Instantiating the driver for machine ID: #{@machine.id.inspect}") @driver = Driver::Meta.new(id) rescue Driver::Meta::VMNotFound # The virtual machine doesn't exist, so we probably have a stale # ID. Just clear the id out of the machine and reload it. @logger.debug("VM not found! Clearing saved machine ID and reloading.") id = nil retry end end # Returns the SSH info for accessing the VirtualBox VM. def ssh_info # If the VM is not created then we cannot possibly SSH into it, so # we return nil. return nil if state.id == :not_created # Return what we know. The host is always "127.0.0.1" because # VirtualBox VMs are always local. The port we try to discover # by reading the forwarded ports. return { :host => "127.0.0.1", :port => @driver.ssh_port(@machine.config.ssh.guest_port) } end # Return the state of VirtualBox virtual machine by actually # querying VBoxManage. # # @return [Symbol] def state # XXX: What happens if we destroy the VM but the UUID is still # set here? # Determine the ID of the state here. state_id = nil state_id = :not_created if !@driver.uuid state_id = @driver.read_state if !state_id state_id = :unknown if !state_id # Translate into short/long descriptions short = state_id.to_s.gsub("_", " ") long = I18n.t("vagrant.commands.status.#{state_id}") # Return the state Vagrant::MachineState.new(state_id, short, long) end # Returns a human-friendly string version of this provider which # includes the machine's ID that this provider represents, if it # has one. # # @return [String] def to_s id = @machine.id ? @machine.id : "new VM" "VirtualBox (#{id})" end end end end vagrant-1.4.3/plugins/providers/virtualbox/synced_folder.rb000066400000000000000000000045601226132634600242350ustar00rootroot00000000000000require "vagrant/util/platform" module VagrantPlugins module ProviderVirtualBox class SyncedFolder < Vagrant.plugin("2", :synced_folder) def usable?(machine) # These synced folders only work if the provider if VirtualBox machine.provider_name == :virtualbox end def prepare(machine, folders, _opts) defs = [] folders.each do |id, data| hostpath = Vagrant::Util::Platform.cygwin_windows_path(data[:hostpath]) defs << { name: id, hostpath: hostpath.to_s, transient: data[:transient], } end driver(machine).share_folders(defs) end def enable(machine, folders, _opts) # short guestpaths first, so we don't step on ourselves folders = folders.sort_by do |id, data| if data[:guestpath] data[:guestpath].length else # A long enough path to just do this at the end. 10000 end end # Go through each folder and mount machine.ui.info(I18n.t("vagrant.actions.vm.share_folders.mounting")) folders.each do |id, data| if data[:guestpath] # Guest path specified, so mount the folder to specified point machine.ui.info(I18n.t("vagrant.actions.vm.share_folders.mounting_entry", :guest_path => data[:guestpath])) # Dup the data so we can pass it to the guest API data = data.dup # Calculate the owner and group ssh_info = machine.ssh_info data[:owner] ||= ssh_info[:username] data[:group] ||= ssh_info[:username] # Mount the actual folder machine.guest.capability( :mount_virtualbox_shared_folder, id, data[:guestpath], data) else # If no guest path is specified, then automounting is disabled machine.ui.info(I18n.t("vagrant.actions.vm.share_folders.nomount_entry", :host_path => data[:hostpath])) end end end def cleanup(machine, opts) driver(machine).clear_shared_folders if machine.id && machine.id != "" end protected # This is here so that we can stub it for tests def driver(machine) machine.provider.driver end end end end vagrant-1.4.3/plugins/providers/virtualbox/util/000077500000000000000000000000001226132634600220405ustar00rootroot00000000000000vagrant-1.4.3/plugins/providers/virtualbox/util/compile_forwarded_ports.rb000066400000000000000000000020221226132634600272750ustar00rootroot00000000000000require "vagrant/util/scoped_hash_override" module VagrantPlugins module ProviderVirtualBox module Util module CompileForwardedPorts include Vagrant::Util::ScopedHashOverride # This method compiles the forwarded ports into {ForwardedPort} # models. def compile_forwarded_ports(config) mappings = {} config.vm.networks.each do |type, options| if type == :forwarded_port guest_port = options[:guest] host_port = options[:host] protocol = options[:protocol] || "tcp" options = scoped_hash_override(options, :virtualbox) id = options[:id] # If the forwarded port was marked as disabled, ignore. next if options[:disabled] mappings[host_port.to_s + protocol.to_s] = Model::ForwardedPort.new(id, host_port, guest_port, options) end end mappings.values end end end end end vagrant-1.4.3/plugins/provisioners/000077500000000000000000000000001226132634600174115ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/ansible/000077500000000000000000000000001226132634600210265ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/ansible/config.rb000066400000000000000000000101301226132634600226130ustar00rootroot00000000000000module VagrantPlugins module Ansible class Config < Vagrant.plugin("2", :config) attr_accessor :playbook attr_accessor :extra_vars attr_accessor :inventory_path attr_accessor :ask_sudo_pass attr_accessor :limit attr_accessor :sudo attr_accessor :sudo_user attr_accessor :verbose attr_accessor :tags attr_accessor :skip_tags attr_accessor :start_at_task attr_accessor :groups attr_accessor :host_key_checking # Joker attribute, used to pass unsupported arguments to ansible anyway attr_accessor :raw_arguments def initialize @playbook = UNSET_VALUE @extra_vars = UNSET_VALUE @inventory_path = UNSET_VALUE @ask_sudo_pass = UNSET_VALUE @limit = UNSET_VALUE @sudo = UNSET_VALUE @sudo_user = UNSET_VALUE @verbose = UNSET_VALUE @tags = UNSET_VALUE @skip_tags = UNSET_VALUE @start_at_task = UNSET_VALUE @raw_arguments = UNSET_VALUE @groups = UNSET_VALUE @host_key_checking = "true" end def finalize! @playbook = nil if @playbook == UNSET_VALUE @extra_vars = nil if @extra_vars == UNSET_VALUE @inventory_path = nil if @inventory_path == UNSET_VALUE @ask_sudo_pass = nil if @ask_sudo_pass == UNSET_VALUE @limit = nil if @limit == UNSET_VALUE @sudo = nil if @sudo == UNSET_VALUE @sudo_user = nil if @sudo_user == UNSET_VALUE @verbose = nil if @verbose == UNSET_VALUE @tags = nil if @tags == UNSET_VALUE @skip_tags = nil if @skip_tags == UNSET_VALUE @start_at_task = nil if @start_at_task == UNSET_VALUE @raw_arguments = nil if @raw_arguments == UNSET_VALUE @groups = {} if @groups == UNSET_VALUE @host_key_checking = nil if @host_key_checking == UNSET_VALUE end def validate(machine) errors = _detected_errors # Validate that a playbook path was provided if !playbook errors << I18n.t("vagrant.provisioners.ansible.no_playbook") end # Validate the existence of said playbook on the host if playbook expanded_path = Pathname.new(playbook).expand_path(machine.env.root_path) if !expanded_path.file? errors << I18n.t("vagrant.provisioners.ansible.playbook_path_invalid", :path => expanded_path) end end # Validate that extra_vars is either a hash, or a path to an # existing file if extra_vars extra_vars_is_valid = extra_vars.kind_of?(Hash) || extra_vars.kind_of?(String) if extra_vars.kind_of?(String) # Accept the usage of '@' prefix in Vagrantfile (e.g. '@vars.yml' # and 'vars.yml' are both supported) match_data = /^@?(.+)$/.match(extra_vars) extra_vars_path = match_data[1].to_s expanded_path = Pathname.new(extra_vars_path).expand_path(machine.env.root_path) extra_vars_is_valid = expanded_path.exist? if extra_vars_is_valid @extra_vars = '@' + extra_vars_path end end if !extra_vars_is_valid errors << I18n.t("vagrant.provisioners.ansible.extra_vars_invalid", :type => extra_vars.class.to_s, :value => extra_vars.to_s ) end end # Validate the existence of the inventory_path, if specified if inventory_path expanded_path = Pathname.new(inventory_path).expand_path(machine.env.root_path) if !expanded_path.exist? errors << I18n.t("vagrant.provisioners.ansible.inventory_path_invalid", :path => expanded_path) end end { "ansible provisioner" => errors } end end end end vagrant-1.4.3/plugins/provisioners/ansible/plugin.rb000066400000000000000000000007761226132634600226630ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Ansible class Plugin < Vagrant.plugin("2") name "ansible" description <<-DESC Provides support for provisioning your virtual machines with Ansible playbooks. DESC config(:ansible, :provisioner) do require File.expand_path("../config", __FILE__) Config end provisioner(:ansible) do require File.expand_path("../provisioner", __FILE__) Provisioner end end end end vagrant-1.4.3/plugins/provisioners/ansible/provisioner.rb000066400000000000000000000114751226132634600237420ustar00rootroot00000000000000module VagrantPlugins module Ansible class Provisioner < Vagrant.plugin("2", :provisioner) def provision ssh = @machine.ssh_info # Connect with Vagrant user (unless --user or --private-key are # overidden by 'raw_arguments'). # # TODO: multiple private key support options = %W[--private-key=#{ssh[:private_key_path][0]} --user=#{ssh[:username]}] # Joker! Not (yet) supported arguments can be passed this way. options.concat(self.as_array(config.raw_arguments)) if config.raw_arguments # Append Provisioner options (highest precedence): options << "--inventory-file=#{self.setup_inventory_file}" options << "--extra-vars=#{self.get_extra_vars_argument}" if config.extra_vars options << "--sudo" if config.sudo options << "--sudo-user=#{config.sudo_user}" if config.sudo_user options << "#{self.get_verbosity_argument}" if config.verbose options << "--ask-sudo-pass" if config.ask_sudo_pass options << "--tags=#{as_list_argument(config.tags)}" if config.tags options << "--skip-tags=#{as_list_argument(config.skip_tags)}" if config.skip_tags options << "--limit=#{as_list_argument(config.limit)}" if config.limit options << "--start-at-task=#{config.start_at_task}" if config.start_at_task # Assemble the full ansible-playbook command command = (%w(ansible-playbook) << options << config.playbook).flatten # Write stdout and stderr data, since it's the regular Ansible output command << { :env => { "ANSIBLE_FORCE_COLOR" => "true", "ANSIBLE_HOST_KEY_CHECKING" => "#{config.host_key_checking}", # Ensure Ansible output isn't buffered so that we receive ouput # on a task-by-task basis. "PYTHONUNBUFFERED" => 1 }, :notify => [:stdout, :stderr], :workdir => @machine.env.root_path.to_s } begin result = Vagrant::Util::Subprocess.execute(*command) do |type, data| if type == :stdout || type == :stderr @machine.env.ui.info(data, :new_line => false, :prefix => false) end end raise Vagrant::Errors::AnsibleFailed if result.exit_code != 0 rescue Vagrant::Util::Subprocess::LaunchError raise Vagrant::Errors::AnsiblePlaybookAppNotFound end end protected # Auto-generate "safe" inventory file based on Vagrantfile, # unless inventory_path is explicitly provided def setup_inventory_file return config.inventory_path if config.inventory_path ssh = @machine.ssh_info generated_inventory_file = @machine.env.root_path.join("vagrant_ansible_inventory_#{machine.name}") generated_inventory_file.open('w') do |file| file.write("# Generated by Vagrant\n\n") file.write("#{machine.name} ansible_ssh_host=#{ssh[:host]} ansible_ssh_port=#{ssh[:port]}\n") # Write out groups information. Only include current # machine and its groups to avoid Ansible errors on # provisioning. groups_of_groups = {} included_groups = [] config.groups.each_pair do |gname, gmembers| if gname.end_with?(":children") groups_of_groups[gname] = gmembers elsif gmembers.include?("#{machine.name}") included_groups << gname file.write("\n[#{gname}]\n") file.write("#{machine.name}\n") end end groups_of_groups.each_pair do |gname, gmembers| unless (included_groups & gmembers).empty? file.write("\n[#{gname}]\n") gmembers.each do |gm| file.write("#{gm}\n") if included_groups.include?(gm) end end end end return generated_inventory_file.to_s end def get_extra_vars_argument if config.extra_vars.kind_of?(String) and config.extra_vars =~ /^@.+$/ # A JSON or YAML file is referenced (requires Ansible 1.3+) return config.extra_vars else # Expected to be a Hash after config validation. (extra_vars as # JSON requires Ansible 1.2+, while YAML requires Ansible 1.3+) return config.extra_vars.to_json end end def get_verbosity_argument if config.verbose.to_s =~ /^v+$/ # ansible-playbook accepts "silly" arguments like '-vvvvv' as '-vvvv' for now return "-#{config.verbose}" else # safe default, in case input strays return '-v' end end def as_list_argument(v) v.kind_of?(Array) ? v.join(',') : v end def as_array(v) v.kind_of?(Array) ? v : [v] end end end end vagrant-1.4.3/plugins/provisioners/cfengine/000077500000000000000000000000001226132634600211675ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/cfengine/cap/000077500000000000000000000000001226132634600217325ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/cfengine/cap/debian/000077500000000000000000000000001226132634600231545ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/cfengine/cap/debian/cfengine_install.rb000066400000000000000000000012171226132634600270060ustar00rootroot00000000000000module VagrantPlugins module CFEngine module Cap module Debian module CFEngineInstall def self.cfengine_install(machine, config) machine.communicate.tap do |comm| comm.sudo("mkdir -p #{File.dirname(config.deb_repo_file)} && /bin/echo #{config.deb_repo_line} > #{config.deb_repo_file}") comm.sudo("GPGFILE=`tempfile`; wget -O $GPGFILE #{config.repo_gpg_key_url} && apt-key add $GPGFILE; rm -f $GPGFILE") comm.sudo("apt-get update") comm.sudo("apt-get install -y #{config.package_name}") end end end end end end end vagrant-1.4.3/plugins/provisioners/cfengine/cap/linux/000077500000000000000000000000001226132634600230715ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/cfengine/cap/linux/cfengine_installed.rb000066400000000000000000000005171226132634600272360ustar00rootroot00000000000000module VagrantPlugins module CFEngine module Cap module Linux module CFEngineInstalled def self.cfengine_installed(machine) machine.communicate.test( "test -d /var/cfengine && test -x /var/cfengine/bin/cf-agent", sudo: true) end end end end end end vagrant-1.4.3/plugins/provisioners/cfengine/cap/linux/cfengine_needs_bootstrap.rb000066400000000000000000000023211226132634600304450ustar00rootroot00000000000000require "log4r" module VagrantPlugins module CFEngine module Cap module Linux module CFEngineNeedsBootstrap def self.cfengine_needs_bootstrap(machine, config) logger = Log4r::Logger.new("vagrant::plugins::cfengine::cap_linux_cfengine_bootstrap") machine.communicate.tap do |comm| # We hardcode fixing the permissions on /var/cfengine/ppkeys/, if it exists, # because otherwise CFEngine will fail to bootstrap. if comm.test("test -d /var/cfengine/ppkeys", :sudo => true) logger.debug("Fixing permissions on /var/cfengine/ppkeys") comm.sudo("chmod -R 600 /var/cfengine/ppkeys") end logger.debug("Checking if CFEngine is bootstrapped...") bootstrapped = comm.test("test -f /var/cfengine/policy_server.dat", :sudo => true) if bootstrapped && !config.force_bootstrap logger.info("CFEngine already bootstrapped, no need to do it again") return false end logger.info("CFEngine needs bootstrap.") return true end end end end end end end vagrant-1.4.3/plugins/provisioners/cfengine/cap/redhat/000077500000000000000000000000001226132634600232015ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/cfengine/cap/redhat/cfengine_install.rb000066400000000000000000000020301226132634600270250ustar00rootroot00000000000000require "log4r" module VagrantPlugins module CFEngine module Cap module RedHat module CFEngineInstall def self.cfengine_install(machine, config) logger = Log4r::Logger.new("vagrant::plugins::cfengine::cap_redhat_cfengine_install") machine.communicate.tap do |comm| logger.info("Adding the CFEngine repository to #{config.yum_repo_file}") comm.sudo("mkdir -p #{File.dirname(config.yum_repo_file)} && (echo '[cfengine-repository]'; echo 'name=CFEngine Community Yum Repository'; echo 'baseurl=#{config.yum_repo_url}'; echo 'enabled=1'; echo 'gpgcheck=1') > #{config.yum_repo_file}") logger.info("Installing CFEngine Community Yum Repository GPG KEY from #{config.repo_gpg_key_url}") comm.sudo("GPGFILE=$(mktemp) && wget -O $GPGFILE #{config.repo_gpg_key_url} && rpm --import $GPGFILE; rm -f $GPGFILE") comm.sudo("yum -y install #{config.package_name}") end end end end end end end vagrant-1.4.3/plugins/provisioners/cfengine/cap/suse/000077500000000000000000000000001226132634600227115ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/cfengine/cap/suse/cfengine_install.rb000066400000000000000000000011321226132634600265370ustar00rootroot00000000000000module VagrantPlugins module CFEngine module Cap module SuSE module CFEngineInstall def self.cfengine_install(machine, config) machine.communicate.tap do |comm| comm.sudo("GPGFILE=$(mktemp) && wget -O $GPGFILE #{config.repo_gpg_key_url} && rpm --import $GPGFILE; rm -f $GPGFILE") comm.sudo("zypper addrepo -t YUM #{config.yum_repo_url} cfengine-repository") comm.sudo("zypper se #{config.package_name} && zypper -n install #{config.package_name}") end end end end end end end vagrant-1.4.3/plugins/provisioners/cfengine/config.rb000066400000000000000000000074751226132634600227760ustar00rootroot00000000000000require "pathname" require "vagrant" module VagrantPlugins module CFEngine class Config < Vagrant.plugin("2", :config) attr_accessor :am_policy_hub attr_accessor :extra_agent_args attr_accessor :classes attr_accessor :deb_repo_file attr_accessor :deb_repo_line attr_accessor :files_path attr_accessor :force_bootstrap attr_accessor :install attr_accessor :mode attr_accessor :policy_server_address attr_accessor :repo_gpg_key_url attr_accessor :run_file attr_accessor :upload_path attr_accessor :yum_repo_file attr_accessor :yum_repo_url attr_accessor :package_name def initialize @am_policy_hub = UNSET_VALUE @classes = UNSET_VALUE @deb_repo_file = UNSET_VALUE @deb_repo_line = UNSET_VALUE @extra_agent_args = UNSET_VALUE @files_path = UNSET_VALUE @force_bootstrap = UNSET_VALUE @install = UNSET_VALUE @mode = UNSET_VALUE @policy_server_address = UNSET_VALUE @repo_gpg_key_url = UNSET_VALUE @run_file = UNSET_VALUE @upload_path = UNSET_VALUE @yum_repo_file = UNSET_VALUE @yum_repo_url = UNSET_VALUE @package_name = UNSET_VALUE end def finalize! @am_policy_hub = false if @am_policy_hub == UNSET_VALUE @classes = nil if @classes == UNSET_VALUE if @deb_repo_file == UNSET_VALUE @deb_repo_file = "/etc/apt/sources.list.d/cfengine-community.list" end if @deb_repo_line == UNSET_VALUE @deb_repo_line = "deb http://cfengine.com/pub/apt $(lsb_release -cs) main" end @extra_agent_args = nil if @extra_agent_args == UNSET_VALUE @files_path = nil if @files_path == UNSET_VALUE @force_bootstrap = false if @force_bootstrap == UNSET_VALUE @install = true if @install == UNSET_VALUE @install = @install.to_sym if @install.respond_to?(:to_sym) @mode = :bootstrap if @mode == UNSET_VALUE @mode = @mode.to_sym @run_file = nil if @run_file == UNSET_VALUE @policy_server_address = nil if @policy_server_address == UNSET_VALUE if @repo_gpg_key_url == UNSET_VALUE @repo_gpg_key_url = "http://cfengine.com/pub/gpg.key" end @upload_path = "/tmp/vagrant-cfengine-file" if @upload_path == UNSET_VALUE if @yum_repo_file == UNSET_VALUE @yum_repo_file = "/etc/yum.repos.d/cfengine-community.repo" end if @yum_repo_url == UNSET_VALUE @yum_repo_url = "http://cfengine.com/pub/yum/" end if @package_name == UNSET_VALUE @package_name = "cfengine-community" end end def validate(machine) errors = _detected_errors valid_modes = [:bootstrap, :single_run] errors << I18n.t("vagrant.cfengine_config.invalid_mode") if !valid_modes.include?(@mode) if @mode == :bootstrap if !@policy_server_address && !@am_policy_hub errors << I18n.t("vagrant.cfengine_config.policy_server_address") end end if @classes && !@classes.is_a?(Array) errors << I18n.t("vagrant.cfengine_config.classes_array") end if @files_path expanded = Pathname.new(@files_path).expand_path(machine.env.root_path) if !expanded.directory? errors << I18n.t("vagrant.cfengine_config.files_path_not_directory") end end if @run_file expanded = Pathname.new(@run_file).expand_path(machine.env.root_path) if !expanded.file? errors << I18n.t("vagrant.cfengine_config.run_file_not_found") end end { "CFEngine" => errors } end end end end vagrant-1.4.3/plugins/provisioners/cfengine/plugin.rb000066400000000000000000000023331226132634600230130ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module CFEngine class Plugin < Vagrant.plugin("2") name "CFEngine Provisioner" description <<-DESC Provisions machines with CFEngine. DESC config(:cfengine, :provisioner) do require_relative "config" Config end guest_capability("debian", "cfengine_install") do require_relative "cap/debian/cfengine_install" Cap::Debian::CFEngineInstall end guest_capability("linux", "cfengine_needs_bootstrap") do require_relative "cap/linux/cfengine_needs_bootstrap" Cap::Linux::CFEngineNeedsBootstrap end guest_capability("linux", "cfengine_installed") do require_relative "cap/linux/cfengine_installed" Cap::Linux::CFEngineInstalled end guest_capability("redhat", "cfengine_install") do require_relative "cap/redhat/cfengine_install" Cap::RedHat::CFEngineInstall end guest_capability("suse", "cfengine_install") do require_relative "cap/suse/cfengine_install" Cap::SuSE::CFEngineInstall end provisioner(:cfengine) do require_relative "provisioner" Provisioner end end end end vagrant-1.4.3/plugins/provisioners/cfengine/provisioner.rb000066400000000000000000000117411226132634600240770ustar00rootroot00000000000000require "log4r" require "vagrant" module VagrantPlugins module CFEngine class Provisioner < Vagrant.plugin("2", :provisioner) def provision @logger = Log4r::Logger.new("vagrant::plugins::cfengine") @logger.info("Checking for CFEngine installation...") handle_cfengine_installation if @config.files_path @machine.ui.info(I18n.t("vagrant.cfengine_installing_files_path")) install_files(Pathname.new(@config.files_path).expand_path(@machine.env.root_path)) end handle_cfengine_bootstrap if @config.mode == :bootstrap if @config.mode == :single_run # Just let people know @machine.ui.info(I18n.t("vagrant.cfengine_single_run")) end if @config.run_file @machine.ui.info(I18n.t("vagrant.cfengine_single_run_execute")) path = Pathname.new(@config.run_file).expand_path(@machine.env.root_path) machine.communicate.upload(path.to_s, @config.upload_path) cfagent("-KI -f #{@config.upload_path}#{cfagent_classes_args}#{cfagent_extra_args}") end end protected # This runs cf-agent with the given arguments. def cfagent(args, options=nil) options ||= {} command = "/var/cfengine/bin/cf-agent #{args}" @machine.communicate.sudo(command, error_check: options[:error_check]) do |type, data| if [:stderr, :stdout].include?(type) # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red @machine.ui.info( data, :color => color, :new_line => false, :prefix => false) end end end # Returns the arguments for the classes configuration if they are # set. # # @return [String] def cfagent_classes_args return "" if !@config.classes args = @config.classes.map { |c| "-D#{c}" }.join(" ") return " #{args}" end # Extra arguments for calles to cf-agent. # # @return [String] def cfagent_extra_args return "" if !@config.extra_agent_args return " #{@config.extra_agent_args}" end # This handles checking if the CFEngine installation needs to # be bootstrapped, and bootstraps if it does. def handle_cfengine_bootstrap @logger.info("Bootstrapping CFEngine...") if !@machine.guest.capability(:cfengine_needs_bootstrap, @config) @machine.ui.info(I18n.t("vagrant.cfengine_no_bootstrap")) return end # Needs bootstrap, let's determine the parameters policy_server_address = @config.policy_server_address if !policy_server_address policy_server_address = @machine.guest.capability(:read_ip_address) raise Vagrant::Errors::CFEngineCantAutodetectIP if !policy_server_address @machine.ui.info(I18n.t("vagrant.cfengine_detected_ip", address: policy_server_address)) end @machine.ui.info(I18n.t("vagrant.cfengine_bootstrapping", policy_server: policy_server_address)) result = cfagent("--bootstrap #{policy_server_address}", error_check: false) raise Vagrant::Errors::CFEngineBootstrapFailed if result != 0 # Policy hubs need to do additional things before they're ready # to accept agents. Force that run now... if @config.am_policy_hub @machine.ui.info(I18n.t("vagrant.cfengine_bootstrapping_policy_hub")) cfagent("-KI -f /var/cfengine/masterfiles/failsafe.cf#{cfagent_classes_args}") cfagent("-KI #{cfagent_classes_args}#{cfagent_extra_args}") end end # This handles verifying the CFEngine installation, installing it # if it was requested, and so on. This method will raise exceptions # if things are wrong. def handle_cfengine_installation if !@machine.guest.capability?(:cfengine_installed) @machine.ui.warn(I18n.t("vagrant.cfengine_cant_detect")) return end installed = @machine.guest.capability(:cfengine_installed) if !installed || @config.install == :force raise Vagrant::Errors::CFEngineNotInstalled if !@config.install @machine.ui.info(I18n.t("vagrant.cfengine_installing")) @machine.guest.capability(:cfengine_install, @config) if !@machine.guest.capability(:cfengine_installed) raise Vagrant::Errors::CFEngineInstallFailed end end end # This installs a set of files into the CFEngine folder within # the machine. # # @param [Pathname] local_path def install_files(local_path) @logger.debug("Copying local files to CFEngine: #{local_path}") @machine.communicate.sudo("rm -rf /tmp/cfengine-files") @machine.communicate.upload(local_path.to_s, "/tmp/cfengine-files") @machine.communicate.sudo("cp -R /tmp/cfengine-files/* /var/cfengine") end end end end vagrant-1.4.3/plugins/provisioners/chef/000077500000000000000000000000001226132634600203165ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/chef/config/000077500000000000000000000000001226132634600215635ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/chef/config/base.rb000066400000000000000000000111761226132634600230300ustar00rootroot00000000000000require "vagrant/util/counter" module VagrantPlugins module Chef module Config class Base < Vagrant.plugin("2", :config) extend Vagrant::Util::Counter attr_accessor :arguments attr_accessor :attempts attr_accessor :binary_path attr_accessor :binary_env attr_accessor :custom_config_path attr_accessor :formatter attr_accessor :http_proxy attr_accessor :http_proxy_user attr_accessor :http_proxy_pass attr_accessor :https_proxy attr_accessor :https_proxy_user attr_accessor :https_proxy_pass attr_accessor :json attr_accessor :log_level attr_accessor :no_proxy attr_accessor :node_name attr_accessor :provisioning_path attr_accessor :run_list attr_accessor :file_cache_path attr_accessor :file_backup_path attr_accessor :verbose_logging def initialize super @arguments = UNSET_VALUE @attempts = UNSET_VALUE @binary_path = UNSET_VALUE @binary_env = UNSET_VALUE @custom_config_path = UNSET_VALUE @formatter = UNSET_VALUE @http_proxy = UNSET_VALUE @http_proxy_user = UNSET_VALUE @http_proxy_pass = UNSET_VALUE @https_proxy = UNSET_VALUE @https_proxy_user = UNSET_VALUE @https_proxy_pass = UNSET_VALUE @log_level = UNSET_VALUE @no_proxy = UNSET_VALUE @node_name = UNSET_VALUE @provisioning_path = UNSET_VALUE @file_cache_path = UNSET_VALUE @file_backup_path = UNSET_VALUE @verbose_logging = UNSET_VALUE @json = {} @run_list = [] end def finalize! @arguments = nil if @arguments == UNSET_VALUE @attempts = 1 if @attempts == UNSET_VALUE @binary_path = nil if @binary_path == UNSET_VALUE @binary_env = nil if @binary_env == UNSET_VALUE @custom_config_path = nil if @custom_config_path == UNSET_VALUE @formatter = nil if @formatter == UNSET_VALUE @http_proxy = nil if @http_proxy == UNSET_VALUE @http_proxy_user = nil if @http_proxy_user == UNSET_VALUE @http_proxy_pass = nil if @http_proxy_pass == UNSET_VALUE @https_proxy = nil if @https_proxy == UNSET_VALUE @https_proxy_user = nil if @https_proxy_user == UNSET_VALUE @https_proxy_pass = nil if @https_proxy_pass == UNSET_VALUE @log_level = :info if @log_level == UNSET_VALUE @no_proxy = nil if @no_proxy == UNSET_VALUE @node_name = nil if @node_name == UNSET_VALUE @provisioning_path = nil if @provisioning_path == UNSET_VALUE @file_backup_path = "/var/chef/backup" if @file_backup_path == UNSET_VALUE @file_cache_path = "/var/chef/cache" if @file_cache_path == UNSET_VALUE @verbose_logging = false if @verbose_logging == UNSET_VALUE # Make sure the log level is a symbol @log_level = @log_level.to_sym # Set the default provisioning path to be a unique path in /tmp if !@provisioning_path counter = self.class.get_and_update_counter(:chef_config) @provisioning_path = "/tmp/vagrant-chef-#{counter}" end end def merge(other) super.tap do |result| result.instance_variable_set(:@json, @json.merge(other.json)) result.instance_variable_set(:@run_list, (@run_list + other.run_list)) end end # Just like the normal configuration "validate" method except that # it returns an array of errors that should be merged into some # other error accumulator. def validate_base(machine) errors = _detected_errors if @custom_config_path expanded = File.expand_path(@custom_config_path, machine.env.root_path) if !File.file?(expanded) errors << I18n.t("vagrant.config.chef.custom_config_path_missing") end end errors end # Adds a recipe to the run list def add_recipe(name) name = "recipe[#{name}]" unless name =~ /^recipe\[(.+?)\]$/ run_list << name end # Adds a role to the run list def add_role(name) name = "role[#{name}]" unless name =~ /^role\[(.+?)\]$/ run_list << name end end end end end vagrant-1.4.3/plugins/provisioners/chef/config/chef_client.rb000066400000000000000000000050611226132634600243550ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "vagrant/util/which" module VagrantPlugins module Chef module Config class ChefClient < Base attr_accessor :chef_server_url attr_accessor :client_key_path attr_accessor :delete_client attr_accessor :delete_node attr_accessor :encrypted_data_bag_secret_key_path attr_accessor :environment attr_accessor :validation_key_path attr_accessor :validation_client_name def initialize super @chef_server_url = UNSET_VALUE @client_key_path = UNSET_VALUE @delete_client = UNSET_VALUE @delete_node = UNSET_VALUE @encrypted_data_bag_secret_key_path = UNSET_VALUE @environment = UNSET_VALUE @validation_key_path = UNSET_VALUE @validation_client_name = UNSET_VALUE end def encrypted_data_bag_secret=(value) puts "DEPRECATION: Chef encrypted_data_bag_secret has no effect anymore." puts "Remove this from your Vagrantfile since it'll be removed in the next" puts "Vagrant version." end def finalize! super @chef_server_url = nil if @chef_server_url == UNSET_VALUE @client_key_path = "/etc/chef/client.pem" if @client_key_path == UNSET_VALUE @delete_client = false if @delete_client == UNSET_VALUE @delete_node = false if @delete_node == UNSET_VALUE @encrypted_data_bag_secret_key_path = nil if @encrypted_data_bag_secret_key_path == UNSET_VALUE @environment = nil if @environment == UNSET_VALUE @validation_client_name = "chef-validator" if @validation_client_name == UNSET_VALUE @validation_key_path = nil if @validation_key_path == UNSET_VALUE end def validate(machine) errors = _detected_errors errors.concat(validate_base(machine)) errors << I18n.t("vagrant.config.chef.server_url_empty") if \ !chef_server_url || chef_server_url.strip == "" errors << I18n.t("vagrant.config.chef.validation_key_path") if \ !validation_key_path if delete_client || delete_node if !Vagrant::Util::Which.which("knife") errors << I18n.t("vagrant.chef_config_knife_not_found") end end { "chef client provisioner" => errors } end end end end end vagrant-1.4.3/plugins/provisioners/chef/config/chef_solo.rb000066400000000000000000000073361226132634600240620ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) module VagrantPlugins module Chef module Config class ChefSolo < Base attr_accessor :cookbooks_path attr_accessor :data_bags_path attr_accessor :encrypted_data_bag_secret_key_path attr_accessor :encrypted_data_bag_secret attr_accessor :environments_path attr_accessor :environment attr_accessor :nfs attr_accessor :recipe_url attr_accessor :roles_path def initialize super @cookbooks_path = UNSET_VALUE @data_bags_path = UNSET_VALUE @environments_path = UNSET_VALUE @environment = UNSET_VALUE @recipe_url = UNSET_VALUE @roles_path = UNSET_VALUE @nfs = UNSET_VALUE @encrypted_data_bag_secret = UNSET_VALUE @encrypted_data_bag_secret_key_path = UNSET_VALUE end #------------------------------------------------------------ # Internal methods #------------------------------------------------------------ def finalize! super @recipe_url = nil if @recipe_url == UNSET_VALUE @environment = nil if @environment == UNSET_VALUE if @cookbooks_path == UNSET_VALUE @cookbooks_path = [] @cookbooks_path << [:host, "cookbooks"] if !@recipe_url @cookbooks_path << [:vm, "cookbooks"] end @data_bags_path = [] if @data_bags_path == UNSET_VALUE @roles_path = [] if @roles_path == UNSET_VALUE @environments_path = [] if @environments_path == UNSET_VALUE @environments_path = [@environments_path].flatten # Make sure the path is an array. @cookbooks_path = prepare_folders_config(@cookbooks_path) @data_bags_path = prepare_folders_config(@data_bags_path) @roles_path = prepare_folders_config(@roles_path) @environments_path = prepare_folders_config(@environments_path) @nfs = false if @nfs == UNSET_VALUE @encrypted_data_bag_secret = "/tmp/encrypted_data_bag_secret" if \ @encrypted_data_bag_secret == UNSET_VALUE @encrypted_data_bag_secret_key_path = nil if \ @encrypted_data_bag_secret_key_path == UNSET_VALUE end def validate(machine) errors = _detected_errors errors.concat(validate_base(machine)) errors << I18n.t("vagrant.config.chef.cookbooks_path_empty") if \ !cookbooks_path || [cookbooks_path].flatten.empty? errors << I18n.t("vagrant.config.chef.environment_path_required") if \ environment && environments_path.empty? environments_path.each do |type, raw_path| next if type != :host path = Pathname.new(raw_path).expand_path(machine.env.root_path) if !path.directory? errors << I18n.t("vagrant.config.chef.environment_path_missing", path: raw_path.to_s) end end { "chef solo provisioner" => errors } end protected # This takes any of the configurations that take a path or # array of paths and turns it into the proper format. # # @return [Array] def prepare_folders_config(config) # Make sure the path is an array config = [config] if !config.is_a?(Array) || config.first.is_a?(Symbol) # Make sure all the paths are in the proper format config.map do |path| path = [:host, path] if !path.is_a?(Array) path end end end end end end vagrant-1.4.3/plugins/provisioners/chef/plugin.rb000066400000000000000000000015471226132634600221500ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Chef class Plugin < Vagrant.plugin("2") name "chef" description <<-DESC Provides support for provisioning your virtual machines with Chef via `chef-solo` or `chef-client`. DESC config(:chef_solo, :provisioner) do require File.expand_path("../config/chef_solo", __FILE__) Config::ChefSolo end config(:chef_client, :provisioner) do require File.expand_path("../config/chef_client", __FILE__) Config::ChefClient end provisioner(:chef_solo) do require File.expand_path("../provisioner/chef_solo", __FILE__) Provisioner::ChefSolo end provisioner(:chef_client) do require File.expand_path("../provisioner/chef_client", __FILE__) Provisioner::ChefClient end end end end vagrant-1.4.3/plugins/provisioners/chef/provisioner/000077500000000000000000000000001226132634600226755ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/chef/provisioner/base.rb000066400000000000000000000100231226132634600241300ustar00rootroot00000000000000require 'tempfile' require "vagrant/util/template_renderer" module VagrantPlugins module Chef module Provisioner # This class is a base class where the common functionality shared between # chef-solo and chef-client provisioning are stored. This is **not an actual # provisioner**. Instead, {ChefSolo} or {ChefServer} should be used. class Base < Vagrant.plugin("2", :provisioner) class ChefError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.chef") end def verify_binary(binary) # Checks for the existence of chef binary and error if it # doesn't exist. @machine.communicate.sudo( "which #{binary}", :error_class => ChefError, :error_key => :chef_not_detected, :binary => binary) end # Returns the path to the Chef binary, taking into account the # `binary_path` configuration option. def chef_binary_path(binary) return binary if !@config.binary_path return File.join(@config.binary_path, binary) end def chown_provisioning_folder paths = [@config.provisioning_path, @config.file_backup_path, @config.file_cache_path] @machine.communicate.tap do |comm| paths.each do |path| comm.sudo("mkdir -p #{path}") comm.sudo("chown #{@machine.ssh_info[:username]} #{path}") end end end def setup_config(template, filename, template_vars) # If we have custom configuration, upload it remote_custom_config_path = nil if @config.custom_config_path expanded = File.expand_path( @config.custom_config_path, @machine.env.root_path) remote_custom_config_path = File.join( config.provisioning_path, "custom-config.rb") @machine.communicate.upload(expanded, remote_custom_config_path) end config_file = Vagrant::Util::TemplateRenderer.render(template, { :custom_configuration => remote_custom_config_path, :file_cache_path => @config.file_cache_path, :file_backup_path => @config.file_backup_path, :log_level => @config.log_level.to_sym, :verbose_logging => @config.verbose_logging, :http_proxy => @config.http_proxy, :http_proxy_user => @config.http_proxy_user, :http_proxy_pass => @config.http_proxy_pass, :https_proxy => @config.https_proxy, :https_proxy_user => @config.https_proxy_user, :https_proxy_pass => @config.https_proxy_pass, :no_proxy => @config.no_proxy, :formatter => @config.formatter }.merge(template_vars)) # Create a temporary file to store the data so we # can upload it temp = Tempfile.new("vagrant") temp.write(config_file) temp.close remote_file = File.join(config.provisioning_path, filename) @machine.communicate.tap do |comm| comm.sudo("rm -f #{remote_file}", :error_check => false) comm.upload(temp.path, remote_file) end end def setup_json @machine.env.ui.info I18n.t("vagrant.provisioners.chef.json") # Get the JSON that we're going to expose to Chef json = @config.json json[:run_list] = @config.run_list if !@config.run_list.empty? json = JSON.pretty_generate(json) # Create a temporary file to store the data so we # can upload it temp = Tempfile.new("vagrant") temp.write(json) temp.close remote_file = File.join(@config.provisioning_path, "dna.json") @machine.communicate.tap do |comm| comm.sudo("rm -f #{remote_file}", :error_check => false) comm.upload(temp.path, remote_file) end end end end end end vagrant-1.4.3/plugins/provisioners/chef/provisioner/chef_client.rb000066400000000000000000000117731226132634600254760ustar00rootroot00000000000000require 'pathname' require 'vagrant/util/subprocess' require File.expand_path("../base", __FILE__) module VagrantPlugins module Chef module Provisioner # This class implements provisioning via chef-client, allowing provisioning # with a chef server. class ChefClient < Base def configure(root_config) raise ChefError, :server_validation_key_required if @config.validation_key_path.nil? raise ChefError, :server_validation_key_doesnt_exist if !File.file?(validation_key_path) raise ChefError, :server_url_required if @config.chef_server_url.nil? end def provision verify_binary(chef_binary_path("chef-client")) chown_provisioning_folder create_client_key_folder upload_validation_key upload_encrypted_data_bag_secret if @config.encrypted_data_bag_secret_key_path setup_json setup_server_config run_chef_client end def cleanup delete_from_chef_server('client') if @config.delete_client delete_from_chef_server('node') if @config.delete_node end def create_client_key_folder @machine.env.ui.info I18n.t("vagrant.provisioners.chef.client_key_folder") path = Pathname.new(@config.client_key_path) @machine.communicate.sudo("mkdir -p #{path.dirname}") end def upload_validation_key @machine.env.ui.info I18n.t("vagrant.provisioners.chef.upload_validation_key") @machine.communicate.upload(validation_key_path, guest_validation_key_path) end def upload_encrypted_data_bag_secret @machine.env.ui.info I18n.t("vagrant.provisioners.chef.upload_encrypted_data_bag_secret_key") @machine.communicate.upload(encrypted_data_bag_secret_key_path, guest_encrypted_data_bag_secret_key_path) end def setup_server_config setup_config("provisioners/chef_client/client", "client.rb", { :node_name => @config.node_name, :chef_server_url => @config.chef_server_url, :validation_client_name => @config.validation_client_name, :validation_key => guest_validation_key_path, :client_key => @config.client_key_path, :environment => @config.environment, :encrypted_data_bag_secret => guest_encrypted_data_bag_secret_key_path, }) end def run_chef_client if @config.run_list && @config.run_list.empty? @machine.ui.warn(I18n.t("vagrant.chef_run_list_empty")) end command_env = @config.binary_env ? "#{@config.binary_env} " : "" command_args = @config.arguments ? " #{@config.arguments}" : "" command = "#{command_env}#{chef_binary_path("chef-client")} " + "-c #{@config.provisioning_path}/client.rb " + "-j #{@config.provisioning_path}/dna.json #{command_args}" @config.attempts.times do |attempt| if attempt == 0 @machine.env.ui.info I18n.t("vagrant.provisioners.chef.running_client") else @machine.env.ui.info I18n.t("vagrant.provisioners.chef.running_client_again") end exit_status = @machine.communicate.sudo(command, :error_check => false) do |type, data| # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red @machine.env.ui.info( data, :color => color, :new_line => false, :prefix => false) end # There is no need to run Chef again if it converges return if exit_status == 0 end # If we reached this point then Chef never converged! Error. raise ChefError, :no_convergence end def validation_key_path File.expand_path(@config.validation_key_path, @machine.env.root_path) end def encrypted_data_bag_secret_key_path File.expand_path(@config.encrypted_data_bag_secret_key_path, @machine.env.root_path) end def guest_encrypted_data_bag_secret_key_path File.join(@config.provisioning_path, "encrypted_data_bag_secret_key.pem") end def guest_validation_key_path File.join(@config.provisioning_path, "validation.pem") end def delete_from_chef_server(deletable) node_name = @config.node_name || @machine.config.vm.hostname @machine.env.ui.info(I18n.t( "vagrant.provisioners.chef.deleting_from_server", deletable: deletable, name: node_name)) command = ["knife", deletable, "delete", "--yes", node_name] r = Vagrant::Util::Subprocess.execute(*command) if r.exit_code != 0 @machine.env.ui.error(I18n.t( "vagrant.chef_client_cleanup_failed", deletable: deletable, stdout: r.stdout, stderr: r.stderr)) end end end end end end vagrant-1.4.3/plugins/provisioners/chef/provisioner/chef_solo.rb000066400000000000000000000203711226132634600251660ustar00rootroot00000000000000require "log4r" require "vagrant/util/counter" require File.expand_path("../base", __FILE__) module VagrantPlugins module Chef module Provisioner # This class implements provisioning via chef-solo. class ChefSolo < Base extend Vagrant::Util::Counter include Vagrant::Util::Counter attr_reader :environments_folders attr_reader :cookbook_folders attr_reader :role_folders attr_reader :data_bags_folders def initialize(machine, config) super @logger = Log4r::Logger.new("vagrant::provisioners::chef_solo") end def configure(root_config) @cookbook_folders = expanded_folders(@config.cookbooks_path, "cookbooks") @role_folders = expanded_folders(@config.roles_path, "roles") @data_bags_folders = expanded_folders(@config.data_bags_path, "data_bags") @environments_folders = expanded_folders(@config.environments_path, "environments") share_folders(root_config, "csc", @cookbook_folders) share_folders(root_config, "csr", @role_folders) share_folders(root_config, "csdb", @data_bags_folders) share_folders(root_config, "cse", @environments_folders) end def provision # Verify that the proper shared folders exist. check = [] [@cookbook_folders, @role_folders, @data_bags_folders, @environments_folders].each do |folders| folders.each do |type, local_path, remote_path| # We only care about checking folders that have a local path, meaning # they were shared from the local machine, rather than assumed to # exist on the VM. check << remote_path if local_path end end chown_provisioning_folder verify_shared_folders(check) verify_binary(chef_binary_path("chef-solo")) upload_encrypted_data_bag_secret if @config.encrypted_data_bag_secret_key_path setup_json setup_solo_config run_chef_solo delete_encrypted_data_bag_secret end # Converts paths to a list of properly expanded paths with types. def expanded_folders(paths, appended_folder=nil) # Convert the path to an array if it is a string or just a single # path element which contains the folder location (:host or :vm) paths = [paths] if paths.is_a?(String) || paths.first.is_a?(Symbol) results = [] paths.each do |type, path| # Create the local/remote path based on whether this is a host # or VM path. local_path = nil remote_path = nil if type == :host # Get the expanded path that the host path points to local_path = File.expand_path(path, @machine.env.root_path) if File.exist?(local_path) # Path exists on the host, setup the remote path remote_path = "#{@config.provisioning_path}/chef-solo-#{get_and_update_counter(:cookbooks_path)}" else @machine.ui.warn(I18n.t("vagrant.provisioners.chef.cookbook_folder_not_found_warning", path: local_path.to_s)) next end else # Path already exists on the virtual machine. Expand it # relative to where we're provisioning. remote_path = File.expand_path(path, @config.provisioning_path) # Remove drive letter if running on a windows host. This is a bit # of a hack but is the most portable way I can think of at the moment # to achieve this. Otherwise, Vagrant attempts to share at some crazy # path like /home/vagrant/c:/foo/bar remote_path = remote_path.gsub(/^[a-zA-Z]:/, "") end # If we have specified a folder name to append then append it remote_path += "/#{appended_folder}" if appended_folder # Append the result results << [type, local_path, remote_path] end results end # Shares the given folders with the given prefix. The folders should # be of the structure resulting from the `expanded_folders` function. def share_folders(root_config, prefix, folders) folders.each do |type, local_path, remote_path| if type == :host root_config.vm.synced_folder( local_path, remote_path, :id => "v-#{prefix}-#{self.class.get_and_update_counter(:shared_folder)}", :nfs => @config.nfs) end end end def delete_encrypted_data_bag_secret @machine.communicate.tap do |comm| comm.sudo("rm -f #{@config.encrypted_data_bag_secret}", error_check: false) end end def upload_encrypted_data_bag_secret @machine.env.ui.info I18n.t("vagrant.provisioners.chef.upload_encrypted_data_bag_secret_key") @machine.communicate.tap do |comm| comm.sudo("rm -f #{@config.encrypted_data_bag_secret}", :error_check => false) comm.upload(encrypted_data_bag_secret_key_path, @config.encrypted_data_bag_secret) end end def setup_solo_config cookbooks_path = guest_paths(@cookbook_folders) roles_path = guest_paths(@role_folders).first data_bags_path = guest_paths(@data_bags_folders).first environments_path = guest_paths(@environments_folders).first setup_config("provisioners/chef_solo/solo", "solo.rb", { :node_name => @config.node_name, :cookbooks_path => cookbooks_path, :recipe_url => @config.recipe_url, :roles_path => roles_path, :data_bags_path => data_bags_path, :encrypted_data_bag_secret => @config.encrypted_data_bag_secret, :environments_path => environments_path, :environment => @config.environment, }) end def run_chef_solo if @config.run_list && @config.run_list.empty? @machine.ui.warn(I18n.t("vagrant.chef_run_list_empty")) end options = [ "-c #{@config.provisioning_path}/solo.rb", "-j #{@config.provisioning_path}/dna.json" ] if !@machine.env.ui.is_a?(Vagrant::UI::Colored) options << "--no-color" end command_env = @config.binary_env ? "#{@config.binary_env} " : "" command_args = @config.arguments ? " #{@config.arguments}" : "" command = "#{command_env}#{chef_binary_path("chef-solo")} " + "#{options.join(" ")} #{command_args}" @config.attempts.times do |attempt| if attempt == 0 @machine.env.ui.info I18n.t("vagrant.provisioners.chef.running_solo") else @machine.env.ui.info I18n.t("vagrant.provisioners.chef.running_solo_again") end exit_status = @machine.communicate.sudo(command, :error_check => false) do |type, data| # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red @machine.env.ui.info( data, :color => color, :new_line => false, :prefix => false) end # There is no need to run Chef again if it converges return if exit_status == 0 end # If we reached this point then Chef never converged! Error. raise ChefError, :no_convergence end def verify_shared_folders(folders) folders.each do |folder| @logger.debug("Checking for shared folder: #{folder}") if !@machine.communicate.test("test -d #{folder}", sudo: true) raise ChefError, :missing_shared_folders end end end def encrypted_data_bag_secret_key_path File.expand_path(@config.encrypted_data_bag_secret_key_path, @machine.env.root_path) end protected # Extracts only the remote paths from a list of folders def guest_paths(folders) folders.map { |parts| parts[2] } end end end end end vagrant-1.4.3/plugins/provisioners/docker/000077500000000000000000000000001226132634600206605ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/docker/cap/000077500000000000000000000000001226132634600214235ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/docker/cap/debian/000077500000000000000000000000001226132634600226455ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/docker/cap/debian/docker_configure_auto_start.rb000066400000000000000000000006641226132634600307550ustar00rootroot00000000000000module VagrantPlugins module Docker module Cap module Debian module DockerConfigureAutoStart def self.docker_configure_auto_start(machine) if ! machine.communicate.test('grep -q \'\-r=true\' /etc/init/docker.conf') machine.communicate.sudo("sed -i.bak 's/docker -d/docker -d -r=true/' /etc/init/docker.conf ") end end end end end end end vagrant-1.4.3/plugins/provisioners/docker/cap/debian/docker_install.rb000066400000000000000000000022351226132634600261710ustar00rootroot00000000000000module VagrantPlugins module Docker module Cap module Debian module DockerInstall def self.docker_install(machine, version) package = 'lxc-docker' package << "-#{version}" if version != :latest machine.communicate.tap do |comm| # TODO: Perform check on the host machine if aufs is installed and using LXC if machine.provider_name != :lxc comm.sudo("lsmod | grep aufs || modprobe aufs || apt-get install -y linux-image-extra-`uname -r`") end comm.sudo("apt-get install -y --force-yes -q curl") comm.sudo("curl http://get.docker.io/gpg | apt-key add -") comm.sudo("echo deb http://get.docker.io/ubuntu docker main > /etc/apt/sources.list.d/docker.list") comm.sudo("apt-get update") comm.sudo("echo lxc lxc/directory string /var/lib/lxc | debconf-set-selections") comm.sudo("apt-get install -y --force-yes -q xz-utils #{package} -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold'") end end end end end end end vagrant-1.4.3/plugins/provisioners/docker/cap/debian/docker_start_service.rb000066400000000000000000000004171226132634600274000ustar00rootroot00000000000000module VagrantPlugins module Docker module Cap module Debian module DockerStartService def self.docker_start_service(machine) machine.communicate.sudo("service docker start") end end end end end end vagrant-1.4.3/plugins/provisioners/docker/cap/linux/000077500000000000000000000000001226132634600225625ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/docker/cap/linux/docker_configure_vagrant_user.rb000066400000000000000000000005131226132634600311760ustar00rootroot00000000000000module VagrantPlugins module Docker module Cap module Linux module DockerConfigureVagrantUser def self.docker_configure_vagrant_user(machine) machine.communicate.sudo("usermod -a -G docker #{machine.config.ssh.username || "vagrant"}") end end end end end end vagrant-1.4.3/plugins/provisioners/docker/cap/linux/docker_installed.rb000066400000000000000000000004261226132634600264170ustar00rootroot00000000000000module VagrantPlugins module Docker module Cap module Linux module DockerInstalled def self.docker_installed(machine) machine.communicate.test("test -f /usr/bin/docker", sudo: true) end end end end end end vagrant-1.4.3/plugins/provisioners/docker/cap/redhat/000077500000000000000000000000001226132634600226725ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/docker/cap/redhat/docker_configure_auto_start.rb000066400000000000000000000006641226132634600310020ustar00rootroot00000000000000module VagrantPlugins module Docker module Cap module Redhat module DockerConfigureAutoStart def self.docker_configure_auto_start(machine) if ! machine.communicate.test('grep -q \'\-r=true\' /etc/sysconfig/docker') machine.communicate.sudo("sed -i.bak 's/docker -d/docker -d -r=true/' /etc/sysconfig/docker ") end end end end end end end vagrant-1.4.3/plugins/provisioners/docker/cap/redhat/docker_install.rb000066400000000000000000000012701226132634600262140ustar00rootroot00000000000000module VagrantPlugins module Docker module Cap module Redhat module DockerInstall def self.docker_install(machine, version) if version != :latest machine.ui.warn(I18n.t("vagrant.docker_install_with_version_not_supported")) end machine.communicate.tap do |comm| if ! comm.test("rpm -qa | grep epel-release") comm.sudo("rpm -ivh http://dl.fedoraproject.org/pub/epel/6/x86_64/epel-release-6-8.noarch.rpm") end comm.sudo("yum -y upgrade") comm.sudo("yum -y install docker-io") end end end end end end end vagrant-1.4.3/plugins/provisioners/docker/cap/redhat/docker_start_service.rb000066400000000000000000000006061226132634600274250ustar00rootroot00000000000000module VagrantPlugins module Docker module Cap module Redhat module DockerStartService def self.docker_start_service(machine) machine.communicate.sudo("service docker start") # TODO :: waiting to start sleep 5 machine.communicate.sudo("chkconfig docker on") end end end end end end vagrant-1.4.3/plugins/provisioners/docker/client.rb000066400000000000000000000050461226132634600224700ustar00rootroot00000000000000require 'digest/sha1' module VagrantPlugins module Docker class Client def initialize(machine) @machine = machine end def pull_images(*images) @machine.communicate.tap do |comm| images.each do |image| @machine.ui.info(I18n.t("vagrant.docker_pulling_single", name: image)) comm.sudo("docker images | grep -q #{image} || docker pull #{image}") end end end def start_service if !daemon_running? && @machine.guest.capability?(:docker_start_service) @machine.guest.capability(:docker_start_service) end end def daemon_running? @machine.communicate.test('test -f /var/run/docker.pid') end def run(containers) containers.each do |name, config| cids_dir = "/var/lib/vagrant/cids" config[:cidfile] ||= "#{cids_dir}/#{Digest::SHA1.hexdigest name}" @machine.ui.info(I18n.t("vagrant.docker_running", name: name)) @machine.communicate.sudo("mkdir -p #{cids_dir}") run_container({ name: name }.merge(config)) end end def run_container(config) raise "Container's cidfile was not provided!" if !config[:cidfile] id = "$(cat #{config[:cidfile]})" if container_exists?(id) start_container(id) else create_container(config) end end def container_exists?(id) lookup_container(id, true) end def start_container(id) if !container_running?(id) @machine.communicate.sudo("docker start #{id}") end end def container_running?(id) lookup_container(id) end def create_container(config) args = "-cidfile=#{config[:cidfile]} -d " args << config[:args] if config[:args] @machine.communicate.sudo %[ rm -f #{config[:cidfile]} docker run #{args} #{config[:image]} #{config[:cmd]} ] end def lookup_container(id, list_all = false) docker_ps = "sudo docker ps -q" docker_ps << " -a" if list_all @machine.communicate.tap do |comm| # Docker < 0.7.0 stores container IDs using its short version while # recent versions use the full container ID # See https://github.com/dotcloud/docker/pull/2140 for more information return comm.test("#{docker_ps} | grep -wFq #{id}") || comm.test("#{docker_ps} -notrunc | grep -wFq #{id}") end end end end end vagrant-1.4.3/plugins/provisioners/docker/config.rb000066400000000000000000000016411226132634600224540ustar00rootroot00000000000000require 'set' module VagrantPlugins module Docker class Config < Vagrant.plugin("2", :config) attr_reader :images, :containers attr_accessor :version def initialize @images = Set.new @containers = Hash.new @version = UNSET_VALUE end def images=(images) @images = Set.new(images) end def pull_images(*images) @images += images.map(&:to_s) end def run(name, **options) params = options.dup params[:image] ||= name # TODO: Validate provided parameters before assignment @containers[name.to_s] = params end def finalize! @version = "latest" if @version == UNSET_VALUE @version = @version.to_sym end def merge(other) super.tap do |result| result.pull_images(*(other.images + self.images)) end end end end end vagrant-1.4.3/plugins/provisioners/docker/installer.rb000066400000000000000000000023741226132634600232100ustar00rootroot00000000000000module VagrantPlugins module Docker class Installer def initialize(machine, version) @machine = machine @version = version end # This handles verifying the Docker installation, installing it if it was # requested, and so on. This method will raise exceptions if things are # wrong. def ensure_installed if !@machine.guest.capability?(:docker_installed) @machine.ui.warn(I18n.t("vagrant.docker_cant_detect")) return end if !@machine.guest.capability(:docker_installed) @machine.ui.info(I18n.t("vagrant.docker_installing", version: @version.to_s)) @machine.guest.capability(:docker_install, @version) if !@machine.guest.capability(:docker_installed) raise DockerError, :install_failed end end if @machine.guest.capability?(:docker_configure_auto_start) @machine.guest.capability(:docker_configure_auto_start) else @machine.env.ui.warn I18n.t('vagrant.docker_auto_start_not_available') end if @machine.guest.capability?(:docker_configure_vagrant_user) @machine.guest.capability(:docker_configure_vagrant_user) end end end end end vagrant-1.4.3/plugins/provisioners/docker/plugin.rb000066400000000000000000000034701226132634600225070ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Docker class Plugin < Vagrant.plugin("2") name "docker" description <<-DESC Provides support for provisioning your virtual machines with Docker images and containers. DESC config(:docker, :provisioner) do require_relative "config" Config end guest_capability("debian", "docker_install") do require_relative "cap/debian/docker_install" Cap::Debian::DockerInstall end guest_capability("debian", "docker_configure_auto_start") do require_relative "cap/debian/docker_configure_auto_start" Cap::Debian::DockerConfigureAutoStart end guest_capability("debian", "docker_start_service") do require_relative "cap/debian/docker_start_service" Cap::Debian::DockerStartService end guest_capability("redhat", "docker_install") do require_relative "cap/redhat/docker_install" Cap::Redhat::DockerInstall end guest_capability("redhat", "docker_configure_auto_start") do require_relative "cap/redhat/docker_configure_auto_start" Cap::Redhat::DockerConfigureAutoStart end guest_capability("redhat", "docker_start_service") do require_relative "cap/redhat/docker_start_service" Cap::Redhat::DockerStartService end guest_capability("linux", "docker_installed") do require_relative "cap/linux/docker_installed" Cap::Linux::DockerInstalled end guest_capability("linux", "docker_configure_vagrant_user") do require_relative "cap/linux/docker_configure_vagrant_user" Cap::Linux::DockerConfigureVagrantUser end provisioner(:docker) do require_relative "provisioner" Provisioner end end end end vagrant-1.4.3/plugins/provisioners/docker/provisioner.rb000066400000000000000000000024441226132634600235700ustar00rootroot00000000000000require_relative "client" require_relative "installer" module VagrantPlugins module Docker class DockerError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.docker") end # TODO: Improve handling of vagrant-lxc specifics (like checking for apparmor # profile stuff + autocorrection) class Provisioner < Vagrant.plugin("2", :provisioner) def initialize(machine, config, installer = nil, client = nil) super(machine, config) @installer = installer || Installer.new(@machine, config.version) @client = client || Client.new(@machine) end def provision @logger = Log4r::Logger.new("vagrant::provisioners::docker") @logger.info("Checking for Docker installation...") @installer.ensure_installed # Attempt to start service if not running @client.start_service raise DockerError, :not_running if !@client.daemon_running? if config.images.any? @machine.ui.info(I18n.t("vagrant.docker_pulling_images")) @client.pull_images(*config.images) end if config.containers.any? @machine.ui.info(I18n.t("vagrant.docker_starting_containers")) @client.run(config.containers) end end end end end vagrant-1.4.3/plugins/provisioners/file/000077500000000000000000000000001226132634600203305ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/file/config.rb000066400000000000000000000013401226132634600221200ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module FileUpload class Config < Vagrant.plugin("2", :config) attr_accessor :source attr_accessor :destination def validate(machine) errors = _detected_errors if !source errors << I18n.t("vagrant.provisioners.file.no_source_file") end if !destination errors << I18n.t("vagrant.provisioners.file.no_dest_file") end if source s = File.expand_path(source) if ! File.exist?(s) errors << I18n.t("vagrant.provisioners.file.path_invalid", :path => s) end end { "File provisioner" => errors } end end end end vagrant-1.4.3/plugins/provisioners/file/plugin.rb000066400000000000000000000007651226132634600221630ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module FileUpload class Plugin < Vagrant.plugin("2") name "file" description <<-DESC Provides support for provisioning your virtual machines with uploaded files. DESC config(:file, :provisioner) do require File.expand_path("../config", __FILE__) Config end provisioner(:file) do require File.expand_path("../provisioner", __FILE__) Provisioner end end end end vagrant-1.4.3/plugins/provisioners/file/provisioner.rb000066400000000000000000000007121226132634600232340ustar00rootroot00000000000000module VagrantPlugins module FileUpload class Provisioner < Vagrant.plugin("2", :provisioner) def provision @machine.communicate.tap do |comm| # Make sure the remote path exists command = "mkdir -p %s" % File.dirname(config.destination) comm.execute(command) # now upload the file comm.upload(File.expand_path(config.source), config.destination) end end end end end vagrant-1.4.3/plugins/provisioners/puppet/000077500000000000000000000000001226132634600207265ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/puppet/config/000077500000000000000000000000001226132634600221735ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/puppet/config/puppet.rb000066400000000000000000000073041226132634600240410ustar00rootroot00000000000000require "vagrant/util/counter" module VagrantPlugins module Puppet module Config class Puppet < Vagrant.plugin("2", :config) extend Vagrant::Util::Counter attr_accessor :facter attr_accessor :hiera_config_path attr_accessor :manifest_file attr_accessor :manifests_path attr_accessor :module_path attr_accessor :options attr_accessor :temp_dir attr_accessor :working_directory attr_accessor :nfs def initialize super @hiera_config_path = UNSET_VALUE @manifest_file = UNSET_VALUE @manifests_path = UNSET_VALUE @module_path = UNSET_VALUE @options = [] @facter = {} @temp_dir = UNSET_VALUE @working_directory = UNSET_VALUE @nfs = UNSET_VALUE end def finalize! super if @manifests_path == UNSET_VALUE @manifests_path = [:host, "manifests"] end if @manifests_path && !@manifests_path.is_a?(Array) @manifests_path = [:host, @manifests_path] end @manifests_path[0] = @manifests_path[0].to_sym @hiera_config_path = nil if @hiera_config_path == UNSET_VALUE @manifest_file = "default.pp" if @manifest_file == UNSET_VALUE @module_path = nil if @module_path == UNSET_VALUE @temp_dir = nil if @temp_dir == UNSET_VALUE @working_directory = nil if @working_directory == UNSET_VALUE @nfs = false if @nfs == UNSET_VALUE # Set a default temp dir that has an increasing counter so # that multiple Puppet definitions won't overwrite each other if !@temp_dir counter = self.class.get_and_update_counter(:puppet_config) @temp_dir = "/tmp/vagrant-puppet-#{counter}" end end # Returns the module paths as an array of paths expanded relative to the # root path. def expanded_module_paths(root_path) return [] if !module_path # Get all the paths and expand them relative to the root path, returning # the array of expanded paths paths = module_path paths = [paths] if !paths.is_a?(Array) paths.map do |path| Pathname.new(path).expand_path(root_path) end end def validate(machine) errors = _detected_errors # Calculate the manifests and module paths based on env this_expanded_module_paths = expanded_module_paths(machine.env.root_path) # Manifests path/file validation if manifests_path[0].to_sym == :host expanded_path = Pathname.new(manifests_path[1]). expand_path(machine.env.root_path) if !expanded_path.directory? errors << I18n.t("vagrant.provisioners.puppet.manifests_path_missing", :path => expanded_path.to_s) else expanded_manifest_file = expanded_path.join(manifest_file) if !expanded_manifest_file.file? errors << I18n.t("vagrant.provisioners.puppet.manifest_missing", :manifest => expanded_manifest_file.to_s) end end end # Module paths validation this_expanded_module_paths.each do |path| if !path.directory? errors << I18n.t("vagrant.provisioners.puppet.module_path_missing", :path => path) end end { "puppet provisioner" => errors } end end end end end vagrant-1.4.3/plugins/provisioners/puppet/config/puppet_server.rb000066400000000000000000000042411226132634600254240ustar00rootroot00000000000000module VagrantPlugins module Puppet module Config class PuppetServer < Vagrant.plugin("2", :config) attr_accessor :client_cert_path attr_accessor :client_private_key_path attr_accessor :facter attr_accessor :options attr_accessor :puppet_server attr_accessor :puppet_node def initialize super @client_cert_path = UNSET_VALUE @client_private_key_path = UNSET_VALUE @facter = {} @options = [] @puppet_node = UNSET_VALUE @puppet_server = UNSET_VALUE end def finalize! super @client_cert_path = nil if @client_cert_path == UNSET_VALUE @client_private_key_path = nil if @client_private_key_path == UNSET_VALUE @puppet_node = nil if @puppet_node == UNSET_VALUE @puppet_server = "puppet" if @puppet_server == UNSET_VALUE end def validate(machine) errors = _detected_errors if (client_cert_path && !client_private_key_path) || (client_private_key_path && !client_cert_path) errors << I18n.t( "vagrant.provisioners.puppet_server.client_cert_and_private_key") end if client_cert_path path = Pathname.new(client_cert_path). expand_path(machine.env.root_path) if !path.file? errors << I18n.t( "vagrant.provisioners.puppet_server.client_cert_not_found") end end if client_private_key_path path = Pathname.new(client_private_key_path). expand_path(machine.env.root_path) if !path.file? errors << I18n.t( "vagrant.provisioners.puppet_server.client_private_key_not_found") end end if !puppet_node && (client_cert_path || client_private_key_path) errors << I18n.t( "vagrant.provisioners.puppet_server.cert_requires_node") end { "puppet server provisioner" => errors } end end end end end vagrant-1.4.3/plugins/provisioners/puppet/plugin.rb000066400000000000000000000015651226132634600225600ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Puppet class Plugin < Vagrant.plugin("2") name "puppet" description <<-DESC Provides support for provisioning your virtual machines with Puppet either using `puppet apply` or a Puppet server. DESC config(:puppet, :provisioner) do require File.expand_path("../config/puppet", __FILE__) Config::Puppet end config(:puppet_server, :provisioner) do require File.expand_path("../config/puppet_server", __FILE__) Config::PuppetServer end provisioner(:puppet) do require File.expand_path("../provisioner/puppet", __FILE__) Provisioner::Puppet end provisioner(:puppet_server) do require File.expand_path("../provisioner/puppet_server", __FILE__) Provisioner::PuppetServer end end end end vagrant-1.4.3/plugins/provisioners/puppet/provisioner/000077500000000000000000000000001226132634600233055ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/puppet/provisioner/puppet.rb000066400000000000000000000120501226132634600251450ustar00rootroot00000000000000require "log4r" module VagrantPlugins module Puppet module Provisioner class PuppetError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.puppet") end class Puppet < Vagrant.plugin("2", :provisioner) def initialize(machine, config) super @logger = Log4r::Logger.new("vagrant::provisioners::puppet") end def configure(root_config) # Calculate the paths we're going to use based on the environment root_path = @machine.env.root_path @expanded_module_paths = @config.expanded_module_paths(root_path) @manifest_file = File.join(manifests_guest_path, @config.manifest_file) # Setup the module paths @module_paths = [] @expanded_module_paths.each_with_index do |path, i| @module_paths << [path, File.join(config.temp_dir, "modules-#{i}")] end folder_opts = {} folder_opts[:nfs] = true if @config.nfs folder_opts[:owner] = "root" if !folder_opts[:nfs] # Share the manifests directory with the guest if @config.manifests_path[0].to_sym == :host root_config.vm.synced_folder( File.expand_path(@config.manifests_path[1], root_path), manifests_guest_path, folder_opts) end # Share the module paths @module_paths.each do |from, to| root_config.vm.synced_folder(from, to, folder_opts) end end def provision # Check that the shared folders are properly shared check = [] if @config.manifests_path[0] == :host check << manifests_guest_path end @module_paths.each do |host_path, guest_path| check << guest_path end # Make sure the temporary directory is properly set up @machine.communicate.tap do |comm| comm.sudo("mkdir -p #{config.temp_dir}") comm.sudo("chmod 0777 #{config.temp_dir}") end verify_shared_folders(check) # Verify Puppet is installed and run it verify_binary("puppet") # Upload Hiera configuration if we have it @hiera_config_path = nil if config.hiera_config_path local_hiera_path = File.expand_path(config.hiera_config_path, @machine.env.root_path) @hiera_config_path = File.join(config.temp_dir, "hiera.yaml") @machine.communicate.upload(local_hiera_path, @hiera_config_path) end run_puppet_apply end def manifests_guest_path if config.manifests_path[0] == :host # The path is on the host, so point to where it is shared File.join(config.temp_dir, "manifests") else # The path is on the VM, so just point directly to it config.manifests_path[1] end end def verify_binary(binary) @machine.communicate.sudo( "which #{binary}", :error_class => PuppetError, :error_key => :not_detected, :binary => binary) end def run_puppet_apply options = [config.options].flatten module_paths = @module_paths.map { |_, to| to } if !@module_paths.empty? # Add the command line switch to add the module path options << "--modulepath '#{module_paths.join(':')}'" end if @hiera_config_path options << "--hiera_config=#{@hiera_config_path}" end if !@machine.env.ui.is_a?(Vagrant::UI::Colored) options << "--color=false" end options << "--manifestdir #{manifests_guest_path}" options << "--detailed-exitcodes" options << @manifest_file options = options.join(" ") # Build up the custom facts if we have any facter = "" if !config.facter.empty? facts = [] config.facter.each do |key, value| facts << "FACTER_#{key}='#{value}'" end facter = "#{facts.join(" ")} " end command = "#{facter}puppet apply #{options} || [ $? -eq 2 ]" if config.working_directory command = "cd #{config.working_directory} && #{command}" end @machine.env.ui.info I18n.t("vagrant.provisioners.puppet.running_puppet", :manifest => config.manifest_file) @machine.communicate.sudo(command) do |type, data| if !data.empty? @machine.env.ui.info(data, :new_line => false, :prefix => false) end end end def verify_shared_folders(folders) folders.each do |folder| @logger.debug("Checking for shared folder: #{folder}") if !@machine.communicate.test("test -d #{folder}", sudo: true) raise PuppetError, :missing_shared_folders end end end end end end end vagrant-1.4.3/plugins/provisioners/puppet/provisioner/puppet_server.rb000066400000000000000000000064641226132634600265470ustar00rootroot00000000000000module VagrantPlugins module Puppet module Provisioner class PuppetServerError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.puppet_server") end class PuppetServer < Vagrant.plugin("2", :provisioner) def provision verify_binary("puppet") run_puppet_agent end def verify_binary(binary) @machine.communicate.sudo( "which #{binary}", :error_class => PuppetServerError, :error_key => :not_detected, :binary => binary) end def run_puppet_agent options = config.options options = [options] if !options.is_a?(Array) # Intelligently set the puppet node cert name based on certain # external parameters. cn = nil if config.puppet_node # If a node name is given, we use that directly for the certname cn = config.puppet_node elsif @machine.config.vm.hostname # If a host name is given, we explicitly set the certname to # nil so that the hostname becomes the cert name. cn = nil else # Otherwise, we default to the name of the box. cn = @machine.config.vm.box end # Add the certname option if there is one options += ["--certname", cn] if cn # A shortcut to make things easier comm = @machine.communicate # If we have client certs specified, then upload them if config.client_cert_path && config.client_private_key_path @machine.ui.info( I18n.t("vagrant.provisioners.puppet_server.uploading_client_cert")) dirname = "/tmp/puppet-#{Time.now.to_i}-#{rand(1000)}" comm.sudo("mkdir -p #{dirname}") comm.sudo("mkdir -p #{dirname}/certs") comm.sudo("mkdir -p #{dirname}/private_keys") comm.sudo("chmod -R 0777 #{dirname}") comm.upload(config.client_cert_path, "#{dirname}/certs/#{cn}.pem") comm.upload(config.client_private_key_path, "#{dirname}/private_keys/#{cn}.pem") # Setup the options so that they point to our directories options << "--certdir=#{dirname}/certs" options << "--privatekeydir=#{dirname}/private_keys" end # Disable colors if we must if !@machine.env.ui.is_a?(Vagrant::UI::Colored) options << "--color=false" end # Build up the custom facts if we have any facter = "" if !config.facter.empty? facts = [] config.facter.each do |key, value| facts << "FACTER_#{key}='#{value}'" end facter = "#{facts.join(" ")} " end options = options.join(" ") command = "#{facter}puppet agent --onetime --no-daemonize #{options} " + "--server #{config.puppet_server} --detailed-exitcodes || [ $? -eq 2 ]" @machine.ui.info I18n.t("vagrant.provisioners.puppet_server.running_puppetd") @machine.communicate.sudo(command) do |type, data| if !data.empty? @machine.ui.info(data, :new_line => false, :prefix => false) end end end end end end end vagrant-1.4.3/plugins/provisioners/salt/000077500000000000000000000000001226132634600203545ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/salt/bootstrap-salt.sh000077500000000000000000000006451226132634600236760ustar00rootroot00000000000000#!/bin/sh - # We just download the bootstrap script by default and execute that. if [ -x /usr/bin/fetch ]; then /usr/bin/fetch -o - http://bootstrap.saltstack.org | sh -s -- "$@" elif [ -x /usr/bin/curl ]; then /usr/bin/curl -L http://bootstrap.saltstack.org | sh -s -- "$@" else python \ -c 'import urllib; print urllib.urlopen("http://bootstrap.saltstack.org").read()' \ | sh -s -- "$@" fi vagrant-1.4.3/plugins/provisioners/salt/config.rb000066400000000000000000000072141226132634600221520ustar00rootroot00000000000000require "i18n" require "vagrant" module VagrantPlugins module Salt class Config < Vagrant.plugin("2", :config) ## salty-vagrant options attr_accessor :minion_config attr_accessor :minion_key attr_accessor :minion_pub attr_accessor :master_config attr_accessor :master_key attr_accessor :master_pub attr_accessor :run_highstate attr_accessor :run_overstate attr_accessor :always_install attr_accessor :bootstrap_script attr_accessor :verbose attr_accessor :seed_master attr_reader :pillar_data ## bootstrap options attr_accessor :temp_config_dir attr_accessor :install_type attr_accessor :install_args attr_accessor :install_master attr_accessor :install_syndic attr_accessor :no_minion attr_accessor :bootstrap_options def initialize @minion_config = UNSET_VALUE @minion_key = UNSET_VALUE @minion_pub = UNSET_VALUE @master_config = UNSET_VALUE @master_key = UNSET_VALUE @master_pub = UNSET_VALUE @run_highstate = UNSET_VALUE @run_overstate = UNSET_VALUE @always_install = UNSET_VALUE @bootstrap_script = UNSET_VALUE @verbose = UNSET_VALUE @seed_master = UNSET_VALUE @pillar_data = UNSET_VALUE @temp_config_dir = UNSET_VALUE @install_type = UNSET_VALUE @install_args = UNSET_VALUE @install_master = UNSET_VALUE @install_syndic = UNSET_VALUE @no_minion = UNSET_VALUE @bootstrap_options = UNSET_VALUE end def finalize! @minion_config = nil if @minion_config == UNSET_VALUE @minion_key = nil if @minion_key == UNSET_VALUE @minion_pub = nil if @minion_pub == UNSET_VALUE @master_config = nil if @master_config == UNSET_VALUE @master_key = nil if @master_key == UNSET_VALUE @master_pub = nil if @master_pub == UNSET_VALUE @run_highstate = nil if @run_highstate == UNSET_VALUE @run_overstate = nil if @run_overstate == UNSET_VALUE @always_install = nil if @always_install == UNSET_VALUE @bootstrap_script = nil if @bootstrap_script == UNSET_VALUE @verbose = nil if @verbose == UNSET_VALUE @seed_master = nil if @seed_master == UNSET_VALUE @pillar_data = {} if @pillar_data == UNSET_VALUE @temp_config_dir = nil if @temp_config_dir == UNSET_VALUE @install_type = nil if @install_type == UNSET_VALUE @install_args = nil if @install_args == UNSET_VALUE @install_master = nil if @install_master == UNSET_VALUE @install_syndic = nil if @install_syndic == UNSET_VALUE @no_minion = nil if @no_minion == UNSET_VALUE @bootstrap_options = nil if @bootstrap_options == UNSET_VALUE end def pillar(data) @pillar_data = {} if @pillar_data == UNSET_VALUE @pillar_data.deep_merge!(data) end def validate(machine) errors = _detected_errors if @minion_key || @minion_pub if !@minion_key || !@minion_pub errors << @minion_pub end end if @master_key && @master_pub if !@minion_key && !@minion_pub errors << I18n.t("vagrant.provisioners.salt.missing_key") end end if @install_master && !@no_minion && !@seed_master && @run_highstate errors << I18n.t("vagrant.provisioners.salt.must_accept_keys") end return {"salt provisioner" => errors} end end end end vagrant-1.4.3/plugins/provisioners/salt/errors.rb000066400000000000000000000003171226132634600222160ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Salt module Errors class SaltError < Vagrant::Errors::VagrantError error_namespace("vagrant.provisioners.salt") end end end endvagrant-1.4.3/plugins/provisioners/salt/plugin.rb000066400000000000000000000007101226132634600221750ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Salt class Plugin < Vagrant.plugin("2") name "salt" description <<-DESC Provisions virtual machines using SaltStack DESC config(:salt, :provisioner) do require File.expand_path("../config", __FILE__) Config end provisioner(:salt) do require File.expand_path("../provisioner", __FILE__) Provisioner end end end end vagrant-1.4.3/plugins/provisioners/salt/provisioner.rb000066400000000000000000000212411226132634600232600ustar00rootroot00000000000000require 'json' module VagrantPlugins module Salt class Provisioner < Vagrant.plugin("2", :provisioner) def provision upload_configs upload_keys run_bootstrap_script call_overstate call_highstate end # Return a list of accepted keys def keys(group='minions') out = @machine.communicate.sudo("salt-key --out json") do |type, output| begin if type == :stdout out = JSON::load(output) break out[group] end end end return out end ## Utilities def expanded_path(rel_path) Pathname.new(rel_path).expand_path(@machine.env.root_path) end def binaries_found ## Determine States, ie: install vs configure desired_binaries = [] if !@config.no_minion desired_binaries.push('salt-minion') desired_binaries.push('salt-call') end if @config.install_master desired_binaries.push('salt-master') end if @config.install_syndic desired_binaries.push('salt-syndic') end found = true for binary in desired_binaries @machine.env.ui.info "Checking if %s is installed" % binary if !@machine.communicate.test("which %s" % binary) @machine.env.ui.info "%s was not found." % binary found = false else @machine.env.ui.info "%s found" % binary end end return found end def need_configure @config.minion_config or @config.minion_key or @config.master_config or @config.master_key end def need_install if @config.always_install return true else return !binaries_found() end end def temp_config_dir return @config.temp_config_dir || "/tmp" end # Generates option string for bootstrap script def bootstrap_options(install, configure, config_dir) options = "" if configure options = "%s -c %s" % [options, config_dir] end if @config.seed_master and @config.install_master seed_dir = "/tmp/minion-seed-keys" @machine.communicate.sudo("mkdir -p -m777 #{seed_dir}") @config.seed_master.each do |name, keyfile| sourcepath = expanded_path(keyfile).to_s dest = "#{seed_dir}/#{name}" @machine.communicate.upload(sourcepath, dest) end options = "#{options} -k #{seed_dir}" end if configure and !install options = "%s -C" % options else if @config.install_master options = "%s -M" % options end if @config.install_syndic options = "%s -S" % options end if @config.no_minion options = "%s -N" % options end if @config.install_type options = "%s %s" % [options, @config.install_type] end if @config.install_args options = "%s %s" % [options, @config.install_args] end end ## Any extra options passed to bootstrap if @config.bootstrap_options options = "%s %s" % [options, @config.bootstrap_options] end if @config.verbose @machine.env.ui.info "Using Bootstrap Options: %s" % options end return options end ## Actions # Get pillar string to pass with the salt command def get_pillar " pillar='#{@config.pillar_data.to_json}'" if !@config.pillar_data.empty? end # Copy master and minion configs to VM def upload_configs if @config.minion_config @machine.env.ui.info "Copying salt minion config to vm." @machine.communicate.upload(expanded_path(@config.minion_config).to_s, temp_config_dir + "/minion") end if @config.master_config @machine.env.ui.info "Copying salt master config to vm." @machine.communicate.upload(expanded_path(@config.master_config).to_s, temp_config_dir + "/master") end end # Copy master and minion keys to VM def upload_keys if @config.minion_key and @config.minion_pub @machine.env.ui.info "Uploading minion keys." @machine.communicate.upload(expanded_path(@config.minion_key).to_s, temp_config_dir + "/minion.pem") @machine.communicate.upload(expanded_path(@config.minion_pub).to_s, temp_config_dir + "/minion.pub") end if @config.master_key and @config.master_pub @machine.env.ui.info "Uploading master keys." @machine.communicate.upload(expanded_path(@config.master_key).to_s, temp_config_dir + "/master.pem") @machine.communicate.upload(expanded_path(@config.master_pub).to_s, temp_config_dir + "/master.pub") end end # Get bootstrap file location, bundled or custom def get_bootstrap if @config.bootstrap_script bootstrap_abs_path = expanded_path(@config.bootstrap_script) else bootstrap_abs_path = Pathname.new("../bootstrap-salt.sh").expand_path(__FILE__) end return bootstrap_abs_path end # Determine if we are configure and/or installing, then do either def run_bootstrap_script install = need_install() configure = need_configure() config_dir = temp_config_dir() options = bootstrap_options(install, configure, config_dir) if configure or install if configure and !install @machine.env.ui.info "Salt binaries found. Configuring only." else @machine.env.ui.info "Bootstrapping Salt... (this may take a while)" end bootstrap_path = get_bootstrap bootstrap_destination = File.join(config_dir, "bootstrap_salt.sh") @machine.communicate.sudo("rm -f %s" % bootstrap_destination) @machine.communicate.upload(bootstrap_path.to_s, bootstrap_destination) @machine.communicate.sudo("chmod +x %s" % bootstrap_destination) bootstrap = @machine.communicate.sudo("%s %s" % [bootstrap_destination, options]) do |type, data| if data[0] == "\n" # Remove any leading newline but not whitespace. If we wanted to # remove newlines and whitespace we would have used data.lstrip data = data[1..-1] end if @config.verbose @machine.env.ui.info(data.rstrip) end end if !bootstrap raise Salt::Errors::SaltError, :bootstrap_failed end if configure and !install @machine.env.ui.info "Salt successfully configured!" elsif configure and install @machine.env.ui.info "Salt successfully configured and installed!" elsif !configure and install @machine.env.ui.info "Salt successfully installed!" end else @machine.env.ui.info "Salt did not need installing or configuring." end end def call_overstate if @config.run_overstate if @config.install_master @machine.env.ui.info "Calling state.overstate... (this may take a while)" @machine.communicate.sudo("salt '*' saltutil.sync_all") @machine.communicate.sudo("salt-run state.over") do |type, data| if @config.verbose @machine.env.ui.info(data) end end else @machine.env.ui.info "run_overstate does not make sense on a minion. Not running state.overstate." end else @machine.env.ui.info "run_overstate set to false. Not running state.overstate." end end def call_highstate if @config.run_highstate @machine.env.ui.info "Calling state.highstate... (this may take a while)" if @config.install_master @machine.communicate.sudo("salt '*' saltutil.sync_all") @machine.communicate.sudo("salt '*' state.highstate --verbose#{get_pillar}") do |type, data| if @config.verbose @machine.env.ui.info(data) end end else @machine.communicate.sudo("salt-call saltutil.sync_all") @machine.communicate.sudo("salt-call state.highstate -l debug#{get_pillar}") do |type, data| if @config.verbose @machine.env.ui.info(data) end end end else @machine.env.ui.info "run_highstate set to false. Not running state.highstate." end end end end end vagrant-1.4.3/plugins/provisioners/shell/000077500000000000000000000000001226132634600205205ustar00rootroot00000000000000vagrant-1.4.3/plugins/provisioners/shell/config.rb000066400000000000000000000045171226132634600223210ustar00rootroot00000000000000require 'uri' module VagrantPlugins module Shell class Config < Vagrant.plugin("2", :config) attr_accessor :inline attr_accessor :path attr_accessor :upload_path attr_accessor :args attr_accessor :privileged attr_accessor :binary attr_accessor :keep_color def initialize @args = UNSET_VALUE @inline = UNSET_VALUE @path = UNSET_VALUE @upload_path = UNSET_VALUE @privileged = UNSET_VALUE @binary = UNSET_VALUE @keep_color = UNSET_VALUE end def finalize! @args = nil if @args == UNSET_VALUE @inline = nil if @inline == UNSET_VALUE @path = nil if @path == UNSET_VALUE @upload_path = "/tmp/vagrant-shell" if @upload_path == UNSET_VALUE @privileged = true if @privileged == UNSET_VALUE @binary = false if @binary == UNSET_VALUE @keep_color = false if @keep_color == UNSET_VALUE end def validate(machine) errors = _detected_errors # Validate that the parameters are properly set if path && inline errors << I18n.t("vagrant.provisioners.shell.path_and_inline_set") elsif !path && !inline errors << I18n.t("vagrant.provisioners.shell.no_path_or_inline") end # If it is not an URL, we validate the existence of a script to upload if path && ! remote? expanded_path = Pathname.new(path).expand_path(machine.env.root_path) if !expanded_path.file? errors << I18n.t("vagrant.provisioners.shell.path_invalid", :path => expanded_path) end end # There needs to be a path to upload the script to if !upload_path errors << I18n.t("vagrant.provisioners.shell.upload_path_not_set") end if !args_valid? errors << I18n.t("vagrant.provisioners.shell.args_bad_type") end { "shell provisioner" => errors } end # Args are optional, but if they're provided we only support them as a # string or as an array. def args_valid? return true if !args args.is_a?(String) || args.is_a?(Array) end def remote? path =~ URI.regexp(["ftp", "http", "https"]) end end end end vagrant-1.4.3/plugins/provisioners/shell/plugin.rb000066400000000000000000000007621226132634600223500ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module Shell class Plugin < Vagrant.plugin("2") name "shell" description <<-DESC Provides support for provisioning your virtual machines with shell scripts. DESC config(:shell, :provisioner) do require File.expand_path("../config", __FILE__) Config end provisioner(:shell) do require File.expand_path("../provisioner", __FILE__) Provisioner end end end end vagrant-1.4.3/plugins/provisioners/shell/provisioner.rb000066400000000000000000000070621226132634600234310ustar00rootroot00000000000000require "pathname" require "tempfile" require "vagrant/util/downloader" module VagrantPlugins module Shell class Provisioner < Vagrant.plugin("2", :provisioner) def provision args = "" if config.args.is_a?(String) args = " #{config.args}" elsif config.args.is_a?(Array) args = config.args.map { |a| quote_and_escape(a) } args = " #{args.join(" ")}" end command = "chmod +x #{config.upload_path} && #{config.upload_path}#{args}" with_script_file do |path| # Upload the script to the machine @machine.communicate.tap do |comm| # Reset upload path permissions for the current ssh user user = @machine.ssh_info[:username] comm.sudo("chown -R #{user} #{config.upload_path}", :error_check => false) comm.upload(path.to_s, config.upload_path) if config.path @machine.ui.info(I18n.t("vagrant.provisioners.shell.running", script: path.to_s)) else @machine.ui.info(I18n.t("vagrant.provisioners.shell.running", script: "inline script")) end # Execute it with sudo comm.execute(command, sudo: config.privileged) do |type, data| if [:stderr, :stdout].include?(type) # Output the data with the proper color based on the stream. color = type == :stdout ? :green : :red options = { new_line: false, prefix: false, } options[:color] = color if !config.keep_color @machine.env.ui.info(data, options) end end end end end protected # Quote and escape strings for shell execution, thanks to Capistrano. def quote_and_escape(text, quote = '"') "#{quote}#{text.gsub(/#{quote}/) { |m| "#{m}\\#{m}#{m}" }}#{quote}" end # This method yields the path to a script to upload and execute # on the remote server. This method will properly clean up the # script file if needed. def with_script_file script = nil if config.remote? download_path = @machine.env.tmp_path.join("#{@machine.id}-remote-script") download_path.delete if download_path.file? Vagrant::Util::Downloader.new(config.path, download_path).download! script = download_path.read download_path.delete elsif config.path # Just yield the path to that file... root_path = @machine.env.root_path script = Pathname.new(config.path).expand_path(root_path).read else # The script is just the inline code... script = config.inline end # Replace Windows line endings with Unix ones unless binary file script.gsub!(/\r\n?$/, "\n") if !config.binary # Otherwise we have an inline script, we need to Tempfile it, # and handle it specially... file = Tempfile.new('vagrant-shell') # Unless you set binmode, on a Windows host the shell script will # have CRLF line endings instead of LF line endings, causing havoc # when the guest executes it. This fixes [GH-1181]. file.binmode begin file.write(script) file.fsync file.close yield file.path ensure file.close file.unlink end end end end end vagrant-1.4.3/plugins/synced_folders/000077500000000000000000000000001226132634600176525ustar00rootroot00000000000000vagrant-1.4.3/plugins/synced_folders/nfs/000077500000000000000000000000001226132634600204405ustar00rootroot00000000000000vagrant-1.4.3/plugins/synced_folders/nfs/config.rb000066400000000000000000000007261226132634600222370ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module SyncedFolderNFS class Config < Vagrant.plugin("2", :config) attr_accessor :map_uid attr_accessor :map_gid def initialize super @map_uid = UNSET_VALUE @map_gid = UNSET_VALUE end def finalize! @map_uid = nil if @map_uid == UNSET_VALUE @map_gid = nil if @map_gid == UNSET_VALUE end def to_s "NFS" end end end end vagrant-1.4.3/plugins/synced_folders/nfs/plugin.rb000066400000000000000000000010131226132634600222560ustar00rootroot00000000000000require "vagrant" module VagrantPlugins module SyncedFolderNFS class Plugin < Vagrant.plugin("2") name "NFS synced folders" description <<-EOF The NFS synced folders plugin enables you to use NFS as a synced folder implementation. EOF config("nfs") do require File.expand_path("../config", __FILE__) Config end synced_folder("nfs", 5) do require File.expand_path("../synced_folder", __FILE__) SyncedFolder end end end end vagrant-1.4.3/plugins/synced_folders/nfs/synced_folder.rb000066400000000000000000000065731226132634600236200ustar00rootroot00000000000000require 'fileutils' require 'thread' require 'zlib' require "log4r" require "vagrant/util/platform" module VagrantPlugins module SyncedFolderNFS # This synced folder requires that two keys be set on the environment # within the middleware sequence: # # - `:nfs_host_ip` - The IP of where to mount the NFS folder from. # - `:nfs_machine_ip` - The IP of the machine where the NFS folder # will be mounted. # class SyncedFolder < Vagrant.plugin("2", :synced_folder) @@lock = Mutex.new def initialize(*args) super @logger = Log4r::Logger.new("vagrant::synced_folders::nfs") end def usable?(machine) # NFS is always available true end def prepare(machine, folders, opts) # Nothing is necessary to do before VM boot. end def enable(machine, folders, nfsopts) raise Vagrant::Errors::NFSNoHostIP if !nfsopts[:nfs_host_ip] raise Vagrant::Errors::NFSNoGuestIP if !nfsopts[:nfs_machine_ip] machine_ip = nfsopts[:nfs_machine_ip] machine_ip = [machine_ip] if !machine_ip.is_a?(Array) # Prepare the folder, this means setting up various options # and such on the folder itself. folders.each { |id, opts| prepare_folder(machine, opts) } # Export the folders. We do this with a class-wide lock because # NFS exporting often requires sudo privilege and we don't want # overlapping input requests. [GH-2680] @@lock.synchronize do machine.ui.info I18n.t("vagrant.actions.vm.nfs.exporting") machine.env.host.nfs_export(machine.id, machine_ip, folders) end # Mount machine.ui.info I18n.t("vagrant.actions.vm.nfs.mounting") # Only mount folders that have a guest path specified. mount_folders = {} folders.each do |id, opts| mount_folders[id] = opts.dup if opts[:guestpath] end # Mount them! machine.guest.capability( :mount_nfs_folder, nfsopts[:nfs_host_ip], mount_folders) end def cleanup(machine, opts) ids = opts[:nfs_valid_ids] raise Vagrant::Errors::NFSNoValidIds if !ids # Prune any of the unused machines @logger.info("NFS pruning. Valid IDs: #{ids.inspect}") machine.env.host.nfs_prune(ids) end protected def prepare_folder(machine, opts) opts[:map_uid] = prepare_permission(machine, :uid, opts) opts[:map_gid] = prepare_permission(machine, :gid, opts) opts[:nfs_udp] = true if !opts.has_key?(:nfs_udp) opts[:nfs_version] ||= 3 # We use a CRC32 to generate a 32-bit checksum so that the # fsid is compatible with both old and new kernels. opts[:uuid] = Zlib.crc32(opts[:hostpath]).to_s end # Prepares the UID/GID settings for a single folder. def prepare_permission(machine, perm, opts) key = "map_#{perm}".to_sym return nil if opts.has_key?(key) && opts[key].nil? # The options on the hash get priority, then the default # values value = opts.has_key?(key) ? opts[key] : machine.config.nfs.send(key) return value if value != :auto # Get UID/GID from folder if we've made it this far # (value == :auto) stat = File.stat(opts[:hostpath]) return stat.send(perm) end end end end vagrant-1.4.3/scripts/000077500000000000000000000000001226132634600146555ustar00rootroot00000000000000vagrant-1.4.3/scripts/bintray_upload.sh000077500000000000000000000016471226132634600202400ustar00rootroot00000000000000#!/bin/bash set -e # Get the parent directory of where this script is. SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" # Change into that dir because we expect that cd $DIR # Get the version from the command line VERSION=$1 if [ -z $VERSION ]; then echo "Please specify a version." exit 1 fi # Make sure we have a bintray API key if [ -z $BINTRAY_API_KEY ]; then echo "Please set your bintray API key in the BINTRAY_API_KEY env var." exit 1 fi # Calculate the checksums pushd ./dist shasum -a256 * > ./${VERSION}_SHA256SUMS popd # Upload for ARCHIVE in ./dist/*; do ARCHIVE_NAME=$(basename ${ARCHIVE}) echo Uploading: $ARCHIVE_NAME curl \ -T ${ARCHIVE} \ -umitchellh:${BINTRAY_API_KEY} \ "https://api.bintray.com/content/mitchellh/vagrant/vagrant/${VERSION}/${ARCHIVE_NAME}" done vagrant-1.4.3/scripts/website_push_docs.sh000077500000000000000000000005501226132634600207250ustar00rootroot00000000000000#!/bin/bash # Get the parent directory of where this script is. SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" # Change into that directory cd $DIR # Push the subtree (force) git push heroku-docs `git subtree split --prefix website/docs master`:master --force vagrant-1.4.3/scripts/website_push_www.sh000077500000000000000000000005461226132634600206260ustar00rootroot00000000000000#!/bin/bash # Get the parent directory of where this script is. SOURCE="${BASH_SOURCE[0]}" while [ -h "$SOURCE" ] ; do SOURCE="$(readlink "$SOURCE")"; done DIR="$( cd -P "$( dirname "$SOURCE" )/.." && pwd )" # Change into that directory cd $DIR # Push the subtree (force) git push heroku-www `git subtree split --prefix website/www master`:master --force vagrant-1.4.3/tasks/000077500000000000000000000000001226132634600143135ustar00rootroot00000000000000vagrant-1.4.3/tasks/acceptance.rake000066400000000000000000000007241226132634600172500ustar00rootroot00000000000000namespace :acceptance do desc "shows components that can be tested" task :components do exec("vagrant-spec components --config=vagrant-spec.config.rb") end desc "runs acceptance tests" task :run do args = [ "--config=vagrant-spec.config.rb", ] if ENV["COMPONENTS"] args << "--components=\"#{ENV["COMPONENTS"]}\"" end command = "vagrant-spec test #{args.join(" ")}" puts command puts exec(command) end end vagrant-1.4.3/tasks/bundler.rake000066400000000000000000000001511226132634600166070ustar00rootroot00000000000000# This installs the tasks that help with gem creation and # publishing. Bundler::GemHelper.install_tasks vagrant-1.4.3/tasks/test.rake000066400000000000000000000004641226132634600161420ustar00rootroot00000000000000require 'rake/testtask' require 'rspec/core/rake_task' namespace :test do RSpec::Core::RakeTask.new(:unit) do |t| t.pattern = "test/unit/**/*_test.rb" end Rake::TestTask.new do |t| t.name = "unit_old" t.libs << "test/unit_legacy" t.pattern = "test/unit_legacy/**/*_test.rb" end end vagrant-1.4.3/templates/000077500000000000000000000000001226132634600151645ustar00rootroot00000000000000vagrant-1.4.3/templates/commands/000077500000000000000000000000001226132634600167655ustar00rootroot00000000000000vagrant-1.4.3/templates/commands/init/000077500000000000000000000000001226132634600177305ustar00rootroot00000000000000vagrant-1.4.3/templates/commands/init/Vagrantfile.erb000066400000000000000000000111061226132634600226630ustar00rootroot00000000000000# -*- mode: ruby -*- # vi: set ft=ruby : # Vagrantfile API/syntax version. Don't touch unless you know what you're doing! VAGRANTFILE_API_VERSION = "2" Vagrant.configure(VAGRANTFILE_API_VERSION) do |config| # All Vagrant configuration is done here. The most common configuration # options are documented and commented below. For a complete reference, # please see the online documentation at vagrantup.com. # Every Vagrant virtual environment requires a box to build off of. config.vm.box = "<%= box_name %>" # The url from where the 'config.vm.box' box will be fetched if it # doesn't already exist on the user's system. <% if box_url.nil? %># <% end %>config.vm.box_url = "<%= box_url || "http://domain.com/path/to/above.box" %>" # Create a forwarded port mapping which allows access to a specific port # within the machine from a port on the host machine. In the example below, # accessing "localhost:8080" will access port 80 on the guest machine. # config.vm.network :forwarded_port, guest: 80, host: 8080 # Create a private network, which allows host-only access to the machine # using a specific IP. # config.vm.network :private_network, ip: "192.168.33.10" # Create a public network, which generally matched to bridged network. # Bridged networks make the machine appear as another physical device on # your network. # config.vm.network :public_network # If true, then any SSH connections made will enable agent forwarding. # Default value: false # config.ssh.forward_agent = true # Share an additional folder to the guest VM. The first argument is # the path on the host to the actual folder. The second argument is # the path on the guest to mount the folder. And the optional third # argument is a set of non-required options. # config.vm.synced_folder "../data", "/vagrant_data" # Provider-specific configuration so you can fine-tune various # backing providers for Vagrant. These expose provider-specific options. # Example for VirtualBox: # # config.vm.provider :virtualbox do |vb| # # Don't boot with headless mode # vb.gui = true # # # Use VBoxManage to customize the VM. For example to change memory: # vb.customize ["modifyvm", :id, "--memory", "1024"] # end # # View the documentation for the provider you're using for more # information on available options. # Enable provisioning with Puppet stand alone. Puppet manifests # are contained in a directory path relative to this Vagrantfile. # You will need to create the manifests directory and a manifest in # the file <%= box_name %>.pp in the manifests_path directory. # # An example Puppet manifest to provision the message of the day: # # # group { "puppet": # # ensure => "present", # # } # # # # File { owner => 0, group => 0, mode => 0644 } # # # # file { '/etc/motd': # # content => "Welcome to your Vagrant-built virtual machine! # # Managed by Puppet.\n" # # } # # config.vm.provision :puppet do |puppet| # puppet.manifests_path = "manifests" # puppet.manifest_file = "site.pp" # end # Enable provisioning with chef solo, specifying a cookbooks path, roles # path, and data_bags path (all relative to this Vagrantfile), and adding # some recipes and/or roles. # # config.vm.provision :chef_solo do |chef| # chef.cookbooks_path = "../my-recipes/cookbooks" # chef.roles_path = "../my-recipes/roles" # chef.data_bags_path = "../my-recipes/data_bags" # chef.add_recipe "mysql" # chef.add_role "web" # # # You may also specify custom JSON attributes: # chef.json = { :mysql_password => "foo" } # end # Enable provisioning with chef server, specifying the chef server URL, # and the path to the validation key (relative to this Vagrantfile). # # The Opscode Platform uses HTTPS. Substitute your organization for # ORGNAME in the URL and validation key. # # If you have your own Chef Server, use the appropriate URL, which may be # HTTP instead of HTTPS depending on your configuration. Also change the # validation key to validation.pem. # # config.vm.provision :chef_client do |chef| # chef.chef_server_url = "https://api.opscode.com/organizations/ORGNAME" # chef.validation_key_path = "ORGNAME-validator.pem" # end # # If you're using the Opscode platform, your validator client is # ORGNAME-validator, replacing ORGNAME with your organization name. # # If you have your own Chef Server, the default validation client name is # chef-validator, unless you changed the configuration. # # chef.validation_client_name = "ORGNAME-validator" end vagrant-1.4.3/templates/commands/ssh_config/000077500000000000000000000000001226132634600211075ustar00rootroot00000000000000vagrant-1.4.3/templates/commands/ssh_config/config.erb000066400000000000000000000007371226132634600230550ustar00rootroot00000000000000Host <%= host_key %> HostName <%= ssh_host %> User <%= ssh_user %> Port <%= ssh_port %> UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no <% private_key_path.each do |path| %> <% if path.include?(" ") -%> IdentityFile "<%= path %>" <% else -%> IdentityFile <%= path %> <% end -%> <% end -%> IdentitiesOnly yes LogLevel FATAL <% if forward_agent -%> ForwardAgent yes <% end -%> <% if forward_x11 -%> ForwardX11 yes <% end -%> vagrant-1.4.3/templates/config/000077500000000000000000000000001226132634600164315ustar00rootroot00000000000000vagrant-1.4.3/templates/config/messages.erb000066400000000000000000000003241226132634600207310ustar00rootroot00000000000000<% if !warnings.empty? -%> Warnings: <% warnings.each do |warning| -%> * <%= warning %> <% end -%> <% end -%> <% if !errors.empty? -%> Errors: <% errors.each do |error| -%> * <%= error %> <% end -%> <% end -%> vagrant-1.4.3/templates/config/validation_failed.erb000066400000000000000000000001701226132634600225570ustar00rootroot00000000000000<% errors.each do |section, list| -%> <%= section %>: <% list.each do |error| -%> * <%= error %> <% end -%> <% end -%> vagrant-1.4.3/templates/guests/000077500000000000000000000000001226132634600164765ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/arch/000077500000000000000000000000001226132634600174135ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/arch/network_dhcp.erb000066400000000000000000000001631226132634600225740ustar00rootroot00000000000000Description='A basic dhcp ethernet connection' Interface=eth<%= options[:interface] %> Connection=ethernet IP=dhcp vagrant-1.4.3/templates/guests/arch/network_static.erb000066400000000000000000000002311226132634600231410ustar00rootroot00000000000000Connection=ethernet Description='A basic static ethernet connection' Interface=eth<%= options[:interface] %> IP=static Address=('<%= options[:ip]%>/24') vagrant-1.4.3/templates/guests/coreos/000077500000000000000000000000001226132634600177705ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/coreos/etcd.service.erb000066400000000000000000000004331226132634600230400ustar00rootroot00000000000000[Unit] Description=Clustered etcd #After=docker.service [Service] Restart=always ExecStart=/usr/bin/etcd -c 4001 -s 7001 -h <%= options[:my_ip] %> <% if options[:connection_string] %>-C <%= options[:connection_string] %><% end %> -d /home/core/etcd [Install] WantedBy=local.target vagrant-1.4.3/templates/guests/debian/000077500000000000000000000000001226132634600177205ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/debian/network_dhcp.erb000066400000000000000000000007011226132634600230770ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. auto eth<%= options[:interface] %> iface eth<%= options[:interface] %> inet dhcp <% if !options[:use_dhcp_assigned_default_route] %> post-up route del default dev $IFACE <% else %> # We need to disable eth0, see GH-2648 post-up route del default dev eth0 post-up dhclient $IFACE pre-down route add default dev eth0 <% end %> #VAGRANT-END vagrant-1.4.3/templates/guests/debian/network_static.erb000066400000000000000000000004041226132634600234500ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. auto eth<%= options[:interface] %> iface eth<%= options[:interface] %> inet static address <%= options[:ip] %> netmask <%= options[:netmask] %> #VAGRANT-END vagrant-1.4.3/templates/guests/fedora/000077500000000000000000000000001226132634600177365ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/fedora/network_dhcp.erb000066400000000000000000000002471226132634600231220ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO=dhcp ONBOOT=yes DEVICE=p7p<%= options[:interface] %> #VAGRANT-END vagrant-1.4.3/templates/guests/fedora/network_static.erb000066400000000000000000000006041226132634600234700ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. NM_CONTROLLED=no BOOTPROTO=none ONBOOT=yes IPADDR=<%= options[:ip] %> NETMASK=<%= options[:netmask] %> DEVICE=p7p<%= options[:interface] %> <%= options[:gateway] ? "GATEWAY=#{options[:gateway]}" : '' %> <%= options[:mac_address] ? "HWADDR=#{options[:mac_address]}" : '' %> PEERDNS=no #VAGRANT-END vagrant-1.4.3/templates/guests/freebsd/000077500000000000000000000000001226132634600201105ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/freebsd/network_dhcp.erb000066400000000000000000000001111226132634600232620ustar00rootroot00000000000000#VAGRANT-BEGIN ifconfig_em<%= options[:interface] %>="DHCP" #VAGRANT-END vagrant-1.4.3/templates/guests/freebsd/network_static.erb000066400000000000000000000001761226132634600236460ustar00rootroot00000000000000#VAGRANT-BEGIN ifconfig_em<%= options[:interface] %>="inet <%= options[:ip] %> netmask <%= options[:netmask] %>" #VAGRANT-END vagrant-1.4.3/templates/guests/gentoo/000077500000000000000000000000001226132634600177715ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/gentoo/network_dhcp.erb000066400000000000000000000002241226132634600231500ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. config_eth<%= options[:interface] %>="dhcp" #VAGRANT-END vagrant-1.4.3/templates/guests/gentoo/network_static.erb000066400000000000000000000003061226132634600235220ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. config_eth<%= options[:interface] %>=("<%= options[:ip] %> netmask <%= options[:netmask] %>") #VAGRANT-END vagrant-1.4.3/templates/guests/openbsd/000077500000000000000000000000001226132634600201305ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/openbsd/network_dhcp.erb000066400000000000000000000000051226132634600233040ustar00rootroot00000000000000dhcp vagrant-1.4.3/templates/guests/openbsd/network_static.erb000066400000000000000000000000671226132634600236650ustar00rootroot00000000000000inet <%= options[:ip] %> <%= options[:netmask] %> NONE vagrant-1.4.3/templates/guests/redhat/000077500000000000000000000000001226132634600177455ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/redhat/network_dhcp.erb000066400000000000000000000002471226132634600231310ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO=dhcp ONBOOT=yes DEVICE=eth<%= options[:interface] %> #VAGRANT-END vagrant-1.4.3/templates/guests/redhat/network_static.erb000066400000000000000000000003771226132634600235060ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. NM_CONTROLLED=no BOOTPROTO=none ONBOOT=yes IPADDR=<%= options[:ip] %> NETMASK=<%= options[:netmask] %> DEVICE=eth<%= options[:interface] %> PEERDNS=no #VAGRANT-END vagrant-1.4.3/templates/guests/suse/000077500000000000000000000000001226132634600174555ustar00rootroot00000000000000vagrant-1.4.3/templates/guests/suse/network_dhcp.erb000066400000000000000000000002471226132634600226410ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO=dhcp ONBOOT=yes DEVICE=eth<%= options[:interface] %> #VAGRANT-END vagrant-1.4.3/templates/guests/suse/network_static.erb000066400000000000000000000004171226132634600232110ustar00rootroot00000000000000#VAGRANT-BEGIN # The contents below are automatically generated by Vagrant. Do not modify. BOOTPROTO='static' IPADDR='<%= options[:ip] %>' NETMASK='<%= options[:netmask] %>' DEVICE='eth<%= options[:interface] %>' PEERDNS=no STARTMODE='auto' USERCONTROL='no' #VAGRANT-END vagrant-1.4.3/templates/locales/000077500000000000000000000000001226132634600166065ustar00rootroot00000000000000vagrant-1.4.3/templates/locales/en.yml000066400000000000000000002063641226132634600177460ustar00rootroot00000000000000en: vagrant: boot_completed: |- Machine booted and ready! boot_waiting: |- Waiting for machine to boot. This may take a few minutes... cfengine_bootstrapping: |- Bootstrapping CFEngine with policy server: %{policy_server}... cfengine_bootstrapping_policy_hub: |- Performing additional bootstrap for policy hubs... cfengine_cant_autodetect_ip: |- Vagrant was unable to automatically detect the IP address of the running machine to use as the policy server address. Please specify the policy server address manually, or verify that the networks are configured properly internally. cfengine_cant_detect: |- Vagrant doesn't support detecting whether CFEngine is installed for the guest OS running in the machine. Vagrant will assume it is installed and attempt to continue. cfengine_detected_ip: |- Detected policy server IP address: %{address}... cfengine_installing: |- Installing CFEngine onto machine... cfengine_installing_files_path: |- Copying the 'files_path' files... cfengine_no_bootstrap: |- CFEngine doesn't require bootstrap. Not bootstrapping. cfengine_single_run: |- CFEngine running in "single run" mode. Will execute one file. cfengine_single_run_execute: |- Executing run file for CFEngine... chef_client_cleanup_failed: |- Cleaning up the '%{deletable}' for Chef failed. The stdout and stderr are shown below. Vagrant will continue destroying the machine, so please clean up these resources manually. stdout: %{stdout} stderr: %{stderr} chef_config_knife_not_found: |- The `knife` application was not found! This is required by Vagrant to automatically delete Chef nodes and clients. chef_run_list_empty: |- Warning: Chef run list is empty. This may not be what you want. docker_installing: |- Installing Docker (%{version}) onto machine... docker_pulling_images: Pulling Docker images... docker_pulling_single: |- -- Image: %{name} docker_running: |- -- Container: %{name} docker_starting_containers: Starting Docker containers... docker_auto_start_not_available: |- Unable to configure automatic restart of Docker containers on the guest machine docker_install_with_version_not_supported: |- Vagrant is not capable of installing an specific version of Docker onto the guest machine and the latest version will be installed. plugin_needs_reinstall: |- The following plugins were installed with a version of Vagrant that had different versions of underlying components. Because these component versions were changed (which rarely happens), the plugins must be uninstalled and reinstalled. To ensure that all the dependencies are properly updated as well it is _highly recommended_ to do a `vagrant plugin uninstall` prior to reinstalling. This message will not go away until all the plugins below are either uninstalled or uninstalled then reinstalled. The plugins below will not be loaded until they're uninstalled and reinstalled: %{names} provisioner_cleanup: |- Running cleanup tasks for '%{name}' provisioner... cfengine_config: classes_array: |- The 'classes' configuration must be an array. files_path_not_directory: |- The 'files_path' must point to a valid directory. invalid_mode: |- The mode must be 'bootstrap' or 'single_run' policy_server_address: |- The policy server address must be set for bootstrapping. run_file_not_found: |- The 'run_file' specified could not be found. virtualbox: config: id_in_pre_import: |- The ':id' parameter is not available in "pre-import" customizations. intnet_on_bad_type: |- VirtualBox internal networks can only be enabled on "private_networks" invalid_event: |- %{event} is not a valid event for customization. Valid events are: %{valid_events} general: batch_unexpected_error: |- An unexpected error ocurred when executing the action on the '%{machine}' machine. Please report this as a bug: %{message} batch_vagrant_error: |- An error occurred while executing the action on the '%{machine}' machine. Please handle this error then try again: %{message} config_upgrade_messages: |- There were warnings and/or errors while loading your Vagrantfile for the machine '%{name}'. Your Vagrantfile was written for an earlier version of Vagrant, and while Vagrant does the best it can to remain backwards compatible, there are some cases where things have changed significantly enough to warrant a message. These messages are shown below. %{output} moving_home_dir: "Moving old Vagrant home directory to new location: %{directory}" home_dir_migration_failed: |- Both an old and new Vagrant home directory exist. Only the new one will be used. Please merge the old directory into the new directory if you'd like to use the old data as well. Old: %{old} New: %{new} in_bundler: |- You appear to be running Vagrant in a Bundler environment. Because Vagrant should be run within installers (outside of Bundler), Vagrant will assume that you're developing plugins and will change its behavior in certain ways to better assist plugin development. not_in_installer: |- You appear to be running Vagrant outside of the official installers. Note that the installers are what ensure that Vagrant has all required dependencies, and Vagrant assumes that these dependencies exist. By running outside of the installer environment, Vagrant may not function properly. To remove this warning, install Vagrant using one of the official packages from vagrantup.com. upgraded_v1_dotfile: |- A Vagrant 1.0.x state file was found for this environment. Vagrant has gone ahead and auto-upgraded this to the latest format. Everything should continue working as normal. Beware, however, that older versions of Vagrant may no longer be used with this environment. However, in case anything went wrong, the old dotfile was backed up to the location below. If everything is okay, it is safe to remove this backup. Backup: %{backup_path} #------------------------------------------------------------------------------- # Translations for exception classes #------------------------------------------------------------------------------- errors: active_machine_with_different_provider: |- An active machine was found with a different provider. Vagrant currently allows each machine to be brought up with only a single provider at a time. A future version will remove this limitation. Until then, please destroy the existing machine to up with a new provider. Machine name: %{name} Active provider: %{active_provider} Requested provider: %{requested_provider} ansible_failed: |- Ansible failed to complete successfully. Any error output should be visible above. Please fix these errors and try again. ansible_playbook_app_not_found: |- The "ansible-playbook" program could not be found! Please verify that "ansible-playbook" is available on the PATH of your host system, and try again. If you haven't installed Ansible yet, please install Ansible on your system. Vagrant can't do this for you in a safe, automated way. Please see ansible.cc for more info. base_vm_not_found: The base VM with the name '%{name}' was not found. batch_multi_error: |- An error occurred while executing multiple actions in parallel. Any errors that occurred are shown below. %{message} boot_bad_state: |- The guest machine entered an invalid state while waiting for it to boot. Valid states are '%{valid}'. The machine is in the '%{invalid}' state. Please verify everything is configured properly and try again. If the provider you're using has a GUI that comes with it, it is often helpful to open that and watch the machine, since the GUI often has more helpful error messages than Vagrant can retrieve. For example, if you're using VirtualBox, run `vagrant up` while the VirtualBox GUI is open. boot_timeout: |- Timed out while waiting for the machine to boot. This means that Vagrant was unable to communicate with the guest machine within the configured ("config.vm.boot_timeout" value) time period. This can mean a number of things. If you're using a custom box, make sure that networking is properly working and you're able to connect to the machine. It is a common problem that networking isn't setup properly in these boxes. Verify that authentication configurations are also setup properly, as well. If the box appears to be booting properly, you may want to increase the timeout ("config.vm.boot_timeout") value. box_checksum_invalid_type: |- The specified checksum type is not supported by Vagrant: %{type}. Vagrant supports the following checksum types: md5, sha1, sha256 box_checksum_mismatch: |- The checksum of the dowloaded box did not match the expected value. Please verify that you have the proper URL setup and that you're downloading the proper file. Expected: %{expected} Received: %{actual} box_config_changing_box: |- While loading the Vagrantfile, the provider override specified a new box. This box, in turn, specified a different box. This isn't allowed, as it could lead to infinite recursion of Vagrant configuration loading. Please fix this. box_metadata_corrupted: |- The metadata associated with the box '%{name}' appears corrupted. This is most often caused by a disk issue or system crash. Please remove the box, re-add it, and try again. box_metadata_file_not_found: |- The "metadata.json" file for the box '%{name}' was not found. Boxes require this file in order for Vagrant to determine the provider it was made for. If you made the box, please add a "metadata.json" file to it. If someone else made the box, please notify the box creator that the box is corrupt. Documentation for box file format can be found at the URL below: http://docs.vagrantup.com/v2/boxes/format.html box_not_found: Box '%{name}' with '%{provider}' provider could not be found. box_provider_doesnt_match: |- The box you attempted to add doesn't match the provider you specified. Provider expected: %{expected} Provider of box: %{actual} box_upgrade_required: |- The box '%{name}' is still stored on disk in the Vagrant 1.0.x format. This box must be upgraded in order to work properly with this version of Vagrant. cfengine_bootstrap_failed: |- Failed to bootstrap CFEngine. Please see the output above to see what went wrong and address the issue. cfengine_install_failed: |- After installing CFEngine, Vagrant still can't detect a proper CFEngine installation. Please verify that CFEngine was properly installed, as the installation may have failed. cfengine_not_installed: |- CFEngine appears not to be installed on the guest machine. Vagrant can attempt to install CFEngine for you if you set the "install" setting to "true", but this setting was not set. Please install CFEngine on the guest machine or enable the "install" setting for Vagrant to attempt to install it for you. cli_invalid_options: |- An invalid option was specified. The help for this command is available below. %{help} cli_invalid_usage: |- This command was not invoked properly. The help for this command is available below. %{help} command_unavailable: |- The executable '%{file}' Vagrant is trying to run was not found in the PATH variable. This is an error. Please verify this software is installed and on the path. command_unavailable_windows: |- The executable '%{file}' Vagrant is trying to run was not found in the %PATH% variable. This is an error. Please verify this software is installed and on the path. config_invalid: |- There are errors in the configuration of this machine. Please fix the following errors and try again: %{errors} config_upgrade_errors: |- Because there were errors upgrading your Vagrantfiles, Vagrant can no longer continue. Please fix the errors above and try again. copy_private_key_failed: |- Vagrant failed to copy the default insecure private key into your home directory. This is usually caused by a permissions error. Please make sure the permissions of the source is readable and the destination is writable. Source: %{source} Destination: %{destination} destroy_requires_force: |- Destroy doesn't have a TTY to ask for confirmation. Please pass the `--force` flag to force a destroy, otherwise attach a TTY so that the destroy can be confirmed. dotfile_is_directory: |- The local file Vagrant uses to store data ".vagrant" already exists and is a directory! If you are in your home directory, then please run this command in another directory. If you aren't in a home directory, then please rename ".vagrant" to something else, or configure Vagrant to use another filename by modifying `config.vagrant.dotfile_name`. dotfile_upgrade_json_error: |- A Vagrant 1.0.x local state file was found. Vagrant is able to upgrade this to the latest format automatically, however various checks are put in place to verify data isn't incorrectly deleted. In this case, the old state file was not valid JSON. Vagrant 1.0.x would store state as valid JSON, meaning that this file was probably tampered with or manually edited. Vagrant's auto-upgrade process cannot continue in this case. In most cases, this can be resolve by simply removing the state file. Note however though that if Vagrant was previously managing virtual machines, they may be left in an "orphan" state. That is, if they are running or exist, they'll have to manually be removed. If you're unsure what to do, ask the Vagrant mailing list or contact support. State file path: %{state_file} downloader_error: |- An error occurred while downloading the remote file. The error message, if any, is reproduced below. Please fix this error and try again. %{message} downloader_interrupted: |- The download was interrupted by an external signal. It did not complete. environment_locked: |- An instance of Vagrant is already running. Only one instance of Vagrant may run at any given time to avoid problems with VirtualBox inconsistencies occurring. Please wait for the other instance of Vagrant to end and then try again. environment_non_existent_cwd: |- The working directory for Vagrant doesn't exist! This is the specified working directory: %{cwd} forward_port_adapter_not_found: |- The adapter to attach a forwarded port to was not found. Please verify that the given adapter is setup on the machine as a NAT interface. Host port: %{host} Guest port: %{guest} Adapter: %{adapter} gem_command_in_bundler: |- You cannot run the `vagrant plugin` command while in a bundler environment. This should generally never happen unless Vagrant is installed outside of the official installers or another gem is wrongly attempting to use Vagrant internals directly. Please properly install Vagrant to fix this. If this error persists, please contact support. guest_capability_invalid: |- The registered guest capability '%{cap}' for the detected guest OS '%{guest}' is invalid. The capability does not implement the proper method. This is a bug with Vagrant or the plugin that implements this capability. Please report a bug. guest_capability_not_found: |- Vagrant attempted to execute the capability '%{cap}' on the detect guest OS '%{guest}', but the guest doesn't support that capability. This capability is required for your configuration of Vagrant. Please either reconfigure Vagrant to avoid this capability or fix the issue by creating the capability. guest_explicit_not_detected: |- The guest implementation explicitly specified in your Vagrantfile ("%{value}") could not be found. Please verify that the plugin is installed which implements this guest and that the value you used for `config.vm.guest` is correct. guest_not_detected: |- The guest operating system of the machine could not be detected! Vagrant requires this knowledge to perform specific tasks such as mounting shared folders and configuring networks. Please add the ability to detect this guest operating system to Vagrant by creating a plugin or reporting a bug. home_dir_not_accessible: |- The home directory you specified is not accessible. The home directory that Vagrant uses must be both readable and writable. You specified: %{home_path} interrupted: |- Vagrant exited after cleanup due to external interrupt. local_data_dir_not_accessible: |- The directory Vagrant will use to store local environment-specific state is not accessible. The directory specified as the local data directory must be both readable and writable for the user that is running Vagrant. Local data directory: %{local_data_path} linux_mount_failed: |- Failed to mount folders in Linux guest. This is usually beacuse the "vboxsf" file system is not available. Please verify that the guest additions are properly installed in the guest and can work properly. The command attempted was: %{command} linux_nfs_mount_failed: |- Mounting NFS shared folders failed. This is most often caused by the NFS client software not being installed on the guest machine. Please verify that the NFS client software is properly installed, and consult any resources specific to the linux distro you're using for more information on how to do this. linux_shell_expand_failed: |- Vagrant failed to determine the shell expansion of the guest path for one of your shared folders. This is an extremely rare error case and most likely indicates an unusual configuration of the guest system. Please report a bug with your Vagrantfile. machine_guest_not_ready: |- Guest-specific operations were attempted on a machine that is not ready for guest communication. This should not happen and a bug should be reported. machine_not_found: |- The machine with the name '%{name}' was not found configured for this Vagrant environment. machine_state_invalid: |- An internal error has occurred! The provider of the machine you're trying to work with reported an invalid state. This is a bug with the provider you're using, and not with Vagrant itself or with any configuration you may have done. Please report this bug to the proper location. multi_vm_required: |- A multi-vm environment is required for name specification to this command. multi_vm_target_required: |- This command requires a specific VM name to target in a multi-VM environment. nfs_bad_exports: |- NFS is reporting that your exports file is invalid. Vagrant does this check before making any changes to the file. Please correct the issues below and execute "vagrant reload": %{output} nfs_cant_read_exports: |- Vagrant can't read your current NFS exports! The exports file should be readable by any user. This is usually caused by invalid permissions on your NFS exports file. Please fix them and try again. nfs_no_guest_ip: |- No guest IP was given to the Vagrant core NFS helper. This is an internal error that should be reported as a bug. nfs_no_host_ip: |- No host IP was given to the Vagrant core NFS helper. This is an internal error that should be reported as a bug. nfs_no_hostonly_network: |- NFS requires a host-only network to be created. Please add a host-only network to the machine (with either DHCP or a static IP) for NFS to work. nfs_no_valid_ids: |- No valid IDs were given to the NFS synced folder implementation to prune. This is an internal bug with Vagrant and an issue should be filed. no_default_synced_folder_impl: |- No synced folder implementation is available for your synced folders! Please consult the documentation to learn why this may be the case. You may force a synced folder implementation by specifying a "type:" option for the synced folders. Available synced folder implementations are listed below. %{types} no_env: |- A Vagrant environment is required to run this command. Run `vagrant init` to set one up in this directory, or change to a directory with a Vagrantfile and try again. plugin_gem_error: |- An error occurred within RubyGems, the underlying system used to manage Vagrant plugins. The output of the errors are shown below: %{output} plugin_install_bad_entry_point: |- Attempting to load the plugin '%{name}' failed, because the entry point doesn't exist. The entry point attempted was '%{entry_point}'. If this is not correct, please manually specify an `--entry-point` when installing the plugin. plugin_install_license_not_found: |- The license file to install could not be found. Please verify the path you gave is correct. The path to the license file given was: '%{path}' plugin_install_not_found: |- The plugin '%{name}' could not be found in local or remote repositories. Please check the name of the plugin and try again. plugin_load_error: |- The plugin "%{plugin}" could not be found. Please make sure that it is properly installed via `vagrant plugin`. Note that plugins made for Vagrant 1.0.x are not compatible with 1.1+ and this error will likely continue to show when you use `plugin install` with a 1.0.x plugin. plugin_load_failed: |- Failed to load the "%{plugin}" plugin. View logs for more details. plugin_load_failed_with_output: |- Failed to load the "%{plugin}" plugin. The output from loading the plugin is shown below. View the logs for complete details. stdout: %{stdout} stderr: %{stderr} plugin_not_found: |- The plugin '%{name}' could not be found. Please install this plugin prior to attempting to do anything with it. plugin_not_installed: |- The plugin '%{name}' is not installed. Please install it first. plugin_state_file_not_parsable: |- Failed to parse the state file "%{path}": %{message} Please remove the file and reinstall the plugins. If this error recurs, please report a bug. port_collision_resume: |- This VM cannot be resumed, because the forwarded ports would collide with a running program (it could be another virtual machine). Normally, Vagrant will attempt to fix this for you but VirtualBox only allows forwarded ports to change if the VM is powered off. Therefore, please reload your VM or stop the other program to continue. provider_not_found: |- The provider '%{provider}' could not be found, but was requested to back the machine '%{machine}'. Please use a provider that exists. provisioner_flag_invalid: |- '%{name}' is not a known provisioner. Please specify a valid provisioner. scp_permission_denied: |- Failed to upload a file to the guest VM via SCP due to a permissions error. This is normally because the user running Vagrant doesn't have read permission on the file. Please set proper permissions on the file: %{path} scp_unavailable: |- SSH server on the guest doesn't support SCP. Please install the necessary software to enable SCP on your guest operating system. shared_folder_create_failed: |- Failed to create the following shared folder on the host system. This is usually because Vagrant does not have sufficient permissions to create the folder. %{path} Please create the folder manually or specify another path to share. ssh_authentication_failed: |- SSH authentication failed! This is typically caused by the public/private keypair for the SSH user not being properly set on the guest VM. Please verify that the guest VM is setup with the proper public key, and that the private key path for Vagrant is setup properly as well. ssh_bad_exit_status: |- The following SSH command responded with a non-zero exit status. Vagrant assumes that this means the command failed! %{command} Stdout from the command: %{stdout} Stderr from the command: %{stderr} ssh_connect_eacces: |- SSH is getting permission denied errors when attempting to connect to the IP for SSH. This is usually caused by network rules and not being able to connect to the specified IP. Please try changing the IP on which the guest machine binds to for SSH. ssh_connection_refused: |- SSH connection was refused! This usually happens if the VM failed to boot properly. Some steps to try to fix this: First, try reloading your VM with `vagrant reload`, since a simple restart sometimes fixes things. If that doesn't work, destroy your VM and recreate it with a `vagrant destroy` followed by a `vagrant up`. If that doesn't work, contact a Vagrant maintainer (support channels listed on the website) for more assistance. ssh_connection_reset: |- SSH connection was reset! This usually happens when the machine is taking too long to reboot. First, try reloading your machine with `vagrant reload`, since a simple restart sometimes fixes things. If that doesn't work, destroy your machine and recreate it with a `vagrant destroy` followed by a `1vagrant up`. If that doesn't work, contact support. ssh_connection_timeout: |- Vagrant timed out while attempting to connect via SSH. This usually means that the VM booted, but there are issues with the SSH configuration or network connectivity issues. Please try to `vagrant reload` or `vagrant up` again. ssh_disconnected: |- The SSH connection was unexpectedly closed by the remote end. This usually indicates that SSH within the guest machine was unable to properly start up. Please boot the VM in GUI mode to check whether it is booting properly. ssh_no_route: |- While attempting to connect with SSH, a "no route to host" (EHOSTUNREACH) error was received. Please verify your network settings are correct and try again. ssh_host_down: |- While attempting to connect with SSH, a "host is down" (EHOSTDOWN) error was received. Please verify your SSH settings are correct and try again. ssh_is_putty_link: |- The `ssh` executable found in the PATH is a PuTTY Link SSH client. Vagrant is only compatible with OpenSSH SSH clients. Please install an OpenSSH SSH client or manually SSH in using your existing client using the information below. Host: %{host} Port: %{port} Username: %{username} Private key: %{key_path} ssh_key_bad_owner: |- The private key to connect to the machine via SSH must be owned by the user running Vagrant. This is a strict requirement from SSH itself. Please fix the following key to be owned by the user running Vagrant: %{key_path} ssh_key_bad_permissions: |- The private key to connect to this box via SSH has invalid permissions set on it. The permissions of the private key should be set to 0600, otherwise SSH will ignore the key. Vagrant tried to do this automatically for you but failed. Please set the permissions on the following file to 0600 and then try running this command again: %{key_path} ssh_key_type_not_supported: |- The private key you're attempting to use with this Vagrant box uses an unsupported encryption type. The SSH library Vagrant uses does not support this key type. Please use `ssh-rsa` or `ssh-dss` instead. Note that sometimes keys in your ssh-agent can interfere with this as well, so verify the keys are valid there in addition to standard file paths. ssh_not_ready: |- The provider for this Vagrant-managed machine is reporting that it is not yet ready for SSH. Depending on your provider this can carry different meanings. Make sure your machine is created and running and try again. Additionally, check the output of `vagrant status` to verify that the machine is in the state that you expect. If you continue to get this error message, please view the documentation for the provider you're using. ssh_port_not_detected: |- Vagrant couldn't determine the SSH port for your VM! Vagrant attempts to automatically find a forwarded port that matches your `config.ssh.guest_port` (default: 22) value and uses this for SSH. Alternatively, if `config.ssh.port` is set, it will use this. However, in this case Vagrant was unable to find a forwarded port that matches the guest port and `config.ssh.port` is not set! Please make sure that you have a forwarded port that goes to the configured guest port value, or specify an explicit SSH port with `config.ssh.port`. ssh_unavailable: "`ssh` binary could not be found. Is an SSH client installed?" ssh_unavailable_windows: |- `ssh` executable not found in any directories in the %PATH% variable. Is an SSH client installed? Try installing Cygwin, MinGW or Git, all of which contain an SSH client. Or use the PuTTY SSH client with the following authentication information shown below: Host: %{host} Port: %{port} Username: %{username} Private key: %{key_path} synced_folder_unusable: |- The synced folder type '%{type}' is reporting as unusable for your current setup. Please verify you have all the proper prerequisites for using this shared folder type and try again. test_key: "test value" ui_expects_tty: |- Vagrant is attempting to interface with the UI in a way that requires a TTY. Most actions in Vagrant that require a TTY have configuration switches to disable this requirement. Please do that or run Vagrant with TTY. unimplemented_provider_action: |- Vagrant attempted to call the action '%{action}' on the provider '%{provider}', but this provider doesn't support this action. This is probably a bug in either the provider or the plugin calling this action, and should be reported. vagrantfile_exists: |- `Vagrantfile` already exists in this directory. Remove it before running `vagrant init`. vagrantfile_load_error: |- There was an error loading a Vagrantfile. The file being loaded and the error message are shown below. This is usually caused by a syntax error. Path: %{path} Message: %{message} vagrantfile_syntax_error: |- There is a syntax error in the following Vagrantfile. The syntax error message is reproduced below for convenience: %{file} vagrantfile_write_error: |- The user that is running Vagrant doesn't have the proper permissions to write a Vagrantfile to the specified location. Please ensure that you call `vagrant init` in a location where the proper permissions are in place to create a Vagrantfile. vagrant_version_bad: |- This Vagrant environment has specified that it requires the Vagrant version to satisfy the following version requirements: %{requirements} You are running Vagrant %{version}, which does not satisify these requirements. Please change your Vagrant version or update the Vagrantfile to allow this Vagrant version. However, be warned that if the Vagrantfile has specified another version, it probably has good reason to do so, and changing that may cause the environment to not function properly. vboxmanage_error: |- There was an error while executing `VBoxManage`, a CLI used by Vagrant for controlling VirtualBox. The command and stderr is shown below. Command: %{command} Stderr: %{stderr} vboxmanage_not_found_error: |- The "VBoxManage" command or one of its dependencies could not be found. Please verify VirtualBox is properly installed. You can verify everything is okay by running "VBoxManage --version" and verifying that the VirtualBox version is outputted. virtualbox_broken_version_040214: |- Vagrant detected you have VirtualBox 4.2.14 installed. VirtualBox 4.2.14 contains a critical bug which prevents it from working with Vagrant. VirtualBox 4.2.16+ fixes this problem. Please upgrade VirtualBox. virtualbox_guest_property_not_found: |- Could not find a required VirtualBox guest property: %{guest_property} This is an internal error that should be reported as a bug. virtualbox_invalid_version: |- Vagrant has detected that you have a version of VirtualBox installed that is not supported. Please install one of the supported versions listed below to use Vagrant: %{supported_versions} virtualbox_kernel_module_not_loaded: |- VirtualBox is complaining that the kernel module is not loaded. Please run `VBoxManage --version` or open the VirtualBox GUI to see the error message which should contain instructions on how to fix this error. virtualbox_install_incomplete: |- VirtualBox is complaining that the installation is incomplete. Please run `VBoxManage --version` to see the error message which should contain instructions on how to fix this error. virtualbox_no_room_for_high_level_network: |- There is no available slots on the VirtualBox VM for the configured high-level network interfaces. "private_network" and "public_network" network configurations consume a single network adapter slot on the VirtualBox VM. VirtualBox limits the number of slots to 8, and it appears that every slot is in use. Please lower the number of used network adapters. virtualbox_not_detected: |- Vagrant could not detect VirtualBox! Make sure VirtualBox is properly installed. Vagrant uses the `VBoxManage` binary that ships with VirtualBox, and requires this to be available on the PATH. If VirtualBox is installed, please find the `VBoxManage` binary and add it to the PATH environmental variable. vm_creation_required: |- VM must be created before running this command. Run `vagrant up` first. vm_inaccessible: |- Your VM has become "inaccessible." Unfortunately, this is a critical error with VirtualBox that Vagrant can not cleanly recover from. Please open VirtualBox and clear out your inaccessible virtual machines or find a way to fix them. vm_name_exists: |- A VirtualBox machine with the name '%{name}' already exists. Please use another name or delete the machine with the existing name, and try again. vm_no_match: |- No virtual machines matched the regular expression given. vm_not_found: |- A VM by the name of %{name} was not found. vm_not_running: |- VM must be running to open SSH connection. Run `vagrant up` to start the virtual machine. #------------------------------------------------------------------------------- # Translations for config validation errors #------------------------------------------------------------------------------- config: common: bad_field: "The following settings shouldn't exist: %{fields}" error_empty: "`%{field}` must be not be empty." chef: cookbooks_path_empty: "Must specify a cookbooks path for chef solo." environment_path_missing: |- Environment path not found: %{path} environment_path_required: |- When 'environment' is specified, you must provide 'environments_path'. cookbooks_path_missing: |- Cookbook path doesn't exist: %{path} custom_config_path_missing: |- Path specified for "custom_config_path" does not exist. server_url_empty: "Chef server URL must be populated." validation_key_path: "Validation key path must be valid path to your chef server validation key." loader: bad_v1_key: |- Unknown configuration section '%{key}'. If this section was part of a Vagrant 1.0.x plugin, note that 1.0.x plugins are incompatible with 1.1+. root: bad_key: |- Unknown configuration section '%{key}'. ssh: private_key_missing: "`private_key_path` file must exist: %{path}" vm: base_mac_invalid: "Base MAC address for eth0/NAT must be set. Contact box maintainer for more information." box_download_ca_cert_not_found: |- "box_download_ca_cert" file not found: %{path} box_download_checksum_blank: |- Checksum type specified but "box_download_checksum" is blank box_download_checksum_notblank: |- Checksum specified but must also specify "box_download_checksum_type" box_missing: "A box must be specified." box_not_found: "The box '%{name}' could not be found." hostname_invalid_characters: |- The hostname set for the VM should only contain letters, numbers, hyphens or dots. It cannot start with a hyphen or dot. network_invalid: |- The network type '%{type}' is not valid. Please use 'hostonly' or 'bridged'. network_ip_invalid: |- The host only network IP '%{ip}' is invalid. network_ip_ends_one: |- The host only network IP '%{ip}' must not end in a 1, as this is reserved for the host machine. nfs_not_supported: |- It appears your machine doesn't support NFS, or there is not an adapter to enable NFS on this machine for Vagrant. Please verify that `nfsd` is installed on your machine, and try again. If you're on Windows, NFS isn't supported. If the problem persists, please contact Vagrant support. nfs_requires_host: |- Using NFS shared folders requires a host to be specified using `config.vagrant.host`. network_ip_ends_in_one: |- Static IPs cannot end in ".1" since that address is always reserved for the router. Please use another ending. network_ip_required: |- An IP is required for a private network. network_fp_host_not_unique: |- Forwarded port '%{host}' (host port) is declared multiple times with the protocol '%{protocol}'. network_fp_requires_ports: |- Forwarded port definitions require a "host" and "guest" value network_type_invalid: |- Network type '%{type}' is invalid. Please use a valid network type. provisioner_not_found: |- The '%{name}' provisioner could not be found. shared_folder_guestpath_duplicate: |- A shared folder guest path is used multiple times. Shared folders must all map to a unique guest path: %{path} shared_folder_guestpath_relative: |- The shared folder guest path must be absolute: %{path} shared_folder_hostpath_missing: |- The host path of the shared folder is missing: %{path} shared_folder_nfs_owner_group: |- Shared folder that have NFS enabled do no support owner/group attributes. Host path: %{path} shared_folder_mount_options_array: |- Shared folder mount options specified by 'mount_options' must be an array of options. #------------------------------------------------------------------------------- # Translations for commands. e.g. `vagrant x` #------------------------------------------------------------------------------- commands: common: vm_already_running: |- VirtualBox VM is already running. vm_not_created: "VM not created. Moving on..." vm_not_running: "VM is not currently running. Please, first bring it up with `vagrant up` then run this command." box: remove_must_specify_provider: |- Multiple providers were found for the box '%{name}'. Please specify the specific provider for the box you want to remove. The list of providers backing this box is: '%{providers}' To remove the box for a specific provider, run the following command, filling in PROVIDER with one of the providers above: vagrant box remove '%{name}' PROVIDER no_installed_boxes: "There are no installed boxes! Use `vagrant box add` to add some." removing: |- Removing box '%{name}' with provider '%{provider}'... destroy: confirmation: "Are you sure you want to destroy the '%{name}' VM? [y/N] " will_not_destroy: |- The VM '%{name}' will not be destroyed, since the confirmation was declined. init: success: |- A `Vagrantfile` has been placed in this directory. You are now ready to `vagrant up` your first virtual environment! Please read the comments in the Vagrantfile as well as documentation on `vagrantup.com` for more information on using Vagrant. plugin: installed_license: |- The license for '%{name}' was successfully installed! installing_license: |- Installing license for '%{name}'... no_plugins: |- No plugins installed. installed: |- Installed the plugin '%{name} (%{version})'! installing: |- Installing the '%{name}' plugin. This can take a few minutes... uninstalling: |- Uninstalling the '%{name}' plugin... post_install: |- Post install message from the '%{name}' plugin: %{message} status: aborted: |- The VM is in an aborted state. This means that it was abruptly stopped without properly closing the session. Run `vagrant up` to resume this virtual machine. If any problems persist, you may have to destroy and restart the virtual machine. gurumeditation: |- The VM is in the "guru meditation" state. This is a rare case which means that an internal error in VitualBox caused the VM to fail. This is always the sign of a bug in VirtualBox. You can try to bring your VM back online with a `vagrant up`. inaccessible: |- The VM is inaccessible! This is a rare case which means that VirtualBox can't find your VM configuration. This usually happens when upgrading VirtualBox, moving to a new computer, etc. Please consult VirtualBox for how to handle this issue. output: |- Current machine states: %{states} %{message} not_created: |- The environment has not yet been created. Run `vagrant up` to create the environment. If a machine is not created, only the default provider will be shown. So if a provider is not listed, then the machine is not created for that environment. paused: |- The VM is paused. This VM may have been paused via the VirtualBox GUI or the VBoxManage command line interface. To unpause, please use the VirtualBox GUI and/or VBoxManage command line interface so that vagrant would be able to control the VM again. poweroff: |- The VM is powered off. To restart the VM, simply run `vagrant up` running: |- The VM is running. To stop this VM, you can run `vagrant halt` to shut it down forcefully, or you can run `vagrant suspend` to simply suspend the virtual machine. In either case, to restart it again, simply run `vagrant up`. saving: |- The VM is currently saving its state. In a few moments this state should transition to "saved." Please run `vagrant status` again in a few seconds. saved: |- To resume this VM, simply run `vagrant up`. stuck: |- The VM is "stuck!" This is a very rare state which means that VirtualBox is unable to recover the current state of the VM. The only known solution to this problem is to restart your machine, sorry. listing: |- This environment represents multiple VMs. The VMs are all listed above with their current state. For more information about a specific VM, run `vagrant status NAME`. up: upping: |- Bringing machine '%{name}' up with '%{provider}' provider... version: output: "Vagrant version %{version}" #------------------------------------------------------------------------------- # Translations for Vagrant middleware acions #------------------------------------------------------------------------------- actions: runner: waiting_cleanup: "Waiting for cleanup before exiting..." exit_immediately: "Exiting immediately, without cleanup!" vm: boot: booting: Booting VM... failed_to_boot: |- Failed to connect to VM via SSH. Please verify the VM successfully booted by looking at the VirtualBox GUI. failed_to_run: |- The VM failed to remain in the "running" state while attempting to boot. This is normally caused by a misconfiguration or host system incompatibilities. Please open the VirtualBox GUI and attempt to boot the virtual machine manually to get a more informative error message. bridged_networking: available: |- Available bridged network interfaces: bridging: |- Bridging adapter #%{adapter} to '%{bridge}' enabling: |- Enabling bridged network... preparing: |- Preparing bridged networking... specific_not_found: |- Specific bridge '%{bridge}' not found. You may be asked to specify which network to bridge to. check_box: not_found: |- Box '%{name}' was not found. Fetching box from specified URL for the provider '%{provider}'. Note that if the URL does not have a box for this provider, you should interrupt Vagrant now and add the box yourself. Otherwise Vagrant will attempt to download the full box prior to discovering this error. not_specified: |- No base box was specified! A base box is required as a staring point for every vagrant virtual machine. Please specify one in your Vagrantfile using `config.vm.box` does_not_exist: |- Specified box `%{name}` does not exist! The box must be added through the `vagrant box add` command. Please view the documentation associated with the command for more information. check_guest_additions: not_detected: |- No guest additions were detected on the base box for this VM! Guest additions are required for forwarded ports, shared folders, host only networking, and more. If SSH fails on this machine, please install the guest additions and repackage the box to continue. This is not an error message; everything may continue to work properly, in which case you may ignore this message. version_mismatch: |- The guest additions on this VM do not match the installed version of VirtualBox! In most cases this is fine, but in rare cases it can prevent things such as shared folders from working properly. If you see shared folder errors, please make sure the guest additions within the virtual machine match the version of VirtualBox you have installed on your host and reload your VM. Guest Additions Version: %{guest_version} VirtualBox Version: %{virtualbox_version} clear_forward_ports: deleting: Clearing any previously set forwarded ports... clear_network_interfaces: deleting: Clearing any previously set network interfaces... clear_shared_folders: deleting: Cleaning previously set shared folders... customize: failure: |- A customization command failed: %{command} The following error was experienced: %{error} Please fix this customization and try again. running: Running '%{event}' VM customizations... destroy: destroying: Destroying VM and associated drives... destroy_network: destroying: Destroying unused networking interface... disable_networks: disabling: Disabling host only networks... discard_state: discarding: Discarding saved state of VM... export: create_dir: Creating temporary directory for export... exporting: Exporting VM... power_off: "The Vagrant virtual environment you are trying to package must be powered off." forward_ports: auto_empty: |- Vagrant found a port collision for the specified port and virtual machine. While this port was marked to be auto-corrected, the ports in the auto-correction range are all also used. VM: %{vm_name} Forwarded port: %{guest_port} => %{host_port} collision_error: |- Vagrant cannot forward the specified ports on this VM, since they would collide with some other application that is already listening on these ports. The forwarded port to %{host_port} is already in use on the host machine. To fix this, modify your current projects Vagrantfile to use another port. Example, where '1234' would be replaced by a unique host port: config.vm.network :forwarded_port, guest: %{guest_port}, host: 1234 Sometimes, Vagrant will attempt to auto-correct this for you. In this case, Vagrant was unable to. This is usually because the guest machine is in a state which doesn't allow modifying port forwarding. fixed_collision: |- Fixed port collision for %{guest_port} => %{host_port}. Now on port %{new_port}. forwarding: Forwarding ports... forwarding_entry: |- -- %{guest_port} => %{host_port} (adapter %{adapter}) non_nat: |- VirtualBox adapter #%{adapter} not configured as "NAT". Skipping port forwards on this adapter. privileged_ports: |- You are trying to forward to privileged ports (ports <= 1024). Most operating systems restrict this to only privileged process (typically processes running as an administrative user). This is a warning in case the port forwarding doesn't work. If any problems occur, please try a port higher than 1024. halt: force: |- Forcing shutdown of VM... graceful: |- Attempting graceful shutdown of VM... hostname: setting: "Setting hostname..." import: importing: Importing base box '%{name}'... failure: |- The VM import failed! Try running `VBoxManage import` on the box file manually for more verbose error output. match_mac: matching: Matching MAC address for NAT networking... no_base_mac: |- No base MAC address was specified. This is required for the NAT networking to work properly (and hence port forwarding, SSH, etc.). Specifying this MAC address is typically up to the box and box maintiner. Please contact the relevant person to solve this issue. network: adapter_collision: |- More than one network have been assigned to the same adapter. Please make sure your networks you've configured in your Vagrantfile do not overlap. configuring: |- Configuring and enabling network interfaces... dhcp_already_attached: |- A host only network interface you're attempting to configure via DHCP already has a conflicting host only adapter with DHCP enabled. The DHCP on this adapter is incompatible with the DHCP settings. Two host only network interfaces are not allowed to overlap, and each host only network interface can have only one DHCP server. Please reconfigure your host only network or remove the virtual machine using the other host only network. no_adapters: |- No available adapters on the virtual machine were found to accomodate for all configured networks. VirtualBox virtual machines have 8 network interfaces available usually, so please lower the number of networks to below 8. preparing: |- Preparing network interfaces based on configuration... host_only_network: collides: |- The specified host network collides with a non-hostonly network! This will cause your specified IP to be inaccessible. Please change the IP or name of your host only network so that it no longer matches that of a bridged or non-hostonly network. creating: "Creating new host only network for environment..." enabling: "Enabling host only network..." not_found: |- The specified host network could not be found: '%{name}.' If the name specification is removed, Vagrant will create a new host only network for you. Alternatively, please create the specified network manually. preparing: "Preparing host only network..." nfs: exporting: Exporting NFS shared folders... mounting: Mounting NFS shared folders... persist: dotfile_error: |- The dotfile which Vagrant uses to store the UUID of the project's virtual machine already exists and is not a file! The dotfile is currently configured to be '%{dotfile_path}' To change this value, please see `config.vagrant.dotfile_name` Are you trying to use Vagrant from your home directory? This is the leading cause of this error message. To resolve this, simply use a different directory. Or, if you really want to run Vagrant from your home directory, modify the `config.vagrant.dotfile_name` configuration key. persisting: "Persisting the VM UUID (%{uuid})..." provision: beginning: "Running provisioner: %{provisioner}..." disabled_by_sentinel: |- VM already provisioned. Run `vagrant provision` or use `--provision` to force it resume: resuming: Resuming suspended VM... unpausing: |- Unpausing the VM... share_folders: creating: Creating shared folders metadata... mounting: Mounting shared folders... mounting_entry: "-- %{guest_path}" nomount_entry: "-- Automounting disabled: %{host_path}" set_name: setting_name: |- Setting the name of the VM... suspend: suspending: Saving VM state and suspending execution... box: unpackage: untar_failure: |- The box failed to unpackage properly. Please verify that the box file you're trying to add is not corrupted and try again. The output from attempting to unpackage (if any): %{output} already_exists: |- The box you're attempting to add already exists: Name: %{name} Provider: %{formats} add: adding: |- Extracting box... added: |- Successfully added box '%{name}' with provider '%{provider}'! checksumming: |- Calculating and comparing box checksum... destroy: destroying: "Deleting box '%{name}'..." download: cleaning: "Cleaning up downloaded box..." downloading: "Downloading box from URL: %{url}" download_failed: |- Download failed. Will try another box URL if there is one. interrupted: "Box download was interrupted. Exiting." no_provider_cant_check: |- A provider wasn't explicitly provided to the "box add" command. Without a provider, Vagrant can't verify if this box already exists until the box is fully downloaded. This is just a warning, and is not an error. resuming: "Box download is resuming from prior download progress" verify: verifying: "Verifying box..." failed: |- The box file you're attempting to add is invalid. This can be commonly attributed to typos in the path given to the box add command. Another common case of this is invalid packaging of the box itself. general: package: packaging: "Packaging additional file: %{file}" compressing: "Compressing package to: %{tar_path}" output_exists: |- The specified file to save the package as already exists. Please remove this file or specify a different file name for outputting. output_is_directory: |- The specified output is a directory. Please specify a path including a filename. requires_directory: |- A directory was not specified to package. This should never happen and is a result of an internal inconsistency. include_file_missing: |- Package include file doesn't exist: %{file} downloaders: file: download: "Copying box to temporary location..." file_missing: "The specified path to a file doesn't exist." http: connection_reset: |- The remote server unexpectedly closed the connection. Vagrant was unable to finish downloading the box. Please try again. If this problem persists, please try downloading the box manually outside of Vagrant, then adding the box from the filesystem. connection_timeout: |- Vagrant timed out while attempting to connect to the HTTP host. Please check your internet and proxy settings and try again. download: "Downloading box: %{url}" socket_error: |- An error occurred while trying to download the specified box. This most often happens if there is no internet connection or the address is invalid. status_error: |- Bad status code: %{status} Please verify that the box exists and is accessible. Also verify that this computer is properly connected to the internet. hosts: bsd: nfs_export: |- Preparing to edit /etc/exports. Administrator privileges will be required... nfs_prune: |- Pruning invalid NFS exports. Administrator privileges will be required... linux: nfs_export: |- Preparing to edit /etc/exports. Administrator privileges will be required... nfs_prune: |- Pruning invalid NFS exports. Administrator privileges will be required... arch: nfs_export: prepare: "Preparing to edit /etc/exports. Administrator privileges will be required..." freebsd: nfs_whitespace: |- FreeBSD hosts do not support sharing directories with whitespace in their path. Please adjust your path accordingly. provisioners: chef: chef_not_detected: |- The chef binary (either `chef-solo` or `chef-client`) was not found on the VM and is required for chef provisioning. Please verify that chef is installed and that the binary is available on the PATH. cookbook_folder_not_found_warning: "The cookbook path '%{path}' doesn't exist. Ignoring..." json: "Generating chef JSON and uploading..." client_key_folder: "Creating folder to hold client key..." upload_validation_key: "Uploading chef client validation key..." upload_encrypted_data_bag_secret_key: "Uploading chef encrypted data bag secret key..." running_client: "Running chef-client..." running_client_again: "Running chef-client again (failed to converge)..." running_solo: "Running chef-solo..." running_solo_again: "Running chef-solo again (failed to converge)..." missing_shared_folders: |- Shared folders that Chef requires are missing on the virtual machine. This is usually due to configuration changing after already booting the machine. The fix is to run a `vagrant reload` so that the proper shared folders will be prepared and mounted on the VM. no_convergence: |- Chef never successfully completed! Any errors should be visible in the output above. Please fix your recipes so that they properly complete. not_detected: |- The `%{binary}` binary appears not to be in the PATH of the guest. This could be because the PATH is not properly setup or perhaps chef is not installed on this guest. Chef provisioning can not continue without chef properly installed. server_url_required: |- Chef server provisioning requires that the `config.chef.chef_server_url` be set to the URL of your chef server. Examples include "http://12.12.12.12:4000" and "http://myserver.com:4000" (the port of course can be different, but 4000 is the default) server_validation_key_required: |- Chef server provisioning requires that the `config.chef.validation_key_path` configuration be set to a path on your local machine of the validation key used to register the VM with the chef server. server_validation_key_doesnt_exist: |- The validation key set for `config.chef.validation_key_path` does not exist! This file needs to exist so it can be uploaded to the virtual machine. deleting_from_server: "Deleting %{deletable} \"%{name}\" from Chef server..." file: no_dest_file: "File destination must be specified." no_source_file: "File source must be specified." path_invalid: "File upload source file %{path} must exist" puppet: not_detected: |- The `%{binary}` binary appears not to be in the PATH of the guest. This could be because the PATH is not properly setup or perhaps Puppet is not installed on this guest. Puppet provisioning can not continue without Puppet properly installed. running_puppet: "Running Puppet with %{manifest}..." manifest_missing: |- The configured Puppet manifest is missing. Please specify a path to an existing manifest: %{manifest} manifests_path_missing: "The manifests path specified for Puppet does not exist: %{path}" missing_shared_folders: |- Shared folders that Puppet requires are missing on the virtual machine. This is usually due to configuration changing after already booting the machine. The fix is to run a `vagrant reload` so that the proper shared folders will be prepared and mounted on the VM. module_path_missing: "The configured module path doesn't exist: %{path}" puppet_server: cert_requires_node: |- "puppet_node" is required when a client cert or key is specified client_cert_and_private_key: |- Both a client certificate and private key must be specified, if any client_cert_not_found: |- The specified client cert path could not be found client_private_key_not_found: |- The specified client private key path could not be found not_detected: |- The `%{binary}` binary appears not to be in the PATH of the guest. This could be because the PATH is not properly setup or perhaps Puppet is not installed on this guest. Puppet provisioning can not continue without Puppet properly installed. running_puppetd: "Running Puppet agent..." uploading_client_cert: |- Uploading client certificate and private key... shell: args_bad_type: "Shell provisioner `args` must be a string or array." no_path_or_inline: "One of `path` or `inline` must be set." path_and_inline_set: "Only one of `path` or `inline` may be set." path_invalid: "`path` for shell provisioner does not exist on the host system: %{path}" running: "Running: %{script}" upload_path_not_set: "`upload_path` must be set for the shell provisioner." ansible: no_playbook: "`playbook` must be set for the Ansible provisioner." playbook_path_invalid: "`playbook` for the Ansible provisioner does not exist on the host system: %{path}" inventory_path_invalid: "`inventory_path` for the Ansible provisioner does not exist on the host system: %{path}" extra_vars_invalid: "`extra_vars` for the Ansible provisioner must be a hash or a path to an existing file. Received: %{value} (as %{type})" docker: not_running: "Docker is not running on the guest VM." install_failed: "Docker installation failed." salt: missing_key: |- You must include both public and private keys. must_accept_keys: |- You must accept keys when running highstate with master! vagrant-1.4.3/templates/nfs/000077500000000000000000000000001226132634600157525ustar00rootroot00000000000000vagrant-1.4.3/templates/nfs/exports.erb000066400000000000000000000003461226132634600201530ustar00rootroot00000000000000# VAGRANT-BEGIN: <%= user %> <%= uuid %> <% folders.each do |dirs, opts| %> <%= dirs.map { |d| "\"#{d}\"" }.join(" ") %> <%= ips.join(" ") %> <%=opts[:bsd__compiled_nfs_options] %> <% end %> # VAGRANT-END: <%= user %> <%= uuid %> vagrant-1.4.3/templates/nfs/exports_freebsd.erb000066400000000000000000000003461226132634600216450ustar00rootroot00000000000000# VAGRANT-BEGIN: <%= user %> <%= uuid %> <% folders.each do |dirs, opts| %> <%= dirs.map { |d| "\"#{d}\"" }.join(" ") %> <%= ips.join(" ") %> <%=opts[:bsd__compiled_nfs_options] %> <% end %> # VAGRANT-END: <%= user %> <%= uuid %> vagrant-1.4.3/templates/nfs/exports_linux.erb000066400000000000000000000003551226132634600213720ustar00rootroot00000000000000# VAGRANT-BEGIN: <%= user %> <%= uuid %> <% ips.each do |ip| %> <% folders.each do |name, opts| %> "<%= opts[:hostpath] %>" <%= ip %>(<%= opts[:linux__nfs_options].join(",") %>) <% end %> <% end %> # VAGRANT-END: <%= user %> <%= uuid %> vagrant-1.4.3/templates/package_Vagrantfile.erb000066400000000000000000000007741226132634600216030ustar00rootroot00000000000000Vagrant::Config.run do |config| # This Vagrantfile is auto-generated by `vagrant package` to contain # the MAC address of the box. Custom configuration should be placed in # the actual `Vagrantfile` in this box. config.vm.base_mac = "<%= base_mac %>" end # Load include vagrant file if it exists after the auto-generated # so it can override any of the settings include_vagrantfile = File.expand_path("../include/_Vagrantfile", __FILE__) load include_vagrantfile if File.exist?(include_vagrantfile) vagrant-1.4.3/templates/provisioners/000077500000000000000000000000001226132634600177265ustar00rootroot00000000000000vagrant-1.4.3/templates/provisioners/chef_client/000077500000000000000000000000001226132634600221715ustar00rootroot00000000000000vagrant-1.4.3/templates/provisioners/chef_client/client.erb000066400000000000000000000022211226132634600241360ustar00rootroot00000000000000log_level <%= log_level.inspect %> log_location STDOUT verbose_logging <%= verbose_logging.inspect %> <% if node_name %> node_name "<%= node_name %>" <% end %> ssl_verify_mode :verify_none chef_server_url "<%= chef_server_url %>" validation_client_name "<%= validation_client_name %>" validation_key "<%= validation_key %>" client_key "<%= client_key %>" encrypted_data_bag_secret "<%= encrypted_data_bag_secret %>" <% if environment %> environment "<%= environment %>" <% end %> file_cache_path "<%= file_cache_path %>" file_backup_path "<%= file_backup_path %>" http_proxy <%= http_proxy.inspect %> http_proxy_user <%= http_proxy_user.inspect %> http_proxy_pass <%= http_proxy_pass.inspect %> https_proxy <%= https_proxy.inspect %> https_proxy_user <%= https_proxy_user.inspect %> https_proxy_pass <%= https_proxy_pass.inspect %> no_proxy <%= no_proxy.inspect %> pid_file "/var/run/chef/chef-client.pid" Mixlib::Log::Formatter.show_time = true <% if formatter %> add_formatter "<%= formatter %>" <% end %> <% if custom_configuration -%> load "<%= custom_configuration %>" <% end -%> vagrant-1.4.3/templates/provisioners/chef_solo/000077500000000000000000000000001226132634600216675ustar00rootroot00000000000000vagrant-1.4.3/templates/provisioners/chef_solo/solo.erb000066400000000000000000000021721226132634600233370ustar00rootroot00000000000000<% if node_name %> node_name "<%= node_name %>" <% end %> file_cache_path "<%= file_cache_path %>" file_backup_path "<%= file_backup_path %>" cookbook_path <%= cookbooks_path.inspect %> <% if roles_path %> role_path <%= roles_path.inspect %> <% end %> log_level <%= log_level.inspect %> verbose_logging <%= verbose_logging.inspect %> encrypted_data_bag_secret "<%= encrypted_data_bag_secret %>" <% if data_bags_path -%> data_bag_path <%= data_bags_path.inspect %> <% end %> <% if recipe_url -%> recipe_url "<%= recipe_url %>" <% end -%> <% if environments_path %> environment_path <%= environments_path.inspect %> <% end -%> <% if environment %> environment "<%= environment %>" <% end -%> http_proxy <%= http_proxy.inspect %> http_proxy_user <%= http_proxy_user.inspect %> http_proxy_pass <%= http_proxy_pass.inspect %> https_proxy <%= https_proxy.inspect %> https_proxy_user <%= https_proxy_user.inspect %> https_proxy_pass <%= https_proxy_pass.inspect %> no_proxy <%= no_proxy.inspect %> <% if formatter %> add_formatter "<%= formatter %>" <% end %> <% if custom_configuration -%> load "<%= custom_configuration %>" <% end -%> vagrant-1.4.3/templates/rgloader.rb000066400000000000000000000005421226132634600173110ustar00rootroot00000000000000# This file loads the proper rgloader/loader.rb file that comes packaged # with Vagrant so that encoded files can properly run with Vagrant. if ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"] require File.expand_path( "rgloader/loader", ENV["VAGRANT_INSTALLER_EMBEDDED_DIR"]) else raise "Encoded files can't be read outside of the Vagrant installer." end vagrant-1.4.3/test/000077500000000000000000000000001226132634600141455ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance/000077500000000000000000000000001226132634600162335ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance/base.rb000066400000000000000000000001171226132634600174710ustar00rootroot00000000000000require "vagrant-spec/acceptance" require_relative "shared/context_virtualbox" vagrant-1.4.3/test/acceptance/shared/000077500000000000000000000000001226132634600175015ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance/shared/context_virtualbox.rb000066400000000000000000000001651226132634600237730ustar00rootroot00000000000000shared_context "provider-context/virtualbox" do let(:extra_env) {{ "VBOX_USER_HOME" => "{{homedir}}", }} end vagrant-1.4.3/test/acceptance_old/000077500000000000000000000000001226132634600170715ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/base.rb000066400000000000000000000025001226132634600203250ustar00rootroot00000000000000require "rubygems" require "rspec/autorun" require "log4r" # Add the test directory to the load path $:.unshift File.expand_path("../../", __FILE__) # Load in the supporting files for our tests require "acceptance/support/shared/base_context" require "acceptance/support/config" require "acceptance/support/virtualbox" require "acceptance/support/matchers/match_output" require "acceptance/support/matchers/succeed" # Do not buffer output $stdout.sync = true $stderr.sync = true # If VirtualBox is currently running, fail. if Acceptance::VirtualBox.find_vboxsvc $stderr.puts "VirtualBox must be closed and remain closed for the duration of the tests." abort end # Enable logging if requested if ENV["ACCEPTANCE_LOG"] logger = Log4r::Logger.new("test") logger.outputters = Log4r::Outputter.stdout logger.level = Log4r.const_get(ENV["ACCEPTANCE_LOG"].upcase) logger = nil end # Parse the command line options and load the global configuration. if !ENV.has_key?("ACCEPTANCE_CONFIG") $stderr.puts "A configuration file must be passed into the acceptance test." abort elsif !File.file?(ENV["ACCEPTANCE_CONFIG"]) $stderr.puts "The configuration file must exist." abort end $acceptance_options = Acceptance::Config.new(ENV["ACCEPTANCE_CONFIG"]) # Configure RSpec RSpec.configure do |c| c.expect_with :rspec, :stdlib end vagrant-1.4.3/test/acceptance_old/box_test.rb000066400000000000000000000064201226132634600212470ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) describe "vagrant box" do include_context "acceptance" it "has no boxes by default" do result = execute("vagrant", "box", "list") result.stdout.should match_output(:no_boxes) end it "can add a box from a file" do require_box("default") # Add the box, which we expect to succeed result = execute("vagrant", "box", "add", "foo", box_path("default")) result.should succeed # Verify that the box now shows up in the list of available boxes result = execute("vagrant", "box", "list") result.stdout.should match_output(:box_installed, "foo") end it "errors if attempting to add a box with the same name" do require_box("default") # Add the box, which we expect to succeed assert_execute("vagrant", "box", "add", "foo", box_path("default")) # Adding it again should not succeed result = execute("vagrant", "box", "add", "foo", box_path("default")) result.should_not succeed result.stderr.should match_output(:box_already_exists, "foo") end it "overwrites a box when adding with `--force`" do require_box("default") # Add the box, which we expect to succeed assert_execute("vagrant", "box", "add", "foo", box_path("default")) # Adding it again should not succeed assert_execute("vagrant", "box", "add", "foo", box_path("default"), "--force") end it "gives an error if the file doesn't exist" do result = execute("vagrant", "box", "add", "foo", "/tmp/nope/nope/nope/nonono.box") result.should_not succeed result.stderr.should match_output(:box_path_doesnt_exist) end it "gives an error if the file is not a valid box" do invalid = environment.workdir.join("nope.txt") invalid.open("w+") do |f| f.write("INVALID!") end result = execute("vagrant", "box", "add", "foo", invalid.to_s) result.should_not succeed result.stderr.should match_output(:box_invalid) end it "can add a box from an HTTP server" do pending("Need to setup HTTP server functionality") end it "can remove a box" do require_box("default") # Add the box, remove the box, then verify that the box no longer # shows up in the list of available boxes. execute("vagrant", "box", "add", "foo", box_path("default")) execute("vagrant", "box", "remove", "foo") result = execute("vagrant", "box", "list") result.should succeed result.stdout.should match_output(:no_boxes) end it "can repackage a box" do require_box("default") original_size = File.size(box_path("default")) logger.debug("Original package size: #{original_size}") # Add the box, repackage it, and verify that a package.box is # dumped of relatively similar size. execute("vagrant", "box", "add", "foo", box_path("default")) execute("vagrant", "box", "repackage", "foo") # By default, repackage should dump into package.box into the CWD repackaged_file = environment.workdir.join("package.box") repackaged_file.file?.should be, "package.box should exist in cwd of environment" # Compare the sizes repackaged_size = repackaged_file.size logger.debug("Repackaged size: #{repackaged_size}") size_diff = (repackaged_size - original_size).abs size_diff.should be < 1000, "Sizes should be very similar" end end vagrant-1.4.3/test/acceptance_old/destroy_test.rb000066400000000000000000000023511226132634600221470ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/shared/command_examples" describe "vagrant destroy" do include_context "acceptance" it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "destroy"] it "succeeds and ignores if the VM is not created" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") result = assert_execute("vagrant", "destroy") result.stdout.should match_output(:vm_not_created_warning) end it "is able to destroy a running virtual machine" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") assert_execute("vagrant", "up") # Destroy the VM and assert that it worked properly (seemingly) result = assert_execute("vagrant", "destroy") result.stdout.should match_output(:vm_destroyed) # Assert that the VM no longer is created result = assert_execute("vagrant", "status") result.stdout.should match_output(:status, "default", "not created") end # TODO: # it is able to destroy a halted virtual machine # it is able to destroy a suspended virtual machine end vagrant-1.4.3/test/acceptance_old/halt_test.rb000066400000000000000000000046411226132634600214120ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/shared/command_examples" describe "vagrant halt" do include_context "acceptance" it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "halt"] it "succeeds and ignores if the VM is not created" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") result = assert_execute("vagrant", "halt") result.stdout.should match_output(:vm_not_created_warning) end it "is able to halt a running virtual machine" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") assert_execute("vagrant", "up") # Halt the VM and assert that it worked properly (seemingly) result = assert_execute("vagrant", "halt") result.stdout.should match_output(:vm_halt_graceful) # Assert that the VM is no longer running result = assert_execute("vagrant", "status") result.stdout.should match_output(:status, "default", "poweroff") end it "is able to force halt a running virtual machine" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") assert_execute("vagrant", "up") # Halt the VM and assert that it worked properly (seemingly) result = assert_execute("vagrant", "halt", "--force") result.stdout.should match_output(:vm_halt_force) # Assert that the VM is no longer running result = assert_execute("vagrant", "status") result.stdout.should match_output(:status, "default", "poweroff") end it "is able to come back up after the machine has been halted" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") assert_execute("vagrant", "up") assert_execute("vagrant", "halt") # Assert that the VM is no longer running result = assert_execute("vagrant", "status") result.stdout.should match_output(:status, "default", "poweroff") assert_execute("vagrant", "up") # Assert that the VM is once again running result = assert_execute("vagrant", "status") result.stdout.should match_output(:status, "default", "running") end # TODO: # halt behavior on suspend machine # halt behavior if machine is already powered off end vagrant-1.4.3/test/acceptance_old/init_test.rb000066400000000000000000000020551226132634600214220ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) describe "vagrant init" do include_context "acceptance" it "creates a Vagrantfile in the working directory" do vagrantfile = environment.workdir.join("Vagrantfile") vagrantfile.exist?.should_not be, "Vagrantfile shouldn't exist initially" result = execute("vagrant", "init") result.should succeed vagrantfile.exist?.should be, "Vagrantfile should exist" end it "creates a Vagrantfile with the box set to the given argument" do vagrantfile = environment.workdir.join("Vagrantfile") result = execute("vagrant", "init", "foo") result.should succeed vagrantfile.read.should match(/config.vm.box = "foo"$/) end it "creates a Vagrantfile with the box URL set to the given argument" do vagrantfile = environment.workdir.join("Vagrantfile") result = execute("vagrant", "init", "foo", "bar") result.should succeed contents = vagrantfile.read contents.should match(/config.vm.box = "foo"$/) contents.should match(/config.vm.box_url = "bar"$/) end end vagrant-1.4.3/test/acceptance_old/networking/000077500000000000000000000000001226132634600212605ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/networking/host_only_test.rb000066400000000000000000000015741226132634600246710ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "net/http" require "uri" require "acceptance/support/network_tests" require "acceptance/support/shared/command_examples" require "support/tempdir" describe "vagrant host only networking" do include Acceptance::NetworkTests include_context "acceptance" def initialize_environment(env=nil) require_box("default") env ||= environment env.execute("vagrant", "box", "add", "base", box_path("default")).should succeed end it "creates a network with a static IP" do initialize_environment environment.workdir.join("Vagrantfile").open("w+") do |f| f.puts(< Tempdir.new("vagrant").to_s } environment = new_environment(env_vars) environment2 = new_environment(env_vars) # For this test we create two isolated environments and `vagrant up` # in each. SSH would collide, so this verifies that it won't! begin initialize_environment(environment) initialize_environment(environment2) # Build both environments up. environment.execute("vagrant", "init").should succeed environment.execute("vagrant", "up").should succeed environment2.execute("vagrant", "init").should succeed environment2.execute("vagrant", "up").should succeed # Touch files in both environments environment.execute("vagrant", "ssh", "-c", "touch /vagrant/foo").should succeed environment2.execute("vagrant", "ssh", "-c", "touch /vagrant/bar").should succeed # Verify that the files exist in each folder, properly environment.workdir.join("foo").exist?.should be environment2.workdir.join("bar").exist?.should be ensure environment.close environment2.close end end it "refuses to resume if there is a port collision" do # The two environments need to share a VBOX_USER_HOME so that the # VM's go into the same place. env_vars = { "VBOX_USER_HOME" => Tempdir.new("vagrant").path } environment = new_environment(env_vars) environment2 = new_environment(env_vars) # For this test we `vagrant up` one environment, suspend it, # `vagrant up` another environment, and then come back to the first # and try to resume. SSH would collide so it should error. begin # Bring up the first environment and suspend it initialize_environment(environment) environment.execute("vagrant", "init").should succeed environment.execute("vagrant", "up").should succeed environment.execute("vagrant", "suspend").should succeed # Bring up the second environment initialize_environment(environment2) environment2.execute("vagrant", "init").should succeed environment2.execute("vagrant", "up").should succeed # Attempt to bring up the first environment again, but it should # result in an error. result = environment.execute("vagrant", "up") result.should_not succeed result.stderr.should match_output(:resume_port_collision) ensure environment.close environment2.close end end end vagrant-1.4.3/test/acceptance_old/package_test.rb000066400000000000000000000024611226132634600220530ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/shared/command_examples" describe "vagrant package" do include_context "acceptance" # This creates an initial environment that is ready for a "vagrant up" def initialize_valid_environment require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") end it "can package a running virtual machine" do initialize_valid_environment assert_execute("vagrant", "up") assert_execute("vagrant", "package") environment.workdir.join("package.box").should be_file end it "can package a `base` VM directly from VirtualBox" do initialize_valid_environment # Use a custom Vagrantfile that sets the VM name to `foo` environment.workdir.join("Vagrantfile").open("w+") do |f| f.write(<<-vf) Vagrant::Config.run do |config| config.vm.box = "base" config.vm.customize ["modifyvm", :id, "--name", "foo"] end vf end # Bring up the VM assert_execute("vagrant", "up") # Remove the Vagrantfile so it doesn't use that environment.workdir.join("Vagrantfile").unlink # Now package the base VM assert_execute("vagrant", "package", "--base", "foo") environment.workdir.join("package.box").should be_file end end vagrant-1.4.3/test/acceptance_old/provisioning/000077500000000000000000000000001226132634600216175ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/provisioning/basic_test.rb000066400000000000000000000036371226132634600242750ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe "vagrant provisioning basics" do include_context "acceptance" it "doesn't provision with `--no-provision` set" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) environment.workdir.join("Vagrantfile").open("w+") do |f| f.write(<<-vf) Vagrant::Config.run do |config| config.vm.box = "base" config.vm.provision :shell, :inline => "echo success > /vagrant/results" end vf end # Bring the VM up without enabling provisioning assert_execute("vagrant", "up", "--no-provision") # Verify the file that the script creates does NOT exist result_file = environment.workdir.join("results") result_file.exist?.should_not be end it "can provision with multiple provisioners" do # Skeleton for multiple provisioners environment.skeleton!("provisioner_multi") # Setup the basic environment require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) # Bring up the VM assert_execute("vagrant", "up") # Verify the file the shell provisioner made is there, but not the # Chef one. environment.workdir.join("shell").exist?.should be environment.workdir.join("chef_solo").exist?.should be end it "only uses the provisioners specified with `--provision-with`" do # Skeleton for multiple provisioners environment.skeleton!("provisioner_multi") # Setup the basic environment require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) # Bring up the VM, only using the shell provisioner assert_execute("vagrant", "up", "--provision-with", "shell") # Verify the file the shell provisioner made is there, but not the # Chef one. environment.workdir.join("shell").exist?.should be environment.workdir.join("chef_solo").exist?.should_not be end end vagrant-1.4.3/test/acceptance_old/provisioning/chef_solo_test.rb000066400000000000000000000021211226132634600251400ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe "vagrant provisioning with chef solo" do include_context "acceptance" it "runs basic cookbooks" do # Create the chef solo basic skeleton environment.skeleton!("chef_solo_basic") # Setup the basic environment require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) # Bring up the VM assert_execute("vagrant", "up") # Check for the file it should have created results = assert_execute("vagrant", "ssh", "-c", "cat /tmp/chef_solo_basic") results.stdout.should == "success" end it "merges JSON into the attributes" do # Copy the skeleton environment.skeleton!("chef_solo_json") # Setup the basic environment require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) # Bring up the VM assert_execute("vagrant", "up") # Check for the file it should have created results = assert_execute("vagrant", "ssh", "-c", "cat /tmp/chef_solo_basic") results.stdout.should == "json_data" end end vagrant-1.4.3/test/acceptance_old/provisioning/shell_test.rb000066400000000000000000000024361226132634600243170ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe "vagrant provisioning with shell" do include_context "acceptance" it "runs a script on boot" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) environment.workdir.join("Vagrantfile").open("w+") do |f| f.write(<<-vf) Vagrant::Config.run do |config| config.vm.box = "base" config.vm.provision :shell, :path => "script.sh" end vf end environment.workdir.join("script.sh").open("w+") do |f| f.write(<<-vf) echo success > /vagrant/results vf end assert_execute("vagrant", "up") result_file = environment.workdir.join("results") result_file.exist?.should be result_file.read.should == "success\n" end it "runs an inline script" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) environment.workdir.join("Vagrantfile").open("w+") do |f| f.write(<<-vf) Vagrant::Config.run do |config| config.vm.box = "base" config.vm.provision :shell, :inline => "echo success > /vagrant/results" end vf end assert_execute("vagrant", "up") result_file = environment.workdir.join("results") result_file.exist?.should be result_file.read.should == "success\n" end end vagrant-1.4.3/test/acceptance_old/resume_test.rb000066400000000000000000000010601226132634600217520ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/shared/command_examples" describe "vagrant resume" do include_context "acceptance" it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "resume"] it "succeeds and ignores if the VM is not created" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") result = assert_execute("vagrant", "resume") result.stdout.should match_output(:vm_not_created_warning) end end vagrant-1.4.3/test/acceptance_old/shared_folders_test.rb000066400000000000000000000045511226132634600234460ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/shared/command_examples" describe "vagrant shared folders" do include_context "acceptance" # This creates an initial environment that is ready for a "vagrant up" def initialize_valid_environment require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") end it "should have a '/vagrant' shared folder" do initialize_valid_environment # This is the file that will be created from the VM, # but should then exist on the host machine foofile = environment.workdir.join("foo") assert_execute("vagrant", "up") foofile.exist?.should_not be, "'foo' should not exist yet." assert_execute("vagrant", "ssh", "-c", "touch /vagrant/foo") foofile.exist?.should be, "'foo' should exist since it was touched in the shared folder" end it "should create a shared folder if the :create flag is set" do initialize_valid_environment # Setup the custom Vagrantfile environment.workdir.join("Vagrantfile").open("w+") do |f| f.write(<<-VF) Vagrant::Config.run do |config| config.vm.box = "base" config.vm.share_folder "v-root", "/vagrant", "./data", :create => true end VF end data_dir = environment.workdir.join("data") # Verify the directory doesn't exist prior, for sanity data_dir.exist?.should_not be # Bring up the VM assert_execute("vagrant", "up") # Verify the directory exists data_dir.should be_directory # Touch a file and verify it is shared assert_execute("vagrant", "ssh", "-c", "touch /vagrant/foo") data_dir.join("foo").exist?.should be end it "should properly shell expand relative directories on the guest" do initialize_valid_environment # Setup the custom Vagrantfile environment.workdir.join("Vagrantfile").open("w+") do |f| f.write(<<-VF) Vagrant::Config.run do |config| config.vm.box = "base" config.vm.share_folder "v-root", "~/data", "." end VF end # Sanity foo_file = environment.workdir.join("foo") foo_file.exist?.should_not be # Bring up the VM assert_execute("vagrant", "up") # Touch a file in the shared folder we expect and verify # it is shared. assert_execute("vagrant", "ssh", "-c", "touch ~/data/foo") foo_file.exist?.should be end end vagrant-1.4.3/test/acceptance_old/skeletons/000077500000000000000000000000001226132634600211005ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_basic/000077500000000000000000000000001226132634600242025ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_basic/README.md000066400000000000000000000001261226132634600254600ustar00rootroot00000000000000# Chef Solo Basic Skeleton This is a skeleton that contains a basic chef solo setup. vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_basic/cookbooks/000077500000000000000000000000001226132634600261735ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_basic/cookbooks/basic/000077500000000000000000000000001226132634600272545ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_basic/cookbooks/basic/recipes/000077500000000000000000000000001226132634600307065ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_basic/cookbooks/basic/recipes/default.rb000066400000000000000000000001331226132634600326540ustar00rootroot00000000000000# Just create a file file "/tmp/chef_solo_basic" do mode 0644 content "success" end vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_json/000077500000000000000000000000001226132634600240725ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_json/README.md000066400000000000000000000001261226132634600253500ustar00rootroot00000000000000# Chef Solo Basic Skeleton This is a skeleton that contains a basic chef solo setup. vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_json/cookbooks/000077500000000000000000000000001226132634600260635ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_json/cookbooks/basic/000077500000000000000000000000001226132634600271445ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_json/cookbooks/basic/recipes/000077500000000000000000000000001226132634600305765ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/chef_solo_json/cookbooks/basic/recipes/default.rb000066400000000000000000000002351226132634600325470ustar00rootroot00000000000000# Create a file where the contents is the data we set with # the Vagrantfile. file "/tmp/chef_solo_basic" do mode 0644 content node[:test][:data] end vagrant-1.4.3/test/acceptance_old/skeletons/provisioner_multi/000077500000000000000000000000001226132634600246715ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/provisioner_multi/README.md000066400000000000000000000001261226132634600261470ustar00rootroot00000000000000# Chef Solo Basic Skeleton This is a skeleton that contains a basic chef solo setup. vagrant-1.4.3/test/acceptance_old/skeletons/provisioner_multi/cookbooks/000077500000000000000000000000001226132634600266625ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/provisioner_multi/cookbooks/basic/000077500000000000000000000000001226132634600277435ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/provisioner_multi/cookbooks/basic/recipes/000077500000000000000000000000001226132634600313755ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/skeletons/provisioner_multi/cookbooks/basic/recipes/default.rb000066400000000000000000000001311226132634600333410ustar00rootroot00000000000000# Just create a file file "/vagrant/chef_solo" do mode 0644 content "success" end vagrant-1.4.3/test/acceptance_old/ssh_test.rb000066400000000000000000000027101226132634600212520ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/shared/command_examples" describe "vagrant ssh" do include_context "acceptance" it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "ssh"] it_behaves_like "a command that requires a virtual machine", ["vagrant", "ssh"] it "is able to SSH into a running virtual machine" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") assert_execute("vagrant", "up") outputted = false result = assert_execute("vagrant", "ssh") do |io_type, data| if io_type == :stdin and !outputted data.puts("echo hello") data.puts("exit") outputted = true end end result.stdout.chomp.should eql("hello"), "Vagrant should bring up a VM to be able to SSH into." end it "is able to execute a single command via the command line" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") assert_execute("vagrant", "up") result = execute("vagrant", "ssh", "-c", "echo foo") result.exit_code.should == 0 result.stdout.should == "foo\n" result = execute("vagrant", "ssh", "-c", "foooooooooo") result.exit_code.should == 127 result.stderr.should =~ /foooooooooo: command not found/ end # TODO: # SSH should fail if the VM is not running end vagrant-1.4.3/test/acceptance_old/support/000077500000000000000000000000001226132634600206055ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/support/config.rb000066400000000000000000000026311226132634600224010ustar00rootroot00000000000000require "yaml" require "log4r" module Acceptance # This represents a configuration object for acceptance tests. class Config attr_reader :vagrant_path attr_reader :vagrant_version attr_reader :env attr_reader :box_directory def initialize(path) @logger = Log4r::Logger.new("test::acceptance::config") @logger.info("Loading configuration from: #{path}") options = YAML.load_file(path) @logger.info("Loaded: #{options.inspect}") @vagrant_path = options["vagrant_path"] @vagrant_version = options["vagrant_version"] @env = options["env"] @box_directory = options["box_directory"] # Verify the configuration object. validate end # This method verifies the configuration and makes sure that # all the configuration is available and appears good. This # method will raise an ArgumentError in the case that anything # is wrong. def validate if !@vagrant_path || !File.file?(@vagrant_path) raise ArgumentError, "'vagrant_path' must point to the `vagrant` executable" elsif !@vagrant_version raise ArgumentError, "`vagrant_version' must be set to the version of the `vagrant` executable" elsif !@box_directory || !File.directory?(@box_directory) raise ArgumentError, "`box_directory` must be set to a folder containing boxes for the tests." end end end end vagrant-1.4.3/test/acceptance_old/support/isolated_environment.rb000066400000000000000000000100201226132634600253530ustar00rootroot00000000000000require "fileutils" require "pathname" require "log4r" require "childprocess" require "vagrant/util/subprocess" require "acceptance/support/virtualbox" require "support/isolated_environment" module Acceptance # This class manages an isolated environment for Vagrant to # run in. It creates a temporary directory to act as the # working directory as well as sets a custom home directory. class IsolatedEnvironment < ::IsolatedEnvironment SKELETON_DIR = Pathname.new(File.expand_path("../../skeletons", __FILE__)) def initialize(apps=nil, env=nil) super() @logger = Log4r::Logger.new("test::acceptance::isolated_environment") @apps = apps.clone || {} @env = env.clone || {} # Set the home directory and virtualbox home directory environmental # variables so that Vagrant and VirtualBox see the proper paths here. @env["HOME"] ||= @homedir.to_s @env["VBOX_USER_HOME"] ||= @homedir.to_s end # Copies a skeleton into this isolated environment. This is useful # for testing environments that require a complex setup. # # @param [String] name Name of the skeleton in the skeletons/ directory. def skeleton!(name) # Copy all the files into the home directory source = Dir.glob(SKELETON_DIR.join(name).join("*").to_s) FileUtils.cp_r(source, @workdir.to_s) end # Executes a command in the context of this isolated environment. # Any command executed will therefore see our temporary directory # as the home directory. def execute(command, *argN) # Create the command command = replace_command(command) # Determine the options options = argN.last.is_a?(Hash) ? argN.pop : {} options = { :workdir => @workdir, :env => @env, :notify => [:stdin, :stderr, :stdout] }.merge(options) # Add the options to be passed on argN << options # Execute, logging out the stdout/stderr as we get it @logger.info("Executing: #{[command].concat(argN).inspect}") Vagrant::Util::Subprocess.execute(command *argN) do |type, data| @logger.debug("#{type}: #{data}") if type == :stdout || type == :stderr yield type, data if block_given? end end # Closes the environment, cleans up the temporary directories, etc. def close # Only delete virtual machines if VBoxSVC is running, meaning # that something related to VirtualBox started running in this # environment. delete_virtual_machines if VirtualBox.find_vboxsvc # Let the parent handle cleaning up super end def delete_virtual_machines # Delete all virtual machines @logger.debug("Finding all virtual machines") execute("VBoxManage", "list", "vms").stdout.lines.each do |line| data = /^"(?.+?)" {(?.+?)}$/.match(line) begin @logger.debug("Removing VM: #{data[:name]}") # We add a timeout onto this because sometimes for seemingly no # reason it will simply freeze, although the VM is successfully # "aborted." The timeout gets around this strange behavior. execute("VBoxManage", "controlvm", data[:uuid], "poweroff", :timeout => 5) rescue Vagrant::Util::Subprocess::TimeoutExceeded => e @logger.info("Failed to poweroff VM '#{data[:uuid]}'. Killing process.") # Kill the process and wait a bit for it to disappear Process.kill('KILL', e.pid) Process.waitpid2(e.pid) end sleep 0.5 result = execute("VBoxManage", "unregistervm", data[:uuid], "--delete") raise Exception, "VM unregistration failed!" if result.exit_code != 0 end @logger.info("Removed all virtual machines") end # This replaces a command with a replacement defined when this # isolated environment was initialized. If nothing was defined, # then the command itself is returned. def replace_command(command) return @apps[command] if @apps.has_key?(command) return command end end end vagrant-1.4.3/test/acceptance_old/support/matchers/000077500000000000000000000000001226132634600224135ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/support/matchers/have_color.rb000066400000000000000000000003021226132634600250540ustar00rootroot00000000000000RSpec::Matchers.define :have_color do match do |actual| actual.index("\e[31m") end failure_message_for_should do |actual| "expected output to contain color, but didn't" end end vagrant-1.4.3/test/acceptance_old/support/matchers/match_output.rb000066400000000000000000000007311226132634600254550ustar00rootroot00000000000000require "acceptance/support/output" # This creates a matcher that is used to match against certain # Vagrant output. Vagrant output is not what is being tested, # so all that state is hidden away in Acceptance::Output. RSpec::Matchers.define :match_output do |expected, *args| match do |actual| Acceptance::Output.new(actual).send(expected, *args) end failure_message_for_should do |actual| "expected output to match: #{expected} #{args.inspect}" end end vagrant-1.4.3/test/acceptance_old/support/matchers/succeed.rb000066400000000000000000000005661226132634600243620ustar00rootroot00000000000000# Matcher that verifies that a process succeeds. RSpec::Matchers.define :succeed do match do |thing| thing.exit_code.should == 0 end failure_message_for_should do |actual| "expected process to succeed. exit code: #{actual.exit_code}" end failure_message_for_should_not do |actual| "expected process to fail. exit code: #{actual.exit_code}" end end vagrant-1.4.3/test/acceptance_old/support/network_tests.rb000066400000000000000000000017641226132634600240550ustar00rootroot00000000000000require "vagrant/util/retryable" module Acceptance module NetworkTests include Vagrant::Util::Retryable # Tests that the host can access the VM through the network. # # @param [String] url URL to request from the host. # @param [Integer] guest_port Port to run a web server on the guest. def assert_host_to_vm_network(url, guest_port) # Start up a web server in another thread by SSHing into the VM. thr = Thread.new do assert_execute("vagrant", "ssh", "-c", "python -m SimpleHTTPServer #{guest_port}") end # Verify that port forwarding works by making a simple HTTP request # to the port. We should get a 200 response. We retry this a few times # as we wait for the HTTP server to come online. retryable(:tries => 5, :sleep => 2) do result = Net::HTTP.get_response(URI.parse(url)) result.code.should == "200" end ensure # The server needs to die. This is how. thr.kill if thr end end end vagrant-1.4.3/test/acceptance_old/support/output.rb000066400000000000000000000053511226132634600224760ustar00rootroot00000000000000module Acceptance # This class helps with matching against output so that every # test isn't inherently tied to the output format of Vagrant. class Output DEFAULT_VM = "default" def initialize(text) @text = text end def box_already_exists(name) @text =~ /^A box already exists under the name of '#{name}'/ end # Checks that an error message was outputted about the box # being added being invalid. def box_invalid @text =~ /^The box file you're attempting to add is invalid./ end # Checks that an error message was outputted about the path # not existing to the box. def box_path_doesnt_exist @text =~ /^The specified path to a file doesn't exist.$/ end # Tests that the box with given name is installed. def box_installed(name) @text =~ /^#{name}$/ end # Tests that the output says there are no installed boxes. def no_boxes @text =~ /There are no installed boxes!/ end # Tests that the output says there is no Vagrantfile, and as such # can't do whatever we requested Vagrant to do. def no_vagrantfile @text =~ /^A Vagrant environment is required/ end # Tests that the output contains a specific Vagrant version. def version(version) @text =~ /^Vagrant version #{version}$/ end def resume_port_collision @text =~ /^This VM cannot be resumed, because the forwarded ports/ end # This checks that the VM with the given `vm_name` has the # status of `status`. def status(vm_name, status) @text =~ /^#{vm_name}\s+#{status}$/ end # This checks that an error message that the VM must be created # is shown. def error_vm_must_be_created @text =~ /^VM must be created/ end # This checks that the warning that the VM is not created is emitted. def vm_not_created_warning @text =~ /VM not created. Moving on...$/ end # This checks that the VM is destroyed. def vm_destroyed @text =~ /Destroying VM and associated drives...$/ end # This checks that the "up" output properly contains text showing that # it is downloading the box during the up process. def up_fetching_box(name, vm=DEFAULT_VM) @text =~ /^\[#{vm}\] Box #{name} was not found. Fetching box from specified URL...$/ end # Check that the output shows that the VM was shut down gracefully def vm_halt_graceful @text =~ /Attempting graceful shutdown of/ end # Output shows a forceful VM shutdown. def vm_halt_force @text =~ /Forcing shutdown of VM...$/ end # Output shows the VM is in the process of suspending def vm_suspending @text =~ /Saving VM state and suspending execution...$/ end end end vagrant-1.4.3/test/acceptance_old/support/shared/000077500000000000000000000000001226132634600220535ustar00rootroot00000000000000vagrant-1.4.3/test/acceptance_old/support/shared/base_context.rb000066400000000000000000000045101226132634600250560ustar00rootroot00000000000000require "acceptance/support/isolated_environment" require "acceptance/support/output" require "acceptance/support/virtualbox" shared_context "acceptance" do # Setup variables for the loggers of this test. These can be used to # create more verbose logs for tests which can be useful in the case # that a test fails. let(:logger_name) { "logger" } let(:logger) { Log4r::Logger.new("test::acceptance::#{logger_name}") } # This is the global configuration given by the acceptance test # configurations. let(:config) { $acceptance_options } # Setup the environment so that we have an isolated area # to run Vagrant. We do some configuration here as well in order # to replace "vagrant" with the proper path to Vagrant as well # as tell the isolated environment about custom environmental # variables to pass in. let!(:environment) { new_environment } before(:each) do # Wait for VBoxSVC to disappear, since each test requires its # own isolated VirtualBox process. Acceptance::VirtualBox.wait_for_vboxsvc end after(:each) do environment.close end # Creates a new isolated environment instance each time it is called. # # @return [Acceptance::IsolatedEnvironment] def new_environment(env=nil) apps = { "vagrant" => config.vagrant_path } env = config.env.merge(env || {}) Acceptance::IsolatedEnvironment.new(apps, env) end # Executes the given command in the context of the isolated environment. # # @return [Object] def execute(*args, &block) environment.execute(*args, &block) end # This method is an assertion helper for asserting that a process # succeeds. It is a wrapper around `execute` that asserts that the # exit status was successful. def assert_execute(*args, &block) result = execute(*args, &block) assert(result.exit_code == 0, "expected '#{args.join(" ")}' to succeed") result end # This can be added to the beginning of a test to verify that the # box with the given name is available to a test. This will raise # an exception if the box is not found. def require_box(name) if !File.exist?(box_path(name)) raise ArgumentError, "The tests should have a '#{name}' box." end end # This is used to get the path to a box of a specific name. def box_path(name) File.join(config.box_directory, "#{name}.box") end end vagrant-1.4.3/test/acceptance_old/support/shared/command_examples.rb000066400000000000000000000021411226132634600257120ustar00rootroot00000000000000# This is a shared example that tests that a command requires a # Vagrant environment to run properly. The exact command to run # should be given as a parameter to the shared examples. shared_examples "a command that requires a Vagrantfile" do |*args| let(:command) do raise ArgumentError, "A command must be set for the shared example." if args.empty? args[0] end it "fails if no Vagrantfile is found" do result = execute(*command) result.should_not succeed result.stderr.should match_output(:no_vagrantfile) end end # This is a shared example that tests that the command requires a # virtual machine to be created, and additionally to be in one of # many states. shared_examples "a command that requires a virtual machine" do |*args| let(:command) do raise ArgumentError, "A command must be set for the shared example." if args.empty? args[0] end it "fails if the virtual machine is not created" do assert_execute("vagrant", "init") result = execute(*command) result.should_not succeed result.stderr.should match_output(:error_vm_must_be_created) end end vagrant-1.4.3/test/acceptance_old/support/virtualbox.rb000066400000000000000000000017011226132634600233300ustar00rootroot00000000000000require 'sys/proctable' module Acceptance module VirtualBox extend self # This method will wait for the "VBoxSVC" process to end. This will # block during that time period. The reason for this is because only # one "VBoxSVC" can run per user and manages all state within VirtualBox. # Before you can run VirtualBox with a custom home directory, you must # wait for this VBoxSVC process to die. def wait_for_vboxsvc time_passed = 0 while find_vboxsvc if time_passed > 5 raise Exception, "VBoxSVC process is not going away." end sleep 1 time_passed += 1 end end # This method finds the VBoxSVC process and returns information about it. # This will return "nil" if VBoxSVC is not found. def find_vboxsvc Sys::ProcTable.ps do |process| if process.comm == "VBoxSVC" return process end end nil end end end vagrant-1.4.3/test/acceptance_old/suspend_test.rb000066400000000000000000000037141226132634600221430ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/shared/command_examples" describe "vagrant suspend" do include_context "acceptance" it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "suspend"] it "succeeds and ignores if the VM is not created" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") result = assert_execute("vagrant", "suspend") result.stdout.should match_output(:vm_not_created_warning) end it "is able to suspend a running virtual machine" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") assert_execute("vagrant", "up") # Suspend the VM and assert that it worked properly (seemingly) result = assert_execute("vagrant", "suspend") result.stdout.should match_output(:vm_suspending) # Assert that the VM is no longer running result = assert_execute("vagrant", "status") result.stdout.should match_output(:status, "default", "saved") end # These tests are parameterized since both "vagrant resume" and # "vagrant up" should achieve the same result. ["resume", "up"].each do |command| it "is able to resume after the machine has been suspended using #{command}" do require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") assert_execute("vagrant", "up") assert_execute("vagrant", "suspend") # Assert that the VM is no longer running result = assert_execute("vagrant", "status") result.stdout.should match_output(:status, "default", "saved") assert_execute("vagrant", command) # Assert that the VM is once again running result = assert_execute("vagrant", "status") result.stdout.should match_output(:status, "default", "running") end end end vagrant-1.4.3/test/acceptance_old/up_basic_test.rb000066400000000000000000000021071226132634600222420ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/shared/command_examples" describe "vagrant up", "basics" do include_context "acceptance" it_behaves_like "a command that requires a Vagrantfile", ["vagrant", "up"] # This creates an initial environment that is ready for a "vagrant up" def initialize_valid_environment require_box("default") assert_execute("vagrant", "box", "add", "base", box_path("default")) assert_execute("vagrant", "init") end it "brings up a running virtual machine" do initialize_valid_environment assert_execute("vagrant", "up") result = assert_execute("vagrant", "status") result.stdout.should match_output(:status, "default", "running") end it "is able to run if Vagrantfile is in a parent directory" do initialize_valid_environment # Create a subdirectory in the working directory and use # that as the CWD for `vagrant up` and verify it still works foodir = environment.workdir.join("foo") foodir.mkdir assert_execute("vagrant", "up", :chdir => foodir.to_s) end end vagrant-1.4.3/test/acceptance_old/up_with_box_url.rb000066400000000000000000000024351226132634600226330ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/shared/command_examples" describe "vagrant up", "with a box URL set" do include_context "acceptance" it "downloads and brings up the VM if the box doesn't exist" do require_box("default") assert_execute("vagrant", "init", "base", box_path("default")) result = assert_execute("vagrant", "up") result.stdout.should match_output(:up_fetching_box, "base") end it "downloads the file only once and works if shared by multiple VMs", :issue => "GH-564" do require_box("default") environment.workdir.join("Vagrantfile").open("w+") do |f| f.puts(<<-VFILE) Vagrant::Config.run do |config| config.vm.define :machine1 do |vm_config| vm_config.vm.box = "base" vm_config.vm.box_url = "#{box_path("default")}" end config.vm.define :machine2 do |vm_config| vm_config.vm.box = "base" vm_config.vm.box_url = "#{box_path("default")}" end end VFILE end # Bring up the environment, which should work. `machine1` should download # the box while `machine2` doesn't. result = assert_execute("vagrant", "up") result.stdout.should match_output(:up_fetching_box, "base", "machine1") result.stdout.should_not match_output(:up_fetching_box, "base", "machine2") end end vagrant-1.4.3/test/acceptance_old/vagrant_test.rb000066400000000000000000000027301226132634600221210ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) require "acceptance/support/matchers/have_color" describe "vagrant and color output" do include_context "acceptance" # This is a check to see if the `expect` program is installed on this # computer. Some tests require this and if this doesn't exist then the # test itself will be skipped. def self.has_expect? `which expect` $?.success? end it "outputs color if there is a TTY", :if => has_expect? do environment.workdir.join("color.exp").open("w+") do |f| f.puts(<<-SCRIPT) spawn #{environment.replace_command("vagrant")} status expect default {} SCRIPT end result = execute("expect", "color.exp") result.stdout.should have_color end it "doesn't output color if there is a TTY but --no-color is present", :if => has_expect? do environment.workdir.join("color.exp").open("w+") do |f| f.puts(<<-SCRIPT) spawn #{environment.replace_command("vagrant")} status --no-color expect default {} SCRIPT end result = execute("expect", "color.exp") result.stdout.should_not have_color end it "doesn't output color in the absense of a TTY" do # This should always output an error, which on a TTY would # output color. We check that this doesn't output color. # If `vagrant status` itself is broken, another acceptance test # should catch that. We just assume it works here. result = execute("vagrant", "status") result.stdout.should_not have_color end end vagrant-1.4.3/test/acceptance_old/version_test.rb000066400000000000000000000007161226132634600221460ustar00rootroot00000000000000require File.expand_path("../base", __FILE__) describe "vagrant version" do include_context "acceptance" it "prints the version when called with '-v'" do result = execute("vagrant", "-v") result.stdout.should match_output(:version, config.vagrant_version) end it "prints the version when called with '--version'" do result = execute("vagrant", "--version") result.stdout.should match_output(:version, config.vagrant_version) end end vagrant-1.4.3/test/config/000077500000000000000000000000001226132634600154125ustar00rootroot00000000000000vagrant-1.4.3/test/config/acceptance_boxes.yml000066400000000000000000000005571226132634600214320ustar00rootroot00000000000000# This is the list of required boxes for acceptance tests, and # their locations. The locations are used to download the boxes # on the fly, if necessary. Additionally, a SHA1 checksum is # given to determine if the box needs to be updated. - name: default url: http://files.vagrantup.com/test/boxes/default.box checksum: 1b0a7eb6e152c5b43f45485654ff4965a7f9f604 vagrant-1.4.3/test/support/000077500000000000000000000000001226132634600156615ustar00rootroot00000000000000vagrant-1.4.3/test/support/isolated_environment.rb000066400000000000000000000027001226132634600224350ustar00rootroot00000000000000require "fileutils" require "pathname" require "log4r" require "support/tempdir" # This class manages an isolated environment for Vagrant to # run in. It creates a temporary directory to act as the # working directory as well as sets a custom home directory. # # This class also provides various helpers to create Vagrantfiles, # boxes, etc. class IsolatedEnvironment attr_reader :homedir attr_reader :workdir # Initializes an isolated environment. You can pass in some # options here to configure runing custom applications in place # of others as well as specifying environmental variables. # # @param [Hash] apps A mapping of application name (such as "vagrant") # to an alternate full path to the binary to run. # @param [Hash] env Additional environmental variables to inject # into the execution environments. def initialize @logger = Log4r::Logger.new("test::isolated_environment") # Create a temporary directory for our work @tempdir = Tempdir.new("vagrant") @logger.info("Initialize isolated environment: #{@tempdir.path}") # Setup the home and working directories @homedir = Pathname.new(File.join(@tempdir.path, "home")) @workdir = Pathname.new(File.join(@tempdir.path, "work")) @homedir.mkdir @workdir.mkdir end # This closes the environment by cleaning it up. def close @logger.info("Removing isolated environment: #{@tempdir.path}") FileUtils.rm_rf(@tempdir.path) end end vagrant-1.4.3/test/support/tempdir.rb000066400000000000000000000017471226132634600176630ustar00rootroot00000000000000require 'fileutils' require 'tempfile' # This class provides an easy way of creating a temporary # directory and having it removed when the application exits. class Tempdir attr_reader :path def initialize(basename="vagrant") @path = nil # Loop and attempt to create a temporary directory until # it succeeds. while @path.nil? file = Tempfile.new(basename) @path = file.path file.unlink begin Dir.mkdir(@path) rescue @path = nil end end # Setup a finalizer to delete the directory. This is the same way # that Tempfile and friends do this... @cleanup_proc = lambda do FileUtils.rm_rf(@path) if File.directory?(@path) end ObjectSpace.define_finalizer(self, @cleanup_proc) end # This deletes the temporary directory. def unlink # Delete the directory @cleanup_proc.call # Undefine the finalizer since we're all cleaned up ObjectSpace.undefine_finalizer(self) end end vagrant-1.4.3/test/unit/000077500000000000000000000000001226132634600151245ustar00rootroot00000000000000vagrant-1.4.3/test/unit/base.rb000066400000000000000000000014571226132634600163720ustar00rootroot00000000000000require "rubygems" require "rspec/autorun" # Require Vagrant itself so we can reference the proper # classes to test. require "vagrant" # Add the test directory to the load path $:.unshift File.expand_path("../../", __FILE__) # Load in helpers require "support/tempdir" require "unit/support/dummy_communicator" require "unit/support/dummy_provider" require "unit/support/shared/base_context" require "unit/support/shared/action_synced_folders_context" require "unit/support/shared/virtualbox_context" # Do not buffer output $stdout.sync = true $stderr.sync = true # Configure RSpec RSpec.configure do |c| c.expect_with :rspec, :stdlib end # Configure VAGRANT_CWD so that the tests never find an actual # Vagrantfile anywhere, or at least this minimizes those chances. ENV["VAGRANT_CWD"] = Tempdir.new.path vagrant-1.4.3/test/unit/plugins/000077500000000000000000000000001226132634600166055ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/commands/000077500000000000000000000000001226132634600204065ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/commands/plugin/000077500000000000000000000000001226132634600217045ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/commands/plugin/state_file_test.rb000066400000000000000000000037271226132634600254200ustar00rootroot00000000000000require "json" require "pathname" require File.expand_path("../../../../base", __FILE__) describe VagrantPlugins::CommandPlugin::StateFile do let(:path) do f = Tempfile.new("vagrant") p = f.path f.close f.unlink Pathname.new(p) end after do path.unlink if path.file? end subject { described_class.new(path) } context "new usage" do it "should have no plugins without saving some" do expect(subject.installed_plugins).to be_empty end it "should have plugins when saving" do subject.add_plugin("foo") instance = described_class.new(path) plugins = instance.installed_plugins expect(plugins.length).to eql(1) expect(plugins["foo"]).to eql({ "ruby_version" => RUBY_VERSION, "vagrant_version" => Vagrant::VERSION, }) end it "should remove plugins" do subject.add_plugin("foo") subject.remove_plugin("foo") instance = described_class.new(path) expect(instance.installed_plugins).to be_empty end it "should store plugins uniquely" do subject.add_plugin("foo") subject.add_plugin("foo") instance = described_class.new(path) expect(instance.installed_plugins.keys).to eql(["foo"]) end end context "with an old-style file" do before do data = { "installed" => ["foo"], } path.open("w+") do |f| f.write(JSON.dump(data)) end end it "should have the right installed plugins" do plugins = subject.installed_plugins expect(plugins.keys).to eql(["foo"]) expect(plugins["foo"]["ruby_version"]).to eql("0") expect(plugins["foo"]["vagrant_version"]).to eql("0") end end context "with parse errors" do before do path.open("w+") do |f| f.write("I'm not json") end end it "should raise a VagrantError" do expect { subject }. to raise_error(Vagrant::Errors::PluginStateFileParseError) end end end vagrant-1.4.3/test/unit/plugins/commands/ssh_config/000077500000000000000000000000001226132634600225305ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/commands/ssh_config/command_test.rb000066400000000000000000000045651226132634600255440ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe "VagrantPlugins::CommandSSHConfig::Command" do include_context "unit" include_context "virtualbox" let(:described_class) { Vagrant.plugin("2").manager.commands[:"ssh-config"] } let(:argv) { [] } let(:env) { Vagrant::Environment.new } let(:machine) { double("Vagrant::Machine", :name => nil) } let(:ssh_info) {{ :host => "testhost.vagrant.dev", :port => 1234, :username => "testuser", :private_key_path => [], :forward_agent => false, :forward_x11 => false }} subject { described_class.new(argv, env) } before do subject.stub(:with_target_vms) { |&block| block.call machine } end describe "execute" do it "prints out the ssh config for the given machine" do machine.stub(:ssh_info) { ssh_info } subject.should_receive(:safe_puts).with(<<-SSHCONFIG) Host vagrant HostName testhost.vagrant.dev User testuser Port 1234 UserKnownHostsFile /dev/null StrictHostKeyChecking no PasswordAuthentication no IdentitiesOnly yes LogLevel FATAL SSHCONFIG subject.execute end it "turns on agent forwarding when it is configured" do machine.stub(:ssh_info) { ssh_info.merge(:forward_agent => true) } subject.should_receive(:safe_puts).with { |ssh_config| ssh_config.should include("ForwardAgent yes") } subject.execute end it "turns on x11 forwarding when it is configured" do machine.stub(:ssh_info) { ssh_info.merge(:forward_x11 => true) } subject.should_receive(:safe_puts).with { |ssh_config| ssh_config.should include("ForwardX11 yes") } subject.execute end it "handles multiple private key paths" do machine.stub(:ssh_info) { ssh_info.merge(:private_key_path => ["foo", "bar"]) } subject.should_receive(:safe_puts).with { |ssh_config| ssh_config.should include("IdentityFile foo") ssh_config.should include("IdentityFile bar") } subject.execute end it "puts quotes around an identityfile path if it has a space" do machine.stub(:ssh_info) { ssh_info.merge(:private_key_path => ["with a space"]) } subject.should_receive(:safe_puts).with { |ssh_config| ssh_config.should include('IdentityFile "with a space"') } subject.execute end end end vagrant-1.4.3/test/unit/plugins/guests/000077500000000000000000000000001226132634600201175ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/guests/debian/000077500000000000000000000000001226132634600213415ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/guests/debian/cap/000077500000000000000000000000001226132634600221045ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/guests/debian/cap/change_host_name_test.rb000066400000000000000000000023501226132634600267520ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require File.expand_path("../../../support/shared/debian_like_host_name_examples", __FILE__) describe "VagrantPlugins::GuestDebian::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestDebian::Plugin.components.guest_capabilities[:debian].get(:change_host_name) end let(:machine) { double("machine") } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:old_hostname) { 'oldhostname.olddomain.tld' } before do machine.stub(:communicate).and_return(communicator) communicator.stub_command('hostname -f', stdout: old_hostname) end after do communicator.verify_expectations! end describe ".change_host_name" do it_behaves_like "a debian-like host name change" it "refreshes the hostname service with the hostname command" do communicator.expect_command(%q(hostname -F /etc/hostname)) described_class.change_host_name(machine, 'newhostname.newdomain.tld') end it "renews dhcp on the system with the new hostname" do communicator.expect_command(%q(ifdown -a; ifup -a; ifup eth0)) described_class.change_host_name(machine, 'newhostname.newdomain.tld') end end end vagrant-1.4.3/test/unit/plugins/guests/redhat/000077500000000000000000000000001226132634600213665ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/guests/redhat/cap/000077500000000000000000000000001226132634600221315ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/guests/redhat/cap/change_host_name_test.rb000066400000000000000000000045271226132634600270070ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require File.expand_path("../../../support/shared/redhat_like_host_name_examples", __FILE__) describe "VagrantPlugins::GuestRedHat::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestRedHat::Plugin.components.guest_capabilities[:redhat].get(:change_host_name) end let(:machine) { double("machine") } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } before do machine.stub(:communicate).and_return(communicator) communicator.stub_command('hostname -f', stdout: old_hostname) communicator.expect_command('hostname -f') end after do communicator.verify_expectations! end context 'when oldhostname is qualified' do let(:old_hostname) { 'oldhostname.olddomain.tld' } let(:similar_hostname) {'oldhostname'} it_behaves_like 'a full redhat-like host name change' include_examples 'inserting hostname in /etc/hosts' include_examples 'swapping simple hostname in /etc/hosts' include_examples 'swapping qualified hostname in /etc/hosts' end context 'when oldhostname is simple' do let(:old_hostname) { 'oldhostname' } let(:similar_hostname) {'oldhostname.olddomain.tld'} it_behaves_like 'a full redhat-like host name change' include_examples 'inserting hostname in /etc/hosts' include_examples 'swapping simple hostname in /etc/hosts' context 'and is only able to be determined by hostname (without -f)' do before do communicator.stub_command('hostname -f',nil) communicator.stub_command('hostname', stdout: old_hostname) communicator.expect_command('hostname') end it_behaves_like 'a full redhat-like host name change' include_examples 'inserting hostname in /etc/hosts' include_examples 'swapping simple hostname in /etc/hosts' end end context 'when the short version of hostname is localhost' do let(:old_hostname) { 'localhost.olddomain.tld' } it_behaves_like 'a partial redhat-like host name change' include_examples 'inserting hostname in /etc/hosts' it "does more even when the provided hostname is not different" do described_class.change_host_name(machine, old_hostname) communicator.received_commands.to_set.should_not == communicator.expected_commands.keys.to_set end end end vagrant-1.4.3/test/unit/plugins/guests/support/000077500000000000000000000000001226132634600216335ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/guests/support/shared/000077500000000000000000000000001226132634600231015ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/guests/support/shared/debian_like_host_name_examples.rb000066400000000000000000000062421226132634600316130ustar00rootroot00000000000000shared_examples "a debian-like host name change" do it "updates /etc/hostname on the machine" do communicator.expect_command(%q(echo 'newhostname' > /etc/hostname)) described_class.change_host_name(machine, 'newhostname.newdomain.tld') end it "updates mailname to prevent problems with the default mailer" do communicator.expect_command(%q(hostname --fqdn > /etc/mailname)) described_class.change_host_name(machine, 'newhostname.newdomain.tld') end it "does nothing when the provided hostname is not different" do described_class.change_host_name(machine, 'oldhostname.olddomain.tld') communicator.received_commands.should == ['hostname -f'] end describe "flipping out the old hostname in /etc/hosts" do let(:sed_command) do # Here we run the change_host_name through and extract the recorded sed # command from the dummy communicator described_class.change_host_name(machine, 'newhostname.newdomain.tld') communicator.received_commands.find { |cmd| cmd =~ /^sed/ } end # Now we extract the regexp from that sed command so we can do some # verification on it let(:expression) { sed_command.sub(%r{^sed -ri '\(.*\)' /etc/hosts$}, "\1") } let(:search) { Regexp.new(expression.split('@')[1], Regexp::EXTENDED) } let(:replace) { expression.split('@')[2] } it "works on an simple /etc/hosts file" do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 localhost 127.0.1.1 oldhostname.olddomain.tld oldhostname ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 localhost 127.0.1.1 newhostname.newdomain.tld newhostname RESULT end it "does not modify lines which contain similar hostnames" do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 localhost 127.0.1.1 oldhostname.olddomain.tld oldhostname # common prefix, but different fqdn 192.168.12.34 oldhostname.olddomain.tld.different # different characters at the dot 192.168.34.56 oldhostname-olddomain.tld ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 localhost 127.0.1.1 newhostname.newdomain.tld newhostname # common prefix, but different fqdn 192.168.12.34 oldhostname.olddomain.tld.different # different characters at the dot 192.168.34.56 oldhostname-olddomain.tld RESULT end context "when the old fqdn has a trailing dot" do let(:old_hostname) { 'oldhostname.withtrailing.dot.' } it "modifies /etc/hosts properly" do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 localhost 127.0.1.1 oldhostname.withtrailing.dot. oldhostname ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 localhost 127.0.1.1 newhostname.newdomain.tld newhostname RESULT end end end end vagrant-1.4.3/test/unit/plugins/guests/support/shared/redhat_like_host_name_examples.rb000066400000000000000000000215021226132634600316340ustar00rootroot00000000000000shared_examples 'a partial redhat-like host name change' do shared_examples 'shared between newhostname styles' do it 'updates hostname on the machine with the new short hostname' do communicator.expect_command(%q(hostname newhostname)) described_class.change_host_name(machine, new_hostname) end it 'sets dhcp_hostname with the provided short hostname' do communicator.expect_command(%q(sed -i 's/\\(DHCP_HOSTNAME=\\).*/\\1"newhostname"/' /etc/sysconfig/network-scripts/ifcfg-*)) described_class.change_host_name(machine, new_hostname) end it 'restarts networking' do communicator.expect_command(%q(service network restart)) described_class.change_host_name(machine, new_hostname) end end context 'when newhostname is qualified' do let(:new_hostname) {'newhostname.newdomain.tld'} include_examples 'shared between newhostname styles' it 'updates sysconfig with the provided full hostname' do communicator.expect_command(%q(sed -i 's/\\(HOSTNAME=\\).*/\\1newhostname.newdomain.tld/' /etc/sysconfig/network)) described_class.change_host_name(machine, new_hostname) end end context 'when newhostname is simple' do let(:new_hostname) {'newhostname'} include_examples 'shared between newhostname styles' it 'updates sysconfig with as much hostname as is available' do communicator.expect_command(%q(sed -i 's/\\(HOSTNAME=\\).*/\\1newhostname/' /etc/sysconfig/network)) described_class.change_host_name(machine, new_hostname) end end end shared_examples 'a full redhat-like host name change' do include_examples 'a partial redhat-like host name change' it "does nothing when the provided hostname is not different" do described_class.change_host_name(machine, old_hostname) communicator.received_commands.to_set.should == communicator.expected_commands.keys.to_set end it "does more when the provided hostname is a similar version" do described_class.change_host_name(machine, similar_hostname) communicator.received_commands.to_set.should_not == communicator.expected_commands.keys.to_set end end shared_examples 'mutating /etc/hosts helpers' do let(:sed_command) do # Here we run the change_host_name through and extract the recorded sed # command from the dummy communicator described_class.change_host_name(machine, new_hostname) communicator.received_commands.find { |cmd| cmd =~ %r(^sed .* /etc/hosts$) } end # Now we extract the regexp from that sed command so we can do some # verification on it let(:expression) { sed_command.sub(%r{^sed -i '\(.*\)' /etc/hosts$}, "\1") } let(:search) { Regexp.new(expression.split('@')[1].gsub(/\\/,'')) } let(:replace) { expression.split('@')[2] } end shared_examples 'inserting hostname in /etc/hosts' do include_examples 'mutating /etc/hosts helpers' context 'when target hostname is qualified' do let(:new_hostname) {'newhostname.newdomain.tld'} it 'works with a basic file' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname.newdomain.tld newhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end end context 'when target hostname is simple' do let(:new_hostname) {'newhostname'} it 'works with a basic file' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end end end shared_examples 'swapping simple hostname in /etc/hosts' do include_examples 'mutating /etc/hosts helpers' context 'when target hostname is qualified' do let(:new_hostname) {'newhostname.newdomain.tld'} it 'works with a basic file' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 oldhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname.newdomain.tld newhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end it 'does not touch suffixed hosts' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 oldhostname oldhostname.nope localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname.newdomain.tld newhostname oldhostname.nope localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end end context 'when target hostname is simple' do let(:new_hostname) {'newhostname'} it 'works with a basic file' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 oldhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end it 'does not touch suffixed hosts' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 oldhostname oldhostname.nope localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname oldhostname.nope localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end end end shared_examples 'swapping qualified hostname in /etc/hosts' do include_examples 'mutating /etc/hosts helpers' context 'when target hostname is qualified' do let(:new_hostname) {'newhostname.newdomain.tld'} it 'works with a basic file' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 oldhostname.olddomain.tld oldhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname.newdomain.tld newhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end it 'does not touch suffixed hosts' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 oldhostname.olddomain.tld oldhostname oldhostname.nope localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname.newdomain.tld newhostname oldhostname.nope localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end end context 'when target hostname is simple' do let(:new_hostname) {'newhostname'} it 'works with a basic file' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 oldhostname.olddomain.tld oldhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end it 'does not touch suffixed hosts' do original_etc_hosts = <<-ETC_HOSTS.gsub(/^ */, '') 127.0.0.1 oldhostname.olddomain.tld oldhostname oldhostname.nope localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 ETC_HOSTS modified_etc_hosts = original_etc_hosts.gsub(search, replace) modified_etc_hosts.should == <<-RESULT.gsub(/^ */, '') 127.0.0.1 newhostname oldhostname.nope localhost.localdomain localhost ::1 localhost6.localdomain6 localhost6 RESULT end end end vagrant-1.4.3/test/unit/plugins/guests/ubuntu/000077500000000000000000000000001226132634600214415ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/guests/ubuntu/cap/000077500000000000000000000000001226132634600222045ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/guests/ubuntu/cap/change_host_name_test.rb000066400000000000000000000023741226132634600270600ustar00rootroot00000000000000require File.expand_path("../../../../../base", __FILE__) require File.expand_path("../../../support/shared/debian_like_host_name_examples", __FILE__) describe "VagrantPlugins::GuestUbuntu::Cap::ChangeHostName" do let(:described_class) do VagrantPlugins::GuestUbuntu::Plugin.components.guest_capabilities[:ubuntu].get(:change_host_name) end let(:machine) { double("machine") } let(:communicator) { VagrantTests::DummyCommunicator::Communicator.new(machine) } let(:old_hostname) {'oldhostname.olddomain.tld' } before do machine.stub(:communicate).and_return(communicator) communicator.stub_command('hostname -f', stdout: old_hostname) end after do communicator.verify_expectations! end describe ".change_host_name" do it_behaves_like "a debian-like host name change" it "refreshes the hostname service with upstart" do communicator.expect_command(%q(service hostname start)) described_class.change_host_name(machine, 'newhostname.newdomain.tld') end it "renews dhcp on the system with the new hostname (with hotplug allowed)" do communicator.expect_command(%q(ifdown -a; ifup -a; ifup -a --allow=hotplug)) described_class.change_host_name(machine, 'newhostname.newdomain.tld') end end end vagrant-1.4.3/test/unit/plugins/providers/000077500000000000000000000000001226132634600206225ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/providers/virtualbox/000077500000000000000000000000001226132634600230215ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/providers/virtualbox/action/000077500000000000000000000000001226132634600242765ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/providers/virtualbox/action/prepare_nfs_settings_test.rb000066400000000000000000000064421226132634600321140ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Action::PrepareNFSSettings do include_context "virtualbox" let(:machine) { environment = Vagrant::Environment.new provider = :virtualbox provider_cls, provider_options = Vagrant.plugin("2").manager.providers[provider] provider_config = Vagrant.plugin("2").manager.provider_configs[provider] Vagrant::Machine.new( 'test_machine', provider, provider_cls, provider_config, provider_options, environment.config_global, Pathname('data_dir'), double('box'), environment ) } let(:env) {{ machine: machine }} let(:app) { lambda { |*args| }} let(:driver) { env[:machine].provider.driver } subject { described_class.new(app, env) } it "calls the next action in the chain" do called = false app = lambda { |*args| called = true } action = described_class.new(app, env) action.call(env) called.should == true end context "with an nfs synced folder" do before do env[:machine].config.vm.synced_folder("/host/path", "/guest/path", nfs: true) env[:machine].config.finalize! # Stub out the stuff so it just works by default driver.stub(read_network_interfaces: { 2 => {type: :hostonly, hostonly: "vmnet2"}, }) driver.stub(read_host_only_interfaces: [ {name: "vmnet2", ip: "1.2.3.4"}, ]) driver.stub(:read_guest_ip).with(1).and_return("2.3.4.5") # override sleep to 0 so test does not take seconds retry_options = subject.retry_options subject.stub(:retry_options).and_return(retry_options.merge(sleep: 0)) end it "sets nfs_host_ip and nfs_machine_ip properly" do subject.call(env) env[:nfs_host_ip].should == "1.2.3.4" env[:nfs_machine_ip].should == "2.3.4.5" end it "raises an error when no host only adapter is configured" do driver.stub(:read_network_interfaces) {{}} expect { subject.call(env) }. to raise_error(Vagrant::Errors::NFSNoHostonlyNetwork) end it "retries through guest property not found errors" do raise_then_return = [ lambda { raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, :guest_property => 'stub' }, lambda { "2.3.4.5" } ] driver.stub(:read_guest_ip) { raise_then_return.shift.call } subject.call(env) env[:nfs_host_ip].should == "1.2.3.4" env[:nfs_machine_ip].should == "2.3.4.5" end it "raises an error informing the user of a bug when the guest IP cannot be found" do driver.stub(:read_guest_ip) { raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, :guest_property => 'stub' } expect { subject.call(env) }. to raise_error(Vagrant::Errors::NFSNoGuestIP) end it "allows statically configured guest IPs to work for NFS, even when guest property would fail" do env[:machine].config.vm.network :private_network, ip: "11.12.13.14" driver.stub(:read_guest_ip) { raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound, :guest_property => "stub" } subject.call(env) env[:nfs_host_ip].should == "1.2.3.4" env[:nfs_machine_ip].should == ["11.12.13.14"] end end end vagrant-1.4.3/test/unit/plugins/providers/virtualbox/action/prepare_nfs_valid_ids_test.rb000066400000000000000000000024201226132634600322020ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Action::PrepareNFSValidIds do include_context "virtualbox" let(:machine) { environment = Vagrant::Environment.new provider = :virtualbox provider_cls, provider_options = Vagrant.plugin("2").manager.providers[provider] provider_config = Vagrant.plugin("2").manager.provider_configs[provider] Vagrant::Machine.new( 'test_machine', provider, provider_cls, provider_config, provider_options, environment.config_global, Pathname('data_dir'), double('box'), environment ) } let(:env) {{ machine: machine }} let(:app) { lambda { |*args| }} let(:driver) { env[:machine].provider.driver } subject { described_class.new(app, env) } before do driver.stub(read_vms: {}) end it "calls the next action in the chain" do called = false app = lambda { |*args| called = true } action = described_class.new(app, env) action.call(env) called.should == true end it "sets nfs_valid_ids" do hash = {"foo" => "1", "bar" => "4"} driver.stub(read_vms: hash) subject.call(env) expect(env[:nfs_valid_ids]).to eql(hash.values) end end vagrant-1.4.3/test/unit/plugins/providers/virtualbox/base.rb000066400000000000000000000002301226132634600242530ustar00rootroot00000000000000# base test helper for virtualbox unit tests require_relative "../../../base" require_relative "support/shared/virtualbox_driver_version_4_x_examples" vagrant-1.4.3/test/unit/plugins/providers/virtualbox/driver/000077500000000000000000000000001226132634600243145ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/providers/virtualbox/driver/version_4_0_test.rb000066400000000000000000000004421226132634600300270ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Driver::Version_4_0 do include_context "virtualbox" let(:vbox_version) { "4.0.0" } subject { VagrantPlugins::ProviderVirtualBox::Driver::Meta.new(uuid) } it_behaves_like "a version 4.x virtualbox driver" end vagrant-1.4.3/test/unit/plugins/providers/virtualbox/driver/version_4_1_test.rb000066400000000000000000000004421226132634600300300ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Driver::Version_4_1 do include_context "virtualbox" let(:vbox_version) { "4.1.0" } subject { VagrantPlugins::ProviderVirtualBox::Driver::Meta.new(uuid) } it_behaves_like "a version 4.x virtualbox driver" end vagrant-1.4.3/test/unit/plugins/providers/virtualbox/driver/version_4_2_test.rb000066400000000000000000000004421226132634600300310ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Driver::Version_4_2 do include_context "virtualbox" let(:vbox_version) { "4.2.0" } subject { VagrantPlugins::ProviderVirtualBox::Driver::Meta.new(uuid) } it_behaves_like "a version 4.x virtualbox driver" end vagrant-1.4.3/test/unit/plugins/providers/virtualbox/driver/version_4_3_test.rb000066400000000000000000000004441226132634600300340ustar00rootroot00000000000000require_relative "../base" describe VagrantPlugins::ProviderVirtualBox::Driver::Version_4_3 do include_context "virtualbox" let(:vbox_version) { "4.3.0" } subject { VagrantPlugins::ProviderVirtualBox::Driver::Meta.new(uuid) } it_behaves_like "a version 4.x virtualbox driver" end vagrant-1.4.3/test/unit/plugins/providers/virtualbox/support/000077500000000000000000000000001226132634600245355ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/providers/virtualbox/support/shared/000077500000000000000000000000001226132634600260035ustar00rootroot00000000000000virtualbox_driver_version_4_x_examples.rb000066400000000000000000000026671226132634600362530ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/providers/virtualbox/support/sharedshared_examples "a version 4.x virtualbox driver" do |options| before do raise ArgumentError, "Need virtualbox context to use these shared examples." if !(defined? vbox_context) end describe "read_guest_property" do it "reads the guest property of the machine referenced by the UUID" do key = "/Foo/Bar" subprocess.should_receive(:execute). with("VBoxManage", "guestproperty", "get", uuid, key, an_instance_of(Hash)). and_return(subprocess_result(stdout: "Value: Baz\n")) subject.read_guest_property(key).should == "Baz" end it "raises a virtualBoxGuestPropertyNotFound exception when the value is not set" do key = "/Not/There" subprocess.should_receive(:execute). with("VBoxManage", "guestproperty", "get", uuid, key, an_instance_of(Hash)). and_return(subprocess_result(stdout: "No value set!")) expect { subject.read_guest_property(key) }. to raise_error Vagrant::Errors::VirtualBoxGuestPropertyNotFound end end describe "read_guest_ip" do it "reads the guest property for the provided adapter number" do key = "/VirtualBox/GuestInfo/Net/1/V4/IP" subprocess.should_receive(:execute). with("VBoxManage", "guestproperty", "get", uuid, key, an_instance_of(Hash)). and_return(subprocess_result(stdout: "Value: 127.1.2.3")) value = subject.read_guest_ip(1) value.should == "127.1.2.3" end end end vagrant-1.4.3/test/unit/plugins/providers/virtualbox/synced_folder_test.rb000066400000000000000000000015311226132634600272250ustar00rootroot00000000000000require "vagrant" require Vagrant.source_root.join("test/unit/base") require Vagrant.source_root.join("plugins/providers/virtualbox/synced_folder") # TODO(mitchellh): tag with v2 describe VagrantPlugins::ProviderVirtualBox::SyncedFolder do let(:machine) do double("machine").tap do |m| end end subject { described_class.new } describe "usable" do it "should be with virtualbox provider" do machine.stub(provider_name: :virtualbox) subject.should be_usable(machine) end it "should not be with another provider" do machine.stub(provider_name: :vmware_fusion) subject.should_not be_usable(machine) end end describe "prepare" do let(:driver) { double("driver") } before do machine.stub(driver: driver) end it "should share the folders" do pending end end end vagrant-1.4.3/test/unit/plugins/provisioners/000077500000000000000000000000001226132634600213475ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/provisioners/shell/000077500000000000000000000000001226132634600224565ustar00rootroot00000000000000vagrant-1.4.3/test/unit/plugins/provisioners/shell/config_test.rb000066400000000000000000000027061226132634600253140ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe "VagrantPlugins::Shell::Config" do let(:described_class) do VagrantPlugins::Shell::Plugin.components.configs[:provisioner][:shell] end let(:machine) { double('machine', env: Vagrant::Environment.new) } let(:file_that_exists) { File.expand_path(__FILE__) } subject { described_class.new } describe "validate" do it "passes with no args" do subject.path = file_that_exists subject.finalize! result = subject.validate(machine) result["shell provisioner"].should == [] end it "passes with string args" do subject.path = file_that_exists subject.args = "a string" subject.finalize! result = subject.validate(machine) result["shell provisioner"].should == [] end it "passes with array args" do subject.path = file_that_exists subject.args = ["an", "array"] subject.finalize! result = subject.validate(machine) result["shell provisioner"].should == [] end it "returns an error if args is neither a string nor an array" do neither_array_nor_string = Object.new subject.path = file_that_exists subject.args = neither_array_nor_string subject.finalize! result = subject.validate(machine) result["shell provisioner"].should == [ I18n.t("vagrant.provisioners.shell.args_bad_type") ] end end end vagrant-1.4.3/test/unit/support/000077500000000000000000000000001226132634600166405ustar00rootroot00000000000000vagrant-1.4.3/test/unit/support/dummy_communicator.rb000066400000000000000000000035541226132634600231070ustar00rootroot00000000000000module VagrantTests module DummyCommunicator class Communicator < Vagrant.plugin("2", :communicator) def ready? true end attr_reader :known_commands def initialize(machine) @known_commands = Hash.new do |hash, key| hash[key] = { expected: 0, received: 0, response: nil } end end def expected_commands known_commands.select do |command, info| info[:expected] > 0 end end def received_commands known_commands.select do |command, info| info[:received] > 0 end.keys end def stub_command(command, response) known_commands[command][:response] = response end def expect_command(command) known_commands[command][:expected] += 1 end def received_summary received_commands.map { |cmd| " - #{cmd}" }.unshift('received:').join("\n") end def verify_expectations! expected_commands.each do |command, info| if info[:expected] != info[:received] fail([ "expected to receive '#{command}' #{info[:expected]} times", "got #{info[:received]} times instead", received_summary ].join("\n")) end end end def execute(command, opts=nil) known = known_commands[command] known[:received] += 1 response = known[:response] return unless response if block_given? [:stdout, :stderr].each do |type| Array(response[type]).each do |line| yield type, line end end end if response[:raise] raise response[:raise] end response[:exit_code] end def sudo(command, opts=nil, &block) execute(command, opts, &block) end end end end vagrant-1.4.3/test/unit/support/dummy_provider.rb000066400000000000000000000006431226132634600222350ustar00rootroot00000000000000module VagrantTests class DummyProviderPlugin < Vagrant.plugin("2") name "Dummy Provider" description <<-EOF This creates a provider named "dummy" which does nothing, so that the unit tests aren't reliant on VirtualBox (or any other real provider for that matter). EOF provider(:dummy) { DummyProvider } end class DummyProvider < Vagrant.plugin("2", :provider) # No-op end end vagrant-1.4.3/test/unit/support/isolated_environment.rb000066400000000000000000000114251226132634600234200ustar00rootroot00000000000000require "fileutils" require "pathname" require "tempfile" require "json" require "log4r" require "vagrant/util/platform" require "vagrant/util/subprocess" require "support/isolated_environment" require "support/tempdir" module Unit class IsolatedEnvironment < ::IsolatedEnvironment def create_vagrant_env(options=nil) options = { :cwd => @workdir, :home_path => @homedir }.merge(options || {}) Vagrant::Environment.new(options) end # This creates a file in the isolated environment. By default this file # will be created in the working directory of the isolated environment. def file(name, contents) @workdir.join(name).open("w+") do |f| f.write(contents) end end def vagrantfile(contents, root=nil) root ||= @workdir root.join("Vagrantfile").open("w+") do |f| f.write(contents) end end def box(name, vagrantfile_contents="") # Create the box directory box_dir = boxes_dir.join(name) box_dir.mkpath # Create the "box.ovf" file because that is how Vagrant heuristically # determines a box is a V1 box. box_dir.join("box.ovf").open("w") { |f| f.write("") } # Populate the vagrantfile vagrantfile(vagrantfile_contents, box_dir) # Return the directory box_dir end # Create an alias because "box" makes a V1 box, so "box1" alias :box1 :box # Creates a fake box to exist in this environment. # # @param [String] name Name of the box # @param [Symbol] provider Provider the box was built for. # @return [Pathname] Path to the box directory. def box2(name, provider, options=nil) # Default options options = { :vagrantfile => "" }.merge(options || {}) # Make the box directory box_dir = boxes_dir.join(name, provider.to_s) box_dir.mkpath # Create a metadata.json file box_metadata_file = box_dir.join("metadata.json") box_metadata_file.open("w") do |f| f.write(JSON.generate({ :provider => provider.to_s })) end # Create a Vagrantfile box_vagrantfile = box_dir.join("Vagrantfile") box_vagrantfile.open("w") do |f| f.write(options[:vagrantfile]) end # Return the box directory box_dir end # This creates a "box" file that is a valid V1 box. # # @return [Pathname] Path to the newly created box. def box1_file # Create a temporary directory to store our data we will tar up td_source = Tempdir.new td_dest = Tempdir.new # Store the temporary directory so it is not deleted until # this instance is garbage collected. @_box2_file_temp ||= [] @_box2_file_temp << td_dest # The source as a Pathname, which is easier to work with source = Pathname.new(td_source.path) # The destination file result = Pathname.new(td_dest.path).join("temporary.box") # Put a "box.ovf" in there. source.join("box.ovf").open("w") do |f| f.write("FOO!") end Dir.chdir(source) do # Find all the files in our current directory and tar it up! files = Dir.glob(File.join(".", "**", "*")) # Package! Vagrant::Util::Subprocess.execute("bsdtar", "-czf", result.to_s, *files) end # Resulting box result end # This creates a "box" file with the given provider. # # @param [Symbol] provider Provider for the box. # @return [Pathname] Path to the newly created box. def box2_file(provider, options=nil) options ||= {} # This is the metadata we want to store in our file metadata = { "type" => "v2_box", "provider" => provider }.merge(options[:metadata] || {}) # Create a temporary directory to store our data we will tar up td_source = Tempdir.new td_dest = Tempdir.new # Store the temporary directory so it is not deleted until # this instance is garbage collected. @_box2_file_temp ||= [] @_box2_file_temp << td_dest # The source as a Pathname, which is easier to work with source = Pathname.new(td_source.path) # The destination file result = Pathname.new(td_dest.path).join("temporary.box") # Put the metadata.json in here. source.join("metadata.json").open("w") do |f| f.write(JSON.generate(metadata)) end Dir.chdir(source) do # Find all the files in our current directory and tar it up! files = Dir.glob(File.join(".", "**", "*")) # Package! Vagrant::Util::Subprocess.execute("bsdtar", "-czf", result.to_s, *files) end # Resulting box result end def boxes_dir dir = @homedir.join("boxes") dir.mkpath dir end end end vagrant-1.4.3/test/unit/support/shared/000077500000000000000000000000001226132634600201065ustar00rootroot00000000000000vagrant-1.4.3/test/unit/support/shared/action_synced_folders_context.rb000066400000000000000000000004661226132634600265450ustar00rootroot00000000000000shared_context "synced folder actions" do # This creates a synced folder implementation. def impl(usable, name) Class.new(Vagrant.plugin("2", :synced_folder)) do define_method(:name) do name end define_method(:usable?) do |machine| usable end end end end vagrant-1.4.3/test/unit/support/shared/base_context.rb000066400000000000000000000055231226132634600231160ustar00rootroot00000000000000require "tempfile" require "support/tempdir" require "unit/support/isolated_environment" shared_context "unit" do before(:each) do # State to store the list of registered plugins that we have to # unregister later. @_plugins = [] # Create a thing to store our temporary files so that they aren't # unlinked right away. @_temp_files = [] end after(:each) do # Unregister each of the plugins we have may have temporarily # registered for the duration of this test. @_plugins.each do |plugin| Vagrant.plugin("1").manager.unregister(plugin) Vagrant.plugin("2").manager.unregister(plugin) end end # This creates an isolated environment so that Vagrant doesn't # muck around with your real system during unit tests. # # The returned isolated environment has a variety of helper # methods on it to easily create files, Vagrantfiles, boxes, # etc. def isolated_environment env = Unit::IsolatedEnvironment.new yield env if block_given? env end # This registers a Vagrant plugin for the duration of a single test. # This will yield a new plugin class that you can then call the # public plugin methods on. # # @yield [plugin] Yields the plugin class for you to call the public # API that you need to. def register_plugin(version=nil) version ||= Vagrant::Config::CURRENT_VERSION plugin = Class.new(Vagrant.plugin(version)) plugin.name("Test Plugin #{plugin.inspect}") yield plugin if block_given? @_plugins << plugin plugin end # This helper creates a temporary file and returns a Pathname # object pointed to it. # # @return [Pathname] def temporary_file(contents=nil) f = Tempfile.new("vagrant-unit") if contents f.write(contents) f.flush end # Store the tempfile in an instance variable so that it is not # garbage collected, so that the tempfile is not unlinked. @_temp_files << f return Pathname.new(f.path) end # This creates a temporary directory and returns a {Pathname} # pointing to it. # # @return [Pathname] def temporary_dir # Create a temporary directory and append it to the instance # variabe so that it isn't garbage collected and deleted d = Tempdir.new("vagrant-unit") @_temp_files << d # Return the pathname return Pathname.new(d.path) end # This helper provides temporary environmental variable changes. def with_temp_env(environment) # Build up the new environment, preserving the old values so we # can replace them back in later. old_env = {} environment.each do |key, value| old_env[key] = ENV[key] ENV[key] = value end # Call the block, returning its return value return yield ensure # Reset the environment no matter what old_env.each do |key, value| ENV[key] = value end end end vagrant-1.4.3/test/unit/support/shared/virtualbox_context.rb000066400000000000000000000024111226132634600243740ustar00rootroot00000000000000shared_context "virtualbox" do let(:vbox_context) { true } let(:uuid) { "1234-abcd-5678-efgh" } let(:vbox_version) { "4.3.4" } let(:subprocess) { double("Vagrant::Util::Subprocess") } # this is a helper that returns a duck type suitable from a system command # execution; allows setting exit_code, stdout, and stderr in stubs. def subprocess_result(options={}) defaults = {exit_code: 0, stdout: "", stderr: ""} double("subprocess_result", defaults.merge(options)) end before do # we don't want unit tests to ever run commands on the system; so we wire # in a double to ensure any unexpected messages raise exceptions stub_const("Vagrant::Util::Subprocess", subprocess) # drivers will blow up on instantiation if they cannot determine the # virtualbox version, so wire this stub in automatically subprocess.stub(:execute). with("VBoxManage", "--version", an_instance_of(Hash)). and_return(subprocess_result(stdout: vbox_version)) # drivers also call vm_exists? during init; subprocess.stub(:execute). with("VBoxManage", "showvminfo", kind_of(String), kind_of(Hash)). and_return(subprocess_result(exit_code: 0)) end end vagrant-1.4.3/test/unit/vagrant/000077500000000000000000000000001226132634600165665ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/action/000077500000000000000000000000001226132634600200435ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/action/builder_test.rb000066400000000000000000000131661226132634600230640ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) describe Vagrant::Action::Builder do let(:data) { { :data => [] } } # This returns a proc that can be used with the builder # that simply appends data to an array in the env. def appender_proc(data) result = Proc.new { |env| env[:data] << data } # Define a to_s on it for helpful output result.define_singleton_method(:to_s) do "" end result end context "copying" do it "should copy the stack" do copy = subject.dup copy.stack.object_id.should_not == subject.stack.object_id end end context "build" do it "should provide build as a shortcut for basic sequences" do data = {} proc = Proc.new { |env| env[:data] = true } subject = described_class.build(proc) subject.call(data) data[:data].should == true end end context "basic `use`" do it "should add items to the stack and make them callable" do data = {} proc = Proc.new { |env| env[:data] = true } subject.use proc subject.call(data) data[:data].should == true end it "should be able to add multiple items" do data = {} proc1 = Proc.new { |env| env[:one] = true } proc2 = Proc.new { |env| env[:two] = true } subject.use proc1 subject.use proc2 subject.call(data) data[:one].should == true data[:two].should == true end it "should be able to add another builder" do data = {} proc1 = Proc.new { |env| env[:one] = true } # Build the first builder one = described_class.new one.use proc1 # Add it to this builder two = described_class.new two.use one # Call the 2nd and verify results two.call(data) data[:one].should == true end end context "inserting" do it "can insert at an index" do subject.use appender_proc(1) subject.insert(0, appender_proc(2)) subject.call(data) data[:data].should == [2, 1] end it "can insert by name" do # Create the proc then make sure it has a name bar_proc = appender_proc(2) def bar_proc.name; :bar; end subject.use appender_proc(1) subject.use bar_proc subject.insert_before :bar, appender_proc(3) subject.call(data) data[:data].should == [1, 3, 2] end it "can insert next to a previous object" do proc2 = appender_proc(2) subject.use appender_proc(1) subject.use proc2 subject.insert(proc2, appender_proc(3)) subject.call(data) data[:data].should == [1, 3, 2] end it "can insert before" do subject.use appender_proc(1) subject.insert_before 0, appender_proc(2) subject.call(data) data[:data].should == [2, 1] end it "can insert after" do subject.use appender_proc(1) subject.use appender_proc(3) subject.insert_after 0, appender_proc(2) subject.call(data) data[:data].should == [1, 2, 3] end it "merges middleware stacks of other builders" do wrapper_class = Proc.new do |letter| Class.new do def initialize(app, env) @app = app end define_method(:call) do |env| env[:data] << "#{letter}1" @app.call(env) env[:data] << "#{letter}2" end end end proc2 = appender_proc(2) subject.use appender_proc(1) subject.use proc2 builder = described_class.new builder.use wrapper_class.call("A") builder.use wrapper_class.call("B") subject.insert(proc2, builder) subject.call(data) data[:data].should == [1, "A1", "B1", 2, "B2", "A2"] end it "raises an exception if an invalid object given for insert" do expect { subject.insert "object", appender_proc(1) }. to raise_error(RuntimeError) end it "raises an exception if an invalid object given for insert_after" do expect { subject.insert_after "object", appender_proc(1) }. to raise_error(RuntimeError) end end context "replace" do it "can replace an object" do proc1 = appender_proc(1) proc2 = appender_proc(2) subject.use proc1 subject.replace proc1, proc2 subject.call(data) data[:data].should == [2] end it "can replace by index" do proc1 = appender_proc(1) proc2 = appender_proc(2) subject.use proc1 subject.replace 0, proc2 subject.call(data) data[:data].should == [2] end end context "deleting" do it "can delete by object" do proc1 = appender_proc(1) subject.use proc1 subject.use appender_proc(2) subject.delete proc1 subject.call(data) data[:data].should == [2] end it "can delete by index" do proc1 = appender_proc(1) subject.use proc1 subject.use appender_proc(2) subject.delete 0 subject.call(data) data[:data].should == [2] end end describe "action hooks" do it "applies them properly" do hook = double("hook") hook.stub(:apply) do |builder| builder.use appender_proc(2) end data[:action_hooks] = [hook] subject.use appender_proc(1) subject.call(data) data[:data].should == [1, 2] data[:action_hooks_already_ran].should == true end it "applies without prepend/append if it has already" do hook = double("hook") hook.should_receive(:apply).with(anything, { :no_prepend_or_append => true }).once data[:action_hooks] = [hook] data[:action_hooks_already_ran] = true subject.call(data) end end end vagrant-1.4.3/test/unit/vagrant/action/builtin/000077500000000000000000000000001226132634600215115ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/action/builtin/call_test.rb000066400000000000000000000063731226132634600240210ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::Call do let(:app) { lambda { |env| } } let(:env) { {} } it "should yield the env to the block" do received = nil callable = lambda do |env| env[:result] = "value" end described_class.new(app, env, callable) do |env, builder| received = env[:result] end.call({}) received.should == "value" end it "should update the original env with any changes" do callable = lambda { |env| } next_step = lambda { |env| env[:inner] = true } described_class.new(app, env, callable) do |_env, builder| builder.use next_step end.call(env) env[:inner].should == true end it "should call the callable with the original environment" do received = nil callable = lambda { |env| received = env[:foo] } described_class.new(app, env, callable) do |_env, _builder| # Nothing. end.call({ :foo => :bar }) received.should == :bar end it "should call the next builder" do received = nil callable = lambda { |env| } next_step = lambda { |env| received = "value" } described_class.new(app, env, callable) do |_env, builder| builder.use next_step end.call({}) received.should == "value" end it "should call the next builder with the original environment" do received = nil callable = lambda { |env| } next_step = lambda { |env| received = env[:foo] } described_class.new(app, env, callable) do |_env, builder| builder.use next_step end.call({ :foo => :bar }) received.should == :bar end it "should instantiate the callable with the extra args" do env = {} callable = Class.new do def initialize(app, env, arg) env[:arg] = arg end def call(env); end end result = nil instance = described_class.new(app, env, callable, :foo) do |inner_env, _builder| result = inner_env[:arg] end instance.call(env) result.should == :foo end it "should call the recover method for the sequence in an error" do # Basic variables callable = lambda { |env| } # Build the steps for the test basic_step = Class.new do def initialize(app, env) @app = app @env = env end def call(env) @app.call(env) end end step_a = Class.new(basic_step) do def call(env) env[:steps] << :call_A super end def recover(env) env[:steps] << :recover_A end end step_b = Class.new(basic_step) do def call(env) env[:steps] << :call_B super end def recover(env) env[:steps] << :recover_B end end instance = described_class.new(app, env, callable) do |_env, builder| builder.use step_a builder.use step_b end env[:steps] = [] instance.call(env) instance.recover(env) env[:steps].should == [:call_A, :call_B, :recover_B, :recover_A] end it "should recover even if it failed in the callable" do callable = lambda { |env| raise "error" } instance = described_class.new(app, env, callable) { |_env, _builder| } instance.call(env) rescue nil expect { instance.recover(env) }. to_not raise_error end end vagrant-1.4.3/test/unit/vagrant/action/builtin/confirm_test.rb000066400000000000000000000021651226132634600245360ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::Confirm do let(:app) { lambda { |env| } } let(:env) { { :ui => double("ui") } } let(:message) { "foo" } ["y", "Y"].each do |valid| it "should set the result to true if '#{valid}' is given" do env[:ui].should_receive(:ask).with(message).and_return(valid) described_class.new(app, env, message).call(env) env[:result].should be end end it "should set the result to true if force matches" do force_key = :tubes env[force_key] = true described_class.new(app, env, message, force_key).call(env) env[:result].should be end it "should ask if force is not true" do force_key = :tubes env[force_key] = false env[:ui].should_receive(:ask).with(message).and_return("nope") described_class.new(app, env, message).call(env) env[:result].should_not be end it "should set result to false if anything else is given" do env[:ui].should_receive(:ask).with(message).and_return("nope") described_class.new(app, env, message).call(env) env[:result].should_not be end end vagrant-1.4.3/test/unit/vagrant/action/builtin/env_set_test.rb000066400000000000000000000010161226132634600245360ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::EnvSet do let(:app) { lambda { |env| } } let(:env) { {} } it "should set the new environment" do described_class.new(app, env, :foo => :bar).call(env) env[:foo].should == :bar end it "should call the next middleware" do callable = lambda { |env| env[:called] = env[:foo] } env[:called].should be_nil described_class.new(callable, env, :foo => :yep).call(env) env[:called].should == :yep end end vagrant-1.4.3/test/unit/vagrant/action/builtin/graceful_halt_test.rb000066400000000000000000000033011226132634600256720ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::GracefulHalt do let(:app) { lambda { |env| } } let(:env) { { :machine => machine, :ui => ui } } let(:machine) do result = double("machine") result.stub(:config).and_return(machine_config) result.stub(:guest).and_return(machine_guest) result.stub(:state).and_return(machine_state) result end let(:machine_config) do double("machine_config").tap do |top_config| vm_config = double("machien_vm_config") vm_config.stub(:graceful_halt_timeout => 10) top_config.stub(:vm => vm_config) end end let(:machine_guest) { double("machine_guest") } let(:machine_state) do double("machine_state").tap do |result| result.stub(:id).and_return(:unknown) end end let(:target_state) { :target } let(:ui) do double("ui").tap do |result| result.stub(:info) end end it "should do nothing if force is specified" do env[:force_halt] = true machine_guest.should_not_receive(:capability) described_class.new(app, env, target_state).call(env) env[:result].should == false end it "should do nothing if there is an invalid source state" do machine_state.stub(:id).and_return(:invalid_source) machine_guest.should_not_receive(:capability) described_class.new(app, env, target_state, :target_source).call(env) env[:result].should == false end it "should gracefully halt and wait for the target state" do machine_guest.should_receive(:capability).with(:halt).once machine_state.stub(:id).and_return(target_state) described_class.new(app, env, target_state).call(env) env[:result].should == true end end vagrant-1.4.3/test/unit/vagrant/action/builtin/lock_test.rb000066400000000000000000000050421226132634600240260ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::Lock do let(:app) { lambda { |env| } } let(:env) { {} } let(:lock_path) do @__lock_path = Tempfile.new("vagrant-test-lock") @__lock_path.path.to_s end let(:options) do { :exception => Class.new(StandardError), :path => lock_path } end it "should require a path" do expect { described_class.new(app, env) }. to raise_error(ArgumentError) expect { described_class.new(app, env, :path => "foo") }. to raise_error(ArgumentError) expect { described_class.new(app, env, :exception => "foo") }. to raise_error(ArgumentError) expect { described_class.new(app, env, :path => "bar", :exception => "foo") }. to_not raise_error end it "should allow the path to be a proc" do inner_acquire = true app = lambda do |env| File.open(lock_path, "w+") do |f| inner_acquire = f.flock(File::LOCK_EX | File::LOCK_NB) end end options[:path] = lambda { |env| lock_path } instance = described_class.new(app, env, options) instance.call(env) inner_acquire.should == false end it "should allow the exception to be a proc" do exception = options[:exception] options[:exception] = lambda { |env| exception } File.open(lock_path, "w+") do |f| # Acquire lock f.flock(File::LOCK_EX | File::LOCK_NB).should == 0 # Test! instance = described_class.new(app, env, options) expect { instance.call(env) }. to raise_error(exception) end end it "should call the middleware with the lock held" do inner_acquire = true app = lambda do |env| File.open(lock_path, "w+") do |f| inner_acquire = f.flock(File::LOCK_EX | File::LOCK_NB) end end instance = described_class.new(app, env, options) instance.call(env) inner_acquire.should == false end it "should raise an exception if the lock is already held" do File.open(lock_path, "w+") do |f| # Acquire lock f.flock(File::LOCK_EX | File::LOCK_NB).should == 0 # Test! instance = described_class.new(app, env, options) expect { instance.call(env) }. to raise_error(options[:exception]) end end it "should allow nesting locks within the same middleware sequence" do called = false app = lambda { |env| called = true } inner = described_class.new(app, env, options) outer = described_class.new(inner, env, options) outer.call(env) called.should == true end end vagrant-1.4.3/test/unit/vagrant/action/builtin/mixin_synced_folders_test.rb000066400000000000000000000051061226132634600273060ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require "vagrant/action/builtin/mixin_synced_folders" describe Vagrant::Action::Builtin::MixinSyncedFolders do include_context "synced folder actions" subject do Class.new do extend Vagrant::Action::Builtin::MixinSyncedFolders end end let(:machine) do double("machine").tap do |machine| machine.stub(:config).and_return(machine_config) end end let(:machine_config) do double("machine_config").tap do |top_config| top_config.stub(:vm => vm_config) end end let(:vm_config) { double("machine_vm_config") } describe "default_synced_folder_type" do it "returns the usable implementation" do plugins = { "bad" => [impl(false, "bad"), 0], "nope" => [impl(true, "nope"), 1], "good" => [impl(true, "good"), 5], } result = subject.default_synced_folder_type(machine, plugins) result.should == "good" end end describe "impl_opts" do it "should return only relevant keys" do env = { :foo_bar => "baz", :bar_bar => "nope", :foo_baz => "bar", } result = subject.impl_opts("foo", env) result.length.should == 2 result[:foo_bar].should == "baz" result[:foo_baz].should == "bar" end end describe "synced_folders" do let(:folders) { {} } let(:plugins) { {} } before do plugins[:default] = [impl(true, "default"), 10] plugins[:nfs] = [impl(true, "nfs"), 5] subject.stub(:plugins => plugins) vm_config.stub(:synced_folders => folders) end it "should raise exception if bad type is given" do folders["root"] = { type: "bad" } expect { subject.synced_folders(machine) }. to raise_error(StandardError) end it "should return the proper set of folders" do folders["root"] = {} folders["nfs"] = { type: "nfs" } result = subject.synced_folders(machine) result.length.should == 2 result[:default].should == { "root" => folders["root"] } result[:nfs].should == { "nfs" => folders["nfs"] } end it "should error if an explicit type is unusable" do plugins[:unusable] = [impl(false, "bad"), 15] folders["root"] = { type: "unusable" } expect { subject.synced_folders(machine) }. to raise_error end it "should ignore disabled folders" do folders["root"] = {} folders["foo"] = { disabled: true } result = subject.synced_folders(machine) result.length.should == 1 result[:default].length.should == 1 end end end vagrant-1.4.3/test/unit/vagrant/action/builtin/ssh_exec_test.rb000066400000000000000000000030101226132634600246700ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require "vagrant/util/ssh" describe Vagrant::Action::Builtin::SSHExec do let(:app) { lambda { |env| } } let(:env) { { :machine => machine } } let(:machine) do result = double("machine") result.stub(:ssh_info).and_return(machine_ssh_info) result end let(:machine_ssh_info) { {} } let(:ssh_klass) { Vagrant::Util::SSH } before(:each) do # Stub the methods so that even if we test incorrectly, no side # effects actually happen. ssh_klass.stub(:check_key_permissions) ssh_klass.stub(:exec) end it "should raise an exception if SSH is not ready" do not_ready_machine = double("machine") not_ready_machine.stub(:ssh_info).and_return(nil) env[:machine] = not_ready_machine expect { described_class.new(app, env).call(env) }. to raise_error(Vagrant::Errors::SSHNotReady) end it "should check key permissions then exec" do key_path = "/foo" machine_ssh_info[:private_key_path] = [key_path] ssh_klass.should_receive(:check_key_permissions). with(Pathname.new(key_path)). once. ordered ssh_klass.should_receive(:exec). with(machine_ssh_info, nil). once. ordered described_class.new(app, env).call(env) end it "should exec with the options given in `ssh_opts`" do ssh_opts = { :foo => :bar } ssh_klass.should_receive(:exec). with(machine_ssh_info, ssh_opts) env[:ssh_opts] = ssh_opts described_class.new(app, env).call(env) end end vagrant-1.4.3/test/unit/vagrant/action/builtin/synced_folder_cleanup_test.rb000066400000000000000000000055551226132634600274360ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::SyncedFolderCleanup do include_context "synced folder actions" let(:app) { lambda { |env| } } let(:env) { { :machine => machine, :ui => ui } } let(:machine) do double("machine").tap do |machine| machine.stub(:config).and_return(machine_config) end end let(:machine_config) do double("machine_config").tap do |top_config| top_config.stub(:vm => vm_config) end end let(:vm_config) { double("machine_vm_config") } let(:ui) do double("ui").tap do |result| result.stub(:info) end end subject { described_class.new(app, env) } def create_cleanup_tracker Class.new(impl(true, "good")) do class_variable_set(:@@clean, false) def self.clean class_variable_get(:@@clean) end def cleanup(machine, opts) self.class.class_variable_set(:@@clean, true) end end end describe "call" do let(:synced_folders) { {} } let(:plugins) { {} } before do plugins[:default] = [impl(true, "default"), 10] plugins[:nfs] = [impl(true, "nfs"), 5] env[:machine] = Object.new env[:root_path] = Pathname.new(Dir.mktmpdir) subject.stub(:plugins => plugins) subject.stub(:synced_folders => synced_folders) end it "should invoke cleanup" do tracker = create_cleanup_tracker plugins[:tracker] = [tracker, 15] synced_folders["tracker"] = { "root" => { hostpath: "foo", }, "other" => { hostpath: "bar", create: true, } } expect_any_instance_of(tracker).to receive(:cleanup). with(env[:machine], { tracker_foo: :bar }) # Test that the impl-specific opts are passed through env[:tracker_foo] = :bar subject.call(env) end it "should invoke cleanup once per implementation" do trackers = [] (0..2).each do |tracker| trackers << create_cleanup_tracker end plugins[:tracker_0] = [trackers[0], 15] plugins[:tracker_1] = [trackers[1], 15] plugins[:tracker_2] = [trackers[2], 15] synced_folders["tracker_0"] = { "root" => { hostpath: "foo" }, "other" => { hostpath: "bar", create: true } } synced_folders["tracker_1"] = { "root" => { hostpath: "foo" } } synced_folders["tracker_2"] = { "root" => { hostpath: "foo" }, "other" => { hostpath: "bar", create: true }, "another" => { hostpath: "baz" } } subject.call(env) trackers[0].clean.should be_true trackers[1].clean.should be_true trackers[2].clean.should be_true end end end vagrant-1.4.3/test/unit/vagrant/action/builtin/synced_folders_test.rb000066400000000000000000000047771226132634600261170ustar00rootroot00000000000000require "pathname" require "tmpdir" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Action::Builtin::SyncedFolders do include_context "synced folder actions" let(:app) { lambda { |env| } } let(:env) { { :machine => machine, :ui => ui } } let(:machine) do double("machine").tap do |machine| machine.stub(:config).and_return(machine_config) end end let(:machine_config) do double("machine_config").tap do |top_config| top_config.stub(:vm => vm_config) end end let(:vm_config) { double("machine_vm_config") } let(:ui) do double("ui").tap do |result| result.stub(:info) end end subject { described_class.new(app, env) } describe "call" do let(:synced_folders) { {} } let(:plugins) { {} } before do plugins[:default] = [impl(true, "default"), 10] plugins[:nfs] = [impl(true, "nfs"), 5] env[:root_path] = Pathname.new(Dir.mktmpdir) subject.stub(:plugins => plugins) subject.stub(:synced_folders => synced_folders) end it "should create on the host if specified" do synced_folders["default"] = { "root" => { hostpath: "foo", }, "other" => { hostpath: "bar", create: true, } } subject.call(env) env[:root_path].join("foo").should_not be_directory env[:root_path].join("bar").should be_directory end it "should invoke prepare then enable" do order = [] tracker = Class.new(impl(true, "good")) do define_method(:prepare) do |machine, folders, opts| order << :prepare end define_method(:enable) do |machine, folders, opts| order << :enable end end plugins[:tracker] = [tracker, 15] synced_folders["tracker"] = { "root" => { hostpath: "foo", }, "other" => { hostpath: "bar", create: true, } } subject.call(env) order.should == [:prepare, :enable] end it "should scope hash override the settings" do actual = nil tracker = Class.new(impl(true, "good")) do define_method(:prepare) do |machine, folders, opts| actual = folders end end plugins[:tracker] = [tracker, 15] synced_folders["tracker"] = { "root" => { hostpath: "foo", tracker__foo: "bar", }, } subject.call(env) actual["root"][:foo].should == "bar" end end end vagrant-1.4.3/test/unit/vagrant/action/hook_test.rb000066400000000000000000000050001226132634600223620ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/action/builder" require "vagrant/action/hook" describe Vagrant::Action::Hook do describe "defaults" do its("after_hooks") { should be_empty } its("before_hooks") { should be_empty } its("append_hooks") { should be_empty } its("prepend_hooks") { should be_empty } end describe "before hooks" do let(:existing) { "foo" } it "should append them" do block = Proc.new {} subject.before(existing, 1) subject.before(existing, 2) subject.before(existing, 3, :arg, &block) subject.before_hooks[existing].should == [ [1, [], nil], [2, [], nil], [3, [:arg], block] ] end end describe "after hooks" do let(:existing) { "foo" } it "should append them" do block = Proc.new {} subject.after(existing, 1) subject.after(existing, 2) subject.after(existing, 3, :arg, &block) subject.after_hooks[existing].should == [ [1, [], nil], [2, [], nil], [3, [:arg], block] ] end end describe "append" do it "should make a list" do block = Proc.new {} subject.append(1) subject.append(2) subject.append(3, :arg, &block) subject.append_hooks.should == [ [1, [], nil], [2, [], nil], [3, [:arg], block] ] end end describe "prepend" do it "should make a list" do block = Proc.new {} subject.prepend(1) subject.prepend(2) subject.prepend(3, :arg, &block) subject.prepend_hooks.should == [ [1, [], nil], [2, [], nil], [3, [:arg], block] ] end end describe "applying" do let(:builder) { Vagrant::Action::Builder.new } it "should build the proper stack" do subject.prepend("1", 2) subject.append("9") subject.after("1", "2") subject.before("9", "8") subject.apply(builder) builder.stack.should == [ ["1", [2], nil], ["2", [], nil], ["8", [], nil], ["9", [], nil] ] end it "should not prepend or append if disabled" do builder.use("3") builder.use("8") subject.prepend("1", 2) subject.append("9") subject.after("3", "4") subject.before("8", "7") subject.apply(builder, no_prepend_or_append: true) builder.stack.should == [ ["3", [], nil], ["4", [], nil], ["7", [], nil], ["8", [], nil] ] end end end vagrant-1.4.3/test/unit/vagrant/action/runner_test.rb000066400000000000000000000040201226132634600227340ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) describe Vagrant::Action::Runner do let(:instance) { described_class.new } it "should raise an error if an invalid callable is given" do expect { instance.run(7) }.to raise_error(ArgumentError, /must be a callable/) end it "should be able to use a Proc as a callable" do callable = Proc.new { raise Exception, "BOOM" } expect { instance.run(callable) }.to raise_error(Exception, "BOOM") end it "should be able to use a Method instance as a callable" do klass = Class.new do def action(env) raise Exception, "BANG" end end callable = klass.new.method(:action) expect { instance.run(callable) }.to raise_error(Exception, "BANG") end it "should be able to use a Class as a callable" do callable = Class.new do def initialize(app, env) end def call(env) raise Exception, "BOOM" end end expect { instance.run(callable) }.to raise_error(Exception, "BOOM") end it "should return the resulting environment" do callable = lambda do |env| env[:data] = "value" # Return nil so we can make sure it isn't using this return value nil end result = instance.run(callable) result[:data].should == "value" end it "should pass options into hash given to callable" do result = nil callable = lambda do |env| result = env["data"] end instance.run(callable, "data" => "foo") result.should == "foo" end it "should pass global options into the hash" do result = nil callable = lambda do |env| result = env["data"] end instance = described_class.new("data" => "bar") instance.run(callable) result.should == "bar" end it "should yield the block passed to the init method to get lazy loaded globals" do result = nil callable = lambda do |env| result = env["data"] end instance = described_class.new { { "data" => "bar" } } instance.run(callable) result.should == "bar" end end vagrant-1.4.3/test/unit/vagrant/action/warden_test.rb000066400000000000000000000041341226132634600227110ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) describe Vagrant::Action::Warden do let(:data) { { :data => [] } } let(:instance) { described_class.new } # This returns a proc that can be used with the builder # that simply appends data to an array in the env. def appender_proc(data) Proc.new { |env| env[:data] << data } end it "calls the actions like normal" do instance = described_class.new([appender_proc(1), appender_proc(2)], data) instance.call(data) data[:data].should == [1, 2] end it "starts a recovery sequence when an exception is raised" do class Action def initialize(app, env) @app = app end def call(env) @app.call(env) end def recover(env) env[:recover] << 1 end end class ActionTwo def initialize(app, env) @app = app end def call(env) @app.call(env) end def recover(env) env[:recover] << 2 end end error_proc = Proc.new { raise "ERROR!" } data = { :recover => [] } instance = described_class.new([Action, ActionTwo, error_proc], data) # The error should be raised back up expect { instance.call(data) }. to raise_error(RuntimeError) # Verify the recovery process goes in reverse order data[:recover].should == [2, 1] # Verify that the error is available in the data data["vagrant.error"].should be_kind_of(RuntimeError) end it "does not do a recovery sequence if SystemExit is raised" do class Action def initialize(app, env) @app = app end def call(env) @app.call(env) end def recover(env) env[:recover] = true end end # Make a proc that just calls "abort" which raises a # SystemExit exception. error_proc = Proc.new { abort } instance = described_class.new([Action, error_proc], data) # The SystemExit should come through expect { instance.call(data) }.to raise_error(SystemExit) # The recover should not have been called data.has_key?(:recover).should_not be end end vagrant-1.4.3/test/unit/vagrant/batch_action_test.rb000066400000000000000000000016751226132634600226010ustar00rootroot00000000000000require 'thread' require File.expand_path("../../base", __FILE__) describe Vagrant::BatchAction do let(:called_actions) { [] } let!(:lock) { Mutex.new } let(:provider_name) { "test" } let(:provider_options) { {} } def new_machine(options) double("machine").tap do |m| m.stub(:provider_name => provider_name) m.stub(:provider_options => options) m.stub(:action) do |action, opts| lock.synchronize do called_actions << [m, action, opts] end end end end describe "#run" do let(:machine) { new_machine(provider_options) } let(:machine2) { new_machine(provider_options) } it "should run the actions on the machines in order" do subject.action(machine, "up") subject.action(machine2, "destroy") subject.run called_actions.include?([machine, "up", nil]).should be called_actions.include?([machine2, "destroy", nil]).should be end end end vagrant-1.4.3/test/unit/vagrant/box_collection_test.rb000066400000000000000000000161511226132634600231610ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "pathname" require 'tempfile' describe Vagrant::BoxCollection do include_context "unit" let(:box_class) { Vagrant::Box } let(:environment) { isolated_environment } let(:instance) { described_class.new(environment.boxes_dir) } it "should tell us the directory it is using" do instance.directory.should == environment.boxes_dir end describe "adding" do it "should add a valid box to the system" do box_path = environment.box2_file(:virtualbox) # Add the box box = instance.add(box_path, "foo", :virtualbox) box.should be_kind_of(box_class) box.name.should == "foo" box.provider.should == :virtualbox # Verify we can find it as well box = instance.find("foo", :virtualbox) box.should_not be_nil end it "should add a box without specifying a provider" do box_path = environment.box2_file(:vmware) # Add the box box = instance.add(box_path, "foo") box.should be_kind_of(box_class) box.name.should == "foo" box.provider.should == :vmware end it "should add a V1 box" do # Create a V1 box. box_path = environment.box1_file # Add the box box = instance.add(box_path, "foo") box.should be_kind_of(box_class) box.name.should == "foo" box.provider.should == :virtualbox end it "should raise an exception if the box already exists" do prev_box_name = "foo" prev_box_provider = :virtualbox # Create the box we're adding environment.box2(prev_box_name, prev_box_provider) # Attempt to add the box with the same name box_path = environment.box2_file(prev_box_provider) expect { instance.add(box_path, prev_box_name, prev_box_provider) }. to raise_error(Vagrant::Errors::BoxAlreadyExists) end it "should replace the box if force is specified" do prev_box_name = "foo" prev_box_provider = :vmware # Setup the environment with the box pre-added environment.box2(prev_box_name, prev_box_provider) # Attempt to add the box with the same name box_path = environment.box2_file(prev_box_provider, metadata: { "replaced" => "yes" }) box = instance.add(box_path, prev_box_name, nil, true) box.metadata["replaced"].should == "yes" end it "should raise an exception if the box already exists and no provider is given" do # Create some box file box_name = "foo" box_path = environment.box2_file(:vmware) # Add it once, successfully expect { instance.add(box_path, box_name) }.to_not raise_error # Add it again, and fail! expect { instance.add(box_path, box_name) }. to raise_error(Vagrant::Errors::BoxAlreadyExists) end it "should raise an exception if you're attempting to add a box that exists as a V1 box" do prev_box_name = "foo" # Create the V1 box environment.box1(prev_box_name) # Attempt to add some V2 box with the same name box_path = environment.box2_file(:vmware) expect { instance.add(box_path, prev_box_name) }. to raise_error(Vagrant::Errors::BoxUpgradeRequired) end it "should raise an exception and not add the box if the provider doesn't match" do box_name = "foo" good_provider = :virtualbox bad_provider = :vmware # Create a VirtualBox box file box_path = environment.box2_file(good_provider) # Add the box but with an invalid provider, verify we get the proper # error. expect { instance.add(box_path, box_name, bad_provider) }. to raise_error(Vagrant::Errors::BoxProviderDoesntMatch) # Verify the box doesn't exist instance.find(box_name, bad_provider).should be_nil end it "should raise an exception if you add an invalid box file" do # Tar Header information CHECKSUM_OFFSET = 148 CHECKSUM_LENGTH = 8 f = Tempfile.new(['vagrant_testing', '.tar']) begin # Corrupt the tar by writing over the checksum field f.seek(CHECKSUM_OFFSET) f.write("\0"*CHECKSUM_LENGTH) f.close expect { instance.add(f.path, "foo", :virtualbox) }. to raise_error(Vagrant::Errors::BoxUnpackageFailure) ensure f.close f.unlink end end end describe "listing all" do it "should return an empty array when no boxes are there" do instance.all.should == [] end it "should return the boxes and their providers" do # Create some boxes environment.box2("foo", :virtualbox) environment.box2("foo", :vmware) environment.box2("bar", :ec2) # Verify some output results = instance.all results.length.should == 3 results.include?(["foo", :virtualbox]).should be results.include?(["foo", :vmware]).should be results.include?(["bar", :ec2]).should be end it "should return V1 boxes as well" do # Create some boxes, including a V1 box environment.box1("bar") environment.box2("foo", :vmware) # Verify some output results = instance.all.sort results.should == [["bar", :virtualbox, :v1], ["foo", :vmware]] end it 'does not raise an exception when a file appears in the boxes dir' do Tempfile.new('a_file', environment.boxes_dir) expect { instance.all }.to_not raise_error end end describe "finding" do it "should return nil if the box does not exist" do instance.find("foo", :i_dont_exist).should be_nil end it "should return a box if the box does exist" do # Create the "box" environment.box2("foo", :virtualbox) # Actual test result = instance.find("foo", :virtualbox) result.should_not be_nil result.should be_kind_of(box_class) result.name.should == "foo" end it "should throw an exception if it is a v1 box" do # Create a V1 box environment.box1("foo") # Test! expect { instance.find("foo", :virtualbox) }. to raise_error(Vagrant::Errors::BoxUpgradeRequired) end it "should return nil if there is a V1 box but we're looking for another provider" do # Create a V1 box environment.box1("foo") # Test instance.find("foo", :another_provider).should be_nil end end describe "upgrading" do it "should upgrade a V1 box to V2" do # Create a V1 box environment.box1("foo") # Verify that only a V1 box exists expect { instance.find("foo", :virtualbox) }. to raise_error(Vagrant::Errors::BoxUpgradeRequired) # Upgrade the box instance.upgrade("foo").should be # Verify the box exists box = instance.find("foo", :virtualbox) box.should_not be_nil box.name.should == "foo" end it "should raise a BoxNotFound exception if a non-existent box is upgraded" do expect { instance.upgrade("i-dont-exist") }. to raise_error(Vagrant::Errors::BoxNotFound) end it "should return true if we try to upgrade a V2 box" do # Create a V2 box environment.box2("foo", :vmware) instance.upgrade("foo").should be end end end vagrant-1.4.3/test/unit/vagrant/box_test.rb000066400000000000000000000067411226132634600207520ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "pathname" describe Vagrant::Box do include_context "unit" let(:environment) { isolated_environment } let(:box_collection) { Vagrant::BoxCollection.new(environment.boxes_dir) } let(:name) { "foo" } let(:provider) { :virtualbox } let(:directory) { environment.box2("foo", :virtualbox) } let(:instance) { described_class.new(name, provider, directory) } subject { described_class.new(name, provider, directory) } it "provides the name" do instance.name.should == name end it "provides the provider" do instance.provider.should == provider end it "provides the directory" do instance.directory.should == directory end it "provides the metadata associated with a box" do data = { "foo" => "bar" } # Write the metadata directory.join("metadata.json").open("w") do |f| f.write(JSON.generate(data)) end # Verify the metadata instance.metadata.should == data end context "with a corrupt metadata file" do before do directory.join("metadata.json").open("w") do |f| f.write("") end end it "should raise an exception" do expect { subject }. to raise_error(Vagrant::Errors::BoxMetadataCorrupted) end end context "without a metadata file" do before :each do directory.join("metadata.json").delete end it "should raise an exception" do expect { subject }. to raise_error(Vagrant::Errors::BoxMetadataFileNotFound) end end describe "destroying" do it "should destroy an existing box" do # Verify that our "box" exists directory.exist?.should be # Destroy it instance.destroy!.should be # Verify that it is "destroyed" directory.exist?.should_not be end it "should not error destroying a non-existent box" do # Get the instance so that it is instantiated box = instance # Delete the directory directory.rmtree # Destroy it box.destroy!.should be end end describe "repackaging" do it "should repackage the box" do test_file_contents = "hello, world!" # Put a file in the box directory to verify it is packaged properly # later. directory.join("test_file").open("w") do |f| f.write(test_file_contents) end # Repackage our box to some temporary directory box_output_path = temporary_dir.join("package.box") instance.repackage(box_output_path).should be # Let's now add this box again under a different name, and then # verify that we get the proper result back. new_box = box_collection.add(box_output_path, "foo2") new_box.directory.join("test_file").read.should == test_file_contents end end describe "comparison and ordering" do it "should be equal if the name and provider match" do a = described_class.new("a", :foo, directory) b = described_class.new("a", :foo, directory) a.should == b end it "should not be equal if the name and provider do not match" do a = described_class.new("a", :foo, directory) b = described_class.new("b", :foo, directory) a.should_not == b end it "should sort them in order of name then provider" do a = described_class.new("a", :foo, directory) b = described_class.new("b", :foo, directory) c = described_class.new("c", :foo2, directory) [c, a, b].sort.should == [a, b, c] end end end vagrant-1.4.3/test/unit/vagrant/cli_test.rb000066400000000000000000000014201226132634600207160ustar00rootroot00000000000000describe Vagrant::CLI do describe "parsing options" do let(:klass) do Class.new(described_class) end let(:environment) do ui = double("UI::Silent") ui.stub(:machine => "bar") ui.stub(:info => "bar") env = double("Vagrant::Environment") env.stub(:active_machines => []) env.stub(:ui => ui) env.stub(:root_path => "foo") env.stub(:machine_names => []) env end it "returns a non-zero exit status if an invalid command is given" do result = klass.new(["destroypp"], environment).execute result.should_not == 0 end it "returns an exit status of zero if a valid command is given" do result = klass.new(["destroy"], environment).execute result.should == 0 end end end vagrant-1.4.3/test/unit/vagrant/config/000077500000000000000000000000001226132634600200335ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/config/loader_test.rb000066400000000000000000000113671226132634600226750ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/registry" describe Vagrant::Config::Loader do include_context "unit" # This is the current version of configuration for the tests. let(:current_version) { version_order.last } # This is just a dummy implementation of a configuraiton loader which # simply acts on hashes. let(:test_loader) do Class.new(Vagrant::Config::VersionBase) do def self.init {} end def self.load(proc) init.tap do |obj| proc.call(obj) end end def self.merge(old, new) old.merge(new) end end end let(:versions) do Vagrant::Registry.new.tap do |r| r.register("1") { test_loader } end end let(:version_order) { ["1"] } let(:instance) { described_class.new(versions, version_order) } describe "basic loading" do it "should ignore non-existent load order keys" do instance.load([:foo]) end it "should load and return the configuration" do proc = Proc.new do |config| config[:foo] = "yep" end instance.set(:proc, [[current_version, proc]]) config, warnings, errors = instance.load([:proc]) config[:foo].should == "yep" warnings.should == [] errors.should == [] end end describe "finalization" do it "should finalize the configuration" do # Create the finalize method on our loader def test_loader.finalize(obj) obj[:finalized] = true obj end # Basic configuration proc proc = lambda do |config| config[:foo] = "yep" end # Run the actual configuration and assert that we get the proper result instance.set(:proc, [[current_version, proc]]) config, _ = instance.load([:proc]) config[:foo].should == "yep" config[:finalized].should == true end end describe "upgrading" do it "should do an upgrade to the latest version" do test_loader_v2 = Class.new(test_loader) do def self.upgrade(old) new = old.dup new[:v2] = true [new, [], []] end end versions.register("2") { test_loader_v2 } version_order << "2" # Load a version 1 proc, and verify it is upgraded to version 2 proc = lambda { |config| config[:foo] = "yep" } instance.set(:proc, [["1", proc]]) config, _ = instance.load([:proc]) config[:foo].should == "yep" config[:v2].should == true end it "should keep track of warnings and errors" do test_loader_v2 = Class.new(test_loader) do def self.upgrade(old) new = old.dup new[:v2] = true [new, ["foo!"], ["bar!"]] end end versions.register("2") { test_loader_v2 } version_order << "2" # Load a version 1 proc, and verify it is upgraded to version 2 proc = lambda { |config| config[:foo] = "yep" } instance.set(:proc, [["1", proc]]) config, warnings, errors = instance.load([:proc]) config[:foo].should == "yep" config[:v2].should == true warnings.should == ["foo!"] errors.should == ["bar!"] end end describe "loading edge cases" do it "should only run the same proc once" do count = 0 proc = Proc.new do |config| config[:foo] = "yep" count += 1 end instance.set(:proc, [[current_version, proc]]) 5.times do result, _ = instance.load([:proc]) # Verify the config result result[:foo].should == "yep" # Verify the count is only one count.should == 1 end end it "should only load configuration files once" do $_config_data = 0 # We test both setting a file multiple times as well as multiple # loads, since both should not cache the data. file = temporary_file("$_config_data += 1") 5.times { instance.set(:file, file) } 5.times { instance.load([:file]) } $_config_data.should == 1 end it "should not clear the cache if setting to the same value multiple times" do $_config_data = 0 file = temporary_file("$_config_data += 1") instance.set(:proc, file) 5.times { instance.load([:proc]) } instance.set(:proc, file) 5.times { instance.load([:proc]) } $_config_data.should == 1 end it "should raise proper error if there is a syntax error in a Vagrantfile" do expect { instance.set(:file, temporary_file("Vagrant:^Config")) }. to raise_exception(Vagrant::Errors::VagrantfileSyntaxError) end it "should raise a proper error if there is a problem with the Vagrantfile" do expect { instance.set(:file, temporary_file("foo")) }. to raise_exception(Vagrant::Errors::VagrantfileLoadError) end end end vagrant-1.4.3/test/unit/vagrant/config/v1/000077500000000000000000000000001226132634600203615ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/config/v1/dummy_config_test.rb000066400000000000000000000011271226132634600244260ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V1::DummyConfig do it "should allow attribute setting" do expect { subject.foo = :bar }. to_not raise_error end it "should allow method calls that return more DummyConfigs" do subject.foo.should be_kind_of(described_class) end it "should allow hash access" do expect { subject[:foo] }. to_not raise_error subject[:foo].should be_kind_of(described_class) end it "should allow setting hash values" do expect { subject[:foo] = :bar }. to_not raise_error end end vagrant-1.4.3/test/unit/vagrant/config/v1/loader_test.rb000066400000000000000000000076231226132634600232230ustar00rootroot00000000000000require "ostruct" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V1::Loader do include_context "unit" before(:each) do # Force the V1 loader to believe that we are in V1 stub_const("Vagrant::Config::CURRENT_VERSION", "1") end describe "empty" do it "returns an empty configuration object" do result = described_class.init result.should be_kind_of(Vagrant::Config::V1::Root) end it "returns an object with all configuration keys loaded if V1" do # Make sure we're version 1 stub_const("Vagrant::Config::CURRENT_VERSION", "1") # Register some config classes. register_plugin("1") do |p| p.config("foo") { OpenStruct } p.config("bar", true) { OpenStruct } end # Test that we have all keys result = described_class.init result.foo.should be_kind_of(OpenStruct) result.bar.should be_kind_of(OpenStruct) end it "returns only upgradable config objects if not V1" do # Make sure we're NOT version 1 stub_const("Vagrant::Config::CURRENT_VERSION", "2") # Register some config classes. register_plugin("1") do |p| p.config("foo") { OpenStruct } p.config("bar", true) { OpenStruct } end # Test that we have all keys result = described_class.init result.bar.should be_kind_of(OpenStruct) end end describe "finalizing" do it "should call `#finalize` on the configuration object" do # Register a plugin for our test register_plugin("1") do |plugin| plugin.config "foo" do Class.new do attr_accessor :bar def finalize! @bar = "finalized" end end end end # Create the proc we're testing config_proc = Proc.new do |config| config.foo.bar = "value" end # Test that it works properly config = described_class.load(config_proc) config.foo.bar.should == "value" # Finalize it described_class.finalize(config) config.foo.bar.should == "finalized" end end describe "loading" do it "should configure with all plugin config keys loaded" do # Register a plugin for our test register_plugin("1") do |plugin| plugin.config("foo") { OpenStruct } end # Create the proc we're testing config_proc = Proc.new do |config| config.foo.bar = "value" end # Test that it works properly config = described_class.load(config_proc) config.foo.bar.should == "value" end end describe "merging" do it "should merge available configuration keys" do old = Vagrant::Config::V1::Root.new({ :foo => Object }) new = Vagrant::Config::V1::Root.new({ :bar => Object }) result = described_class.merge(old, new) result.foo.should be_kind_of(Object) result.bar.should be_kind_of(Object) end it "should merge instantiated objects" do config_class = Class.new do attr_accessor :value end old = Vagrant::Config::V1::Root.new({ :foo => config_class }) old.foo.value = "old" new = Vagrant::Config::V1::Root.new({ :bar => config_class }) new.bar.value = "new" result = described_class.merge(old, new) result.foo.value.should == "old" result.bar.value.should == "new" end it "should merge conflicting classes by calling `merge`" do config_class = Class.new do attr_accessor :value def merge(new) result = self.class.new result.value = @value + new.value result end end old = Vagrant::Config::V1::Root.new({ :foo => config_class }) old.foo.value = 10 new = Vagrant::Config::V1::Root.new({ :foo => config_class }) new.foo.value = 15 result = described_class.merge(old, new) result.foo.value.should == 25 end end end vagrant-1.4.3/test/unit/vagrant/config/v1/root_test.rb000066400000000000000000000020621226132634600227300ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V1::Root do include_context "unit" it "should provide access to config objects" do foo_class = Class.new map = { :foo => foo_class } instance = described_class.new(map) foo = instance.foo foo.should be_kind_of(foo_class) instance.foo.should eql(foo) end it "can be created with initial state" do instance = described_class.new({}, { :foo => "bar" }) instance.foo.should == "bar" end it "should return internal state" do map = { "foo" => Object, "bar" => Object } instance = described_class.new(map) instance.__internal_state.should == { "config_map" => map, "keys" => {}, "missing_key_calls" => Set.new } end it "should record missing key calls" do instance = described_class.new({}) instance.foo.bar = false keys = instance.__internal_state["missing_key_calls"] keys.should be_kind_of(Set) keys.length.should == 1 keys.include?("foo").should be end end vagrant-1.4.3/test/unit/vagrant/config/v2/000077500000000000000000000000001226132634600203625ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/config/v2/dummy_config_test.rb000066400000000000000000000011271226132634600244270ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V2::DummyConfig do it "should allow attribute setting" do expect { subject.foo = :bar }. to_not raise_error end it "should allow method calls that return more DummyConfigs" do subject.foo.should be_kind_of(described_class) end it "should allow hash access" do expect { subject[:foo] }. to_not raise_error subject[:foo].should be_kind_of(described_class) end it "should allow setting hash values" do expect { subject[:foo] = :bar }. to_not raise_error end end vagrant-1.4.3/test/unit/vagrant/config/v2/loader_test.rb000066400000000000000000000077221226132634600232240ustar00rootroot00000000000000require "ostruct" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V2::Loader do include_context "unit" before(:each) do # Force the V2 loader to believe that we are in V2 stub_const("Vagrant::Config::CURRENT_VERSION", "2") end describe "empty" do it "returns an empty configuration object" do result = described_class.init result.should be_kind_of(Vagrant::Config::V2::Root) end end describe "finalizing" do it "should call `#finalize` on the configuration object" do # Register a plugin for our test register_plugin("2") do |plugin| plugin.config "foo" do Class.new do attr_accessor :bar def finalize! @bar = "finalized" end end end end # Create the proc we're testing config_proc = Proc.new do |config| config.foo.bar = "value" end # Test that it works properly config = described_class.load(config_proc) config.foo.bar.should == "value" # Finalize it described_class.finalize(config) config.foo.bar.should == "finalized" end end describe "loading" do it "should configure with all plugin config keys loaded" do # Register a plugin for our test register_plugin("2") do |plugin| plugin.config("foo") { OpenStruct } end # Create the proc we're testing config_proc = Proc.new do |config| config.foo.bar = "value" end # Test that it works properly config = described_class.load(config_proc) config.foo.bar.should == "value" end end describe "merging" do it "should merge available configuration keys" do old = Vagrant::Config::V2::Root.new({ :foo => Object }) new = Vagrant::Config::V2::Root.new({ :bar => Object }) result = described_class.merge(old, new) result.foo.should be_kind_of(Object) result.bar.should be_kind_of(Object) end it "should merge instantiated objects" do config_class = Class.new do attr_accessor :value end old = Vagrant::Config::V2::Root.new({ :foo => config_class }) old.foo.value = "old" new = Vagrant::Config::V2::Root.new({ :bar => config_class }) new.bar.value = "new" result = described_class.merge(old, new) result.foo.value.should == "old" result.bar.value.should == "new" end it "should merge conflicting classes by calling `merge`" do config_class = Class.new do attr_accessor :value def merge(new) result = self.class.new result.value = @value + new.value result end end old = Vagrant::Config::V2::Root.new({ :foo => config_class }) old.foo.value = 10 new = Vagrant::Config::V2::Root.new({ :foo => config_class }) new.foo.value = 15 result = described_class.merge(old, new) result.foo.value.should == 25 end end describe "upgrading" do it "should continue fine if the key doesn't implement upgrade" do # Make an old V1 root object old = Vagrant::Config::V1::Root.new({ :foo => Class.new }) # It should work fine expect { result = described_class.upgrade(old) }.to_not raise_error end it "should upgrade the config if it implements the upgrade method" do # Create the old V1 class that will be upgraded config_class = Class.new do attr_accessor :value def upgrade(new) new.foo.value = value * 2 [["foo"], ["bar"]] end end # Create the new V2 plugin it is writing to register_plugin("2") do |p| p.config("foo") { OpenStruct } end # Test it out! old = Vagrant::Config::V1::Root.new({ :foo => config_class }) old.foo.value = 5 data = described_class.upgrade(old) data[0].foo.value.should == 10 data[1].should == ["foo"] data[2].should == ["bar"] end end end vagrant-1.4.3/test/unit/vagrant/config/v2/root_test.rb000066400000000000000000000054131226132634600227340ustar00rootroot00000000000000require "set" require File.expand_path("../../../../base", __FILE__) describe Vagrant::Config::V2::Root do include_context "unit" it "should provide access to config objects" do foo_class = Class.new map = { :foo => foo_class } instance = described_class.new(map) foo = instance.foo foo.should be_kind_of(foo_class) instance.foo.should eql(foo) end it "record a missing key call if invalid key used" do instance = described_class.new({}) expect { instance.foo }.to_not raise_error instance.__internal_state["missing_key_calls"].include?("foo").should be end it "returns a dummy config for a missing key" do instance = described_class.new({}) expect { instance.foo.foo = "bar" }.to_not raise_error end it "can be created with initial state" do instance = described_class.new({}, { :foo => "bar" }) instance.foo.should == "bar" end it "should return internal state" do map = { "foo" => Object, "bar" => Object } instance = described_class.new(map) instance.__internal_state.should == { "config_map" => map, "keys" => {}, "missing_key_calls" => Set.new } end describe "finalization" do it "should finalize un-used keys" do foo_class = Class.new do attr_accessor :foo def finalize! @foo = "SET" end end map = { :foo => foo_class } instance = described_class.new(map) instance.finalize! instance.foo.foo.should == "SET" end end describe "validation" do let(:instance) do map = { :foo => Object, :bar => Object } described_class.new(map) end it "should return nil if valid" do instance.validate({}).should == {} end it "should return errors if invalid" do errors = { "foo" => ["errors!"] } env = { "errors" => errors } foo = instance.foo def foo.validate(env) env["errors"] end instance.validate(env).should == errors end it "should merge errors via array concat if matching keys" do errors = { "foo" => ["errors!"] } env = { "errors" => errors } foo = instance.foo bar = instance.bar def foo.validate(env) env["errors"] end def bar.validate(env) env["errors"].merge({ "bar" => ["bar"] }) end expected_errors = { "foo" => ["errors!", "errors!"], "bar" => ["bar"] } instance.validate(env).should == expected_errors end it "shouldn't count empty keys" do errors = { "foo" => [] } env = { "errors" => errors } foo = instance.foo def foo.validate(env) env["errors"] end instance.validate(env).should == {} end end end vagrant-1.4.3/test/unit/vagrant/config/v2/util_test.rb000066400000000000000000000010611226132634600227210ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require "vagrant/config/v2/util" describe Vagrant::Config::V2::Util do describe "merging errors" do it "should merge matching keys and leave the rest alone" do first = { "one" => ["foo"], "two" => ["two"] } second = { "one" => ["bar"], "three" => ["three"] } expected = { "one" => ["foo", "bar"], "two" => ["two"], "three" => ["three"] } result = described_class.merge_errors(first, second) result.should == expected end end end vagrant-1.4.3/test/unit/vagrant/config_test.rb000066400000000000000000000041421226132634600214200ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe Vagrant::Config do it "should not execute the proc on configuration" do described_class.run do raise Exception, "Failure." end end it "should capture calls to `Vagrant.configure`" do receiver = double() procs = described_class.capture_configures do Vagrant.configure("1") do receiver.one end Vagrant.configure("2") do receiver.two end end procs.should be_kind_of(Array) procs.length.should == 2 procs[0][0].should == "1" procs[1][0].should == "2" # Verify the proper procs were captured receiver.should_receive(:one).once.ordered receiver.should_receive(:two).once.ordered procs[0][1].call procs[1][1].call end it "should work with integer configurations "do receiver = double() procs = described_class.capture_configures do Vagrant.configure(1) do receiver.one end Vagrant.configure(2) do receiver.two end end procs.should be_kind_of(Array) procs.length.should == 2 procs[0][0].should == "1" procs[1][0].should == "2" # Verify the proper procs were captured receiver.should_receive(:one).once.ordered receiver.should_receive(:two).once.ordered procs[0][1].call procs[1][1].call end it "should capture configuration procs" do receiver = double() procs = described_class.capture_configures do described_class.run do receiver.hello! end end # Verify the structure of the result procs.should be_kind_of(Array) procs.length.should == 1 # Verify that the proper proc was captured receiver.should_receive(:hello!).once procs[0][0].should == "1" procs[0][1].call end it "should capture the proper version" do procs = described_class.capture_configures do described_class.run("1") {} described_class.run("2") {} end # Verify the structure of the result procs.should be_kind_of(Array) procs.length.should == 2 procs[0][0].should == "1" procs[1][0].should == "2" end end vagrant-1.4.3/test/unit/vagrant/environment_test.rb000066400000000000000000000505561226132634600225310ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) require "json" require "pathname" require "tempfile" require "tmpdir" require "vagrant/util/file_mode" describe Vagrant::Environment do include_context "unit" let(:env) do isolated_environment.tap do |e| e.box2("base", :virtualbox) e.vagrantfile <<-VF Vagrant.configure("2") do |config| config.vm.box = "base" end VF end end let(:instance) { env.create_vagrant_env } subject { instance } describe "active machines" do it "should be empty if the machines folder doesn't exist" do folder = instance.local_data_path.join("machines") folder.should_not be_exist instance.active_machines.should be_empty end it "should return the name and provider of active machines" do machines = instance.local_data_path.join("machines") # Valid machine, with "foo" and virtualbox machine_foo = machines.join("foo/virtualbox") machine_foo.mkpath machine_foo.join("id").open("w+") { |f| f.write("") } # Invalid machine (no ID) machine_bar = machines.join("bar/virtualbox") machine_bar.mkpath instance.active_machines.should == [[:foo, :virtualbox]] end end describe "batching" do let(:batch) do double("batch") do |b| b.stub(:run) end end context "without the disabling env var" do it "should run without disabling parallelization" do with_temp_env("VAGRANT_NO_PARALLEL" => nil) do Vagrant::BatchAction.should_receive(:new).with(true).and_return(batch) batch.should_receive(:run) instance.batch {} end end it "should run with disabling parallelization if explicit" do with_temp_env("VAGRANT_NO_PARALLEL" => nil) do Vagrant::BatchAction.should_receive(:new).with(false).and_return(batch) batch.should_receive(:run) instance.batch(false) {} end end end context "with the disabling env var" do it "should run with disabling parallelization" do with_temp_env("VAGRANT_NO_PARALLEL" => "yes") do Vagrant::BatchAction.should_receive(:new).with(false).and_return(batch) batch.should_receive(:run) instance.batch {} end end end end describe "current working directory" do it "is the cwd by default" do Dir.mktmpdir do |temp_dir| Dir.chdir(temp_dir) do with_temp_env("VAGRANT_CWD" => nil) do described_class.new.cwd.should == Pathname.new(Dir.pwd) end end end end it "is set to the cwd given" do Dir.mktmpdir do |directory| instance = described_class.new(:cwd => directory) instance.cwd.should == Pathname.new(directory) end end it "is set to the environmental variable VAGRANT_CWD" do Dir.mktmpdir do |directory| instance = with_temp_env("VAGRANT_CWD" => directory) do described_class.new end instance.cwd.should == Pathname.new(directory) end end it "raises an exception if the CWD doesn't exist" do expect { described_class.new(:cwd => "doesntexist") }. to raise_error(Vagrant::Errors::EnvironmentNonExistentCWD) end end describe "default provider" do it "is virtualbox without any environmental variable" do with_temp_env("VAGRANT_DEFAULT_PROVIDER" => nil) do subject.default_provider.should == :virtualbox end end it "is whatever the environmental variable is if set" do with_temp_env("VAGRANT_DEFAULT_PROVIDER" => "foo") do subject.default_provider.should == :foo end end end describe "home path" do it "is set to the home path given" do Dir.mktmpdir do |dir| instance = described_class.new(:home_path => dir) instance.home_path.should == Pathname.new(dir) end end it "is set to the environmental variable VAGRANT_HOME" do Dir.mktmpdir do |dir| instance = with_temp_env("VAGRANT_HOME" => dir) do described_class.new end instance.home_path.should == Pathname.new(dir) end end context "default home path" do before :each do Vagrant::Util::Platform.stub(:windows? => false) end it "is set to '~/.vagrant.d' by default" do expected = Pathname.new(File.expand_path("~/.vagrant.d")) described_class.new.home_path.should == expected end it "is set to '~/.vagrant.d' if on Windows but no USERPROFILE" do Vagrant::Util::Platform.stub(:windows? => true) expected = Pathname.new(File.expand_path("~/.vagrant.d")) with_temp_env("USERPROFILE" => nil) do described_class.new.home_path.should == expected end end it "is set to '%USERPROFILE%/.vagrant.d' if on Windows and USERPROFILE is set" do Vagrant::Util::Platform.stub(:windows? => true) Dir.mktmpdir do |dir| expected = Pathname.new(File.expand_path("#{dir}/.vagrant.d")) with_temp_env("USERPROFILE" => dir) do described_class.new.home_path.should == expected end end end end it "throws an exception if inaccessible" do expect { described_class.new(:home_path => "/") }.to raise_error(Vagrant::Errors::HomeDirectoryNotAccessible) end end describe "local data path" do it "is set to the proper default" do default = instance.root_path.join(described_class::DEFAULT_LOCAL_DATA) instance.local_data_path.should == default end it "is expanded relative to the cwd" do instance = described_class.new(:local_data_path => "foo") instance.local_data_path.should == instance.cwd.join("foo") end it "is set to the given value" do Dir.mktmpdir do |dir| instance = described_class.new(:local_data_path => dir) instance.local_data_path.to_s.should == dir end end describe "upgrading V1 dotfiles" do let(:v1_dotfile_tempfile) { Tempfile.new("vagrant") } let(:v1_dotfile) { Pathname.new(v1_dotfile_tempfile.path) } let(:local_data_path) { v1_dotfile_tempfile.path } let(:instance) { described_class.new(:local_data_path => local_data_path) } it "should be fine if dotfile is empty" do v1_dotfile.open("w+") do |f| f.write("") end expect { instance }.to_not raise_error Pathname.new(local_data_path).should be_directory end it "should upgrade all active VMs" do active_vms = { "foo" => "foo_id", "bar" => "bar_id" } v1_dotfile.open("w+") do |f| f.write(JSON.dump({ "active" => active_vms })) end expect { instance }.to_not raise_error local_data_pathname = Pathname.new(local_data_path) foo_id_file = local_data_pathname.join("machines/foo/virtualbox/id") foo_id_file.should be_file foo_id_file.read.should == "foo_id" bar_id_file = local_data_pathname.join("machines/bar/virtualbox/id") bar_id_file.should be_file bar_id_file.read.should == "bar_id" end it "should raise an error if invalid JSON" do v1_dotfile.open("w+") do |f| f.write("bad") end expect { instance }. to raise_error(Vagrant::Errors::DotfileUpgradeJSONError) end end end describe "copying the private SSH key" do it "copies the SSH key into the home directory" do env = isolated_environment instance = described_class.new(:home_path => env.homedir) pk = env.homedir.join("insecure_private_key") pk.should be_exist Vagrant::Util::FileMode.from_octal(pk.stat.mode).should == "600" end end it "has a box collection pointed to the proper directory" do collection = instance.boxes collection.should be_kind_of(Vagrant::BoxCollection) collection.directory.should == instance.boxes_path end describe "action runner" do it "has an action runner" do instance.action_runner.should be_kind_of(Vagrant::Action::Runner) end it "has a `ui` in the globals" do result = nil callable = lambda { |env| result = env[:ui] } instance.action_runner.run(callable) result.should eql(instance.ui) end end describe "#hook" do it "should call the action runner with the proper hook" do hook_name = :foo instance.action_runner.should_receive(:run).with do |callable, env| env[:action_name].should == hook_name end instance.hook(hook_name) end it "should return the result of the action runner run" do instance.action_runner.should_receive(:run).and_return(:foo) instance.hook(:bar).should == :foo end it "should allow passing in a custom action runner" do instance.action_runner.should_not_receive(:run) other_runner = double("runner") other_runner.should_receive(:run).and_return(:foo) instance.hook(:bar, runner: other_runner).should == :foo end it "should allow passing in custom data" do instance.action_runner.should_receive(:run).with do |callable, env| env[:foo].should == :bar end instance.hook(:foo, foo: :bar) end it "should allow passing a custom callable" do instance.action_runner.should_receive(:run).with do |callable, env| callable.should == :what end instance.hook(:foo, callable: :what) end end describe "primary machine name" do it "should be the only machine if not a multi-machine environment" do instance.primary_machine_name.should == instance.machine_names.first end it "should be the machine marked as the primary" do environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define :foo config.vm.define :bar, :primary => true end VF env.box2("base", :virtualbox) end env = environment.create_vagrant_env env.primary_machine_name.should == :bar end it "should be nil if no primary is specified in a multi-machine environment" do environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define :foo config.vm.define :bar end VF env.box2("base", :virtualbox) end env = environment.create_vagrant_env env.primary_machine_name.should be_nil end end describe "loading configuration" do it "should load global configuration" do environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.ssh.port = 200 end VF end env = environment.create_vagrant_env env.config_global.ssh.port.should == 200 end it "should load from a custom Vagrantfile" do environment = isolated_environment do |env| env.file("non_standard_name", <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 200 end VF end env = environment.create_vagrant_env(:vagrantfile_name => "non_standard_name") env.config_global.ssh.port.should == 200 end it "should load from a custom Vagrantfile specified by env var" do environment = isolated_environment do |env| env.file("some_other_name", <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 400 end VF end env = with_temp_env("VAGRANT_VAGRANTFILE" => "some_other_name") do environment.create_vagrant_env end env.config_global.ssh.port.should == 400 end end describe "ui" do it "should be a silent UI by default" do described_class.new.ui.should be_kind_of(Vagrant::UI::Silent) end it "should be a UI given in the constructor" do # Create a custom UI for our test class CustomUI < Vagrant::UI::Interface; end instance = described_class.new(:ui_class => CustomUI) instance.ui.should be_kind_of(CustomUI) end end describe "#unload" do it "should run the unload hook" do instance.should_receive(:hook).with(:environment_unload).once instance.unload end end describe "getting a machine" do # A helper to register a provider for use in tests. def register_provider(name, config_class=nil, options=nil) provider_cls = Class.new(Vagrant.plugin("2", :provider)) register_plugin("2") do |p| p.provider(name, options) { provider_cls } if config_class p.config(name, :provider) { config_class } end end provider_cls end it "should return a machine object with the correct provider" do # Create a provider foo_provider = register_provider("foo") # Create the configuration isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define "foo" end VF e.box2("base", :foo) end # Verify that we can get the machine env = isolated_env.create_vagrant_env machine = env.machine(:foo, :foo) machine.should be_kind_of(Vagrant::Machine) machine.name.should == :foo machine.provider.should be_kind_of(foo_provider) machine.provider_config.should be_nil end it "should return a machine object with the machine configuration" do # Create a provider foo_config = Class.new(Vagrant.plugin("2", :config)) do attr_accessor :value end foo_provider = register_provider("foo", foo_config) # Create the configuration isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define "foo" config.vm.provider :foo do |fooconfig| fooconfig.value = 100 end end VF e.box2("base", :foo) end # Verify that we can get the machine env = isolated_env.create_vagrant_env machine = env.machine(:foo, :foo) machine.should be_kind_of(Vagrant::Machine) machine.name.should == :foo machine.provider.should be_kind_of(foo_provider) machine.provider_config.value.should == 100 end it "should cache the machine objects by name and provider" do # Create a provider foo_provider = register_provider("foo") bar_provider = register_provider("bar") # Create the configuration isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" config.vm.define "vm1" config.vm.define "vm2" end VF e.box2("base", :foo) e.box2("base", :bar) end env = isolated_env.create_vagrant_env vm1_foo = env.machine(:vm1, :foo) vm1_bar = env.machine(:vm1, :bar) vm2_foo = env.machine(:vm2, :foo) vm1_foo.should eql(env.machine(:vm1, :foo)) vm1_bar.should eql(env.machine(:vm1, :bar)) vm1_foo.should_not eql(vm1_bar) vm2_foo.should eql(env.machine(:vm2, :foo)) end it "should load a machine without a box" do register_provider("foo") environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "i-dont-exist" end VF end env = environment.create_vagrant_env machine = env.machine(:default, :foo) machine.box.should be_nil end it "should load the machine configuration" do register_provider("foo") environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.ssh.port = 1 config.vm.box = "base" config.vm.define "vm1" do |inner| inner.ssh.port = 100 end end VF env.box2("base", :foo) end env = environment.create_vagrant_env machine = env.machine(:vm1, :foo) machine.config.ssh.port.should == 100 machine.config.vm.box.should == "base" end it "should load the box configuration for a V2 box" do register_provider("foo") environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF env.box2("base", :foo, :vagrantfile => <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 100 end VF end env = environment.create_vagrant_env machine = env.machine(:default, :foo) machine.config.ssh.port.should == 100 end it "should load the box configuration for a V2 box and custom Vagrantfile name" do register_provider("foo") environment = isolated_environment do |env| env.file("some_other_name", <<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF env.box2("base", :foo, :vagrantfile => <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 100 end VF end env = with_temp_env("VAGRANT_VAGRANTFILE" => "some_other_name") do environment.create_vagrant_env end machine = env.machine(:default, :foo) machine.config.ssh.port.should == 100 end it "should load the box configuration for other formats for a V2 box" do register_provider("foo", nil, box_format: "bar") environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF env.box2("base", :bar, :vagrantfile => <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 100 end VF end env = environment.create_vagrant_env machine = env.machine(:default, :foo) machine.config.ssh.port.should == 100 end it "prefer sooner formats when multiple box formats are available" do register_provider("foo", nil, box_format: ["fA", "fB"]) environment = isolated_environment do |env| env.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF env.box2("base", :fA, :vagrantfile => <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 100 end VF env.box2("base", :fB, :vagrantfile => <<-VF) Vagrant.configure("2") do |config| config.ssh.port = 200 end VF end env = environment.create_vagrant_env machine = env.machine(:default, :foo) machine.config.ssh.port.should == 100 end it "should load the provider override if set" do register_provider("bar") register_provider("foo") isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "foo" config.vm.provider :foo do |_, c| c.vm.box = "bar" end end VF end env = isolated_env.create_vagrant_env foo_vm = env.machine(:default, :foo) bar_vm = env.machine(:default, :bar) foo_vm.config.vm.box.should == "bar" bar_vm.config.vm.box.should == "foo" end it "should reload the cache if refresh is set" do # Create a provider foo_provider = register_provider("foo") # Create the configuration isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.box = "base" end VF e.box2("base", :foo) end env = isolated_env.create_vagrant_env vm1 = env.machine(:default, :foo) vm2 = env.machine(:default, :foo, true) vm3 = env.machine(:default, :foo) vm1.should_not eql(vm2) vm2.should eql(vm3) end it "should raise an error if the VM is not found" do expect { instance.machine("i-definitely-dont-exist", :virtualbox) }. to raise_error(Vagrant::Errors::MachineNotFound) end it "should raise an error if the provider is not found" do expect { instance.machine(:default, :lol_no) }. to raise_error(Vagrant::Errors::ProviderNotFound) end end describe "getting machine names" do it "should return the default machine if no multi-VM is used" do # Create the config isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| end VF end env = isolated_env.create_vagrant_env env.machine_names.should == [:default] end it "should return the machine names in order" do # Create the config isolated_env = isolated_environment do |e| e.vagrantfile(<<-VF) Vagrant.configure("2") do |config| config.vm.define "foo" config.vm.define "bar" end VF end env = isolated_env.create_vagrant_env env.machine_names.should == [:foo, :bar] end end end vagrant-1.4.3/test/unit/vagrant/errors_test.rb000066400000000000000000000016231226132634600214700ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe Vagrant::Errors::VagrantError do describe "subclass with error key" do let(:klass) do Class.new(described_class) do error_key("test_key") end end subject { klass.new } it "should use the translation for the message" do subject.to_s.should == "test value" end its("status_code") { should eq(1) } end describe "passing error key through options" do subject { described_class.new(_key: "test_key") } it "should use the translation for the message" do subject.to_s.should == "test value" end end describe "subclass with error message" do let(:klass) do Class.new(described_class) do error_message("foo") end end subject { klass.new } it "should use the translation for the message" do subject.to_s.should == "foo" end end end vagrant-1.4.3/test/unit/vagrant/guest_test.rb000066400000000000000000000114471226132634600213100ustar00rootroot00000000000000require "pathname" require File.expand_path("../../base", __FILE__) describe Vagrant::Guest do include_context "unit" let(:capabilities) { {} } let(:guests) { {} } let(:machine) do double("machine").tap do |m| m.stub(:inspect => "machine") m.stub(:config => double("config")) m.config.stub(:vm => double("vm_config")) m.config.vm.stub(:guest => nil) end end subject { described_class.new(machine, guests, capabilities) } # This registers a capability with a specific guest def register_capability(guest, capability, options=nil) options ||= {} cap = Class.new do if !options[:corrupt] define_method(capability) do |*args| raise "cap: #{capability} #{args.inspect}" end end end capabilities[guest] ||= {} capabilities[guest][capability] = cap.new end # This registers a guest with the class. # # @param [Symbol] name Name of the guest # @param [Symbol] parent Name of the parent # @param [Boolean] detect Whether or not to detect properly def register_guest(name, parent, detect) guest = Class.new(Vagrant.plugin("2", "guest")) do define_method(:name) do name end define_method(:detect?) do |m| detect end end guests[name] = [guest, parent] end describe "#capability" do before :each do register_guest(:foo, nil, true) register_guest(:bar, :foo, true) subject.detect! end it "executes the capability" do register_capability(:bar, :test) expect { subject.capability(:test) }. to raise_error(RuntimeError, "cap: test [machine]") end it "executes the capability with arguments" do register_capability(:bar, :test) expect { subject.capability(:test, 1) }. to raise_error(RuntimeError, "cap: test [machine, 1]") end it "raises an exception if the capability doesn't exist" do expect { subject.capability(:what_is_this_i_dont_even) }. to raise_error(Vagrant::Errors::GuestCapabilityNotFound) end it "raises an exception if the method doesn't exist on the module" do register_capability(:bar, :test_is_corrupt, corrupt: true) expect { subject.capability(:test_is_corrupt) }. to raise_error(Vagrant::Errors::GuestCapabilityInvalid) end end describe "#capability?" do before :each do register_guest(:foo, nil, true) register_guest(:bar, :foo, true) subject.detect! end it "doesn't have unknown capabilities" do subject.capability?(:what_is_this_i_dont_even).should_not be end it "doesn't have capabilities registered to other guests" do register_capability(:baz, :test) subject.capability?(:test).should_not be end it "has capability of detected guest" do register_capability(:bar, :test) subject.capability?(:test).should be end it "has capability of parent guests" do register_capability(:foo, :test) subject.capability?(:test).should be end end describe "#detect!" do it "detects the first match" do register_guest(:foo, nil, false) register_guest(:bar, nil, true) register_guest(:baz, nil, false) subject.detect! subject.name.should == :bar subject.chain.length.should == 1 subject.chain[0][0].should == :bar subject.chain[0][1].name.should == :bar end it "detects those with the most parents first" do register_guest(:foo, nil, true) register_guest(:bar, :foo, true) register_guest(:baz, :bar, true) register_guest(:foo2, nil, true) register_guest(:bar2, :foo2, true) subject.detect! subject.name.should == :baz subject.chain.length.should == 3 subject.chain.map(&:first).should == [:baz, :bar, :foo] subject.chain.map { |x| x[1] }.map(&:name).should == [:baz, :bar, :foo] end it "detects the forced guest setting" do register_guest(:foo, nil, false) register_guest(:bar, nil, false) machine.config.vm.stub(:guest => :bar) subject.detect! subject.name.should == :bar end it "raises an exception if the forced guest can't be found" do register_guest(:foo, nil, true) machine.config.vm.stub(:guest => :bar) expect { subject.detect! }. to raise_error(Vagrant::Errors::GuestExplicitNotDetected) end it "raises an exception if no guest can be detected" do expect { subject.detect! }. to raise_error(Vagrant::Errors::GuestNotDetected) end end describe "#ready?" do before(:each) do register_guest(:foo, nil, true) end it "should not be ready by default" do subject.ready?.should_not be end it "should be ready after detecting" do subject.detect! subject.ready?.should be end end end vagrant-1.4.3/test/unit/vagrant/hosts_test.rb000066400000000000000000000016111226132634600213110ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe Vagrant::Hosts do let(:registry) { Hash.new } let(:base_class) { Vagrant::Plugin::V1::Host } it "detects the host that matches true" do foo_klass = Class.new(base_class) do def self.match?; false; end end bar_klass = Class.new(base_class) do def self.match?; true; end end registry[:foo] = foo_klass registry[:bar] = bar_klass described_class.detect(registry).should == bar_klass end it "detects the host that matches true with the highest precedence first" do foo_klass = Class.new(base_class) do def self.match?; true; end end bar_klass = Class.new(base_class) do def self.match?; true; end def self.precedence; 9; end end registry[:foo] = foo_klass registry[:bar] = bar_klass described_class.detect(registry).should == bar_klass end end vagrant-1.4.3/test/unit/vagrant/machine_state_test.rb000066400000000000000000000012321226132634600227540ustar00rootroot00000000000000require "pathname" require File.expand_path("../../base", __FILE__) describe Vagrant::MachineState do include_context "unit" let(:id) { :some_state } let(:short) { "foo" } let(:long) { "I am a longer foo" } it "should give access to the id" do instance = described_class.new(id, short, long) instance.id.should == id end it "should give access to the short description" do instance = described_class.new(id, short, long) instance.short_description.should == short end it "should give access to the long description" do instance = described_class.new(id, short, long) instance.long_description.should == long end end vagrant-1.4.3/test/unit/vagrant/machine_test.rb000066400000000000000000000265301226132634600215640ustar00rootroot00000000000000require "pathname" require File.expand_path("../../base", __FILE__) describe Vagrant::Machine do include_context "unit" let(:name) { "foo" } let(:provider) { double("provider") } let(:provider_cls) do obj = double("provider_cls") obj.stub(:new => provider) obj end let(:provider_config) { Object.new } let(:provider_name) { :test } let(:provider_options) { {} } let(:box) { Object.new } let(:config) { env.config_global } let(:data_dir) { Pathname.new(Tempdir.new.path) } let(:env) do # We need to create a Vagrantfile so that this test environment # has a proper root path test_env.vagrantfile("") # Create the Vagrant::Environment instance test_env.create_vagrant_env end let(:test_env) { isolated_environment } let(:instance) { new_instance } subject { instance } # Returns a new instance with the test data def new_instance described_class.new(name, provider_name, provider_cls, provider_config, provider_options, config, data_dir, box, env) end describe "initialization" do describe "provider initialization" do # This is a helper that generates a test for provider intialization. # This is a separate helper method because it takes a block that can # be used to have additional tests on the received machine. # # @yield [machine] Yields the machine that the provider initialization # method received so you can run additional tests on it. def provider_init_test received_machine = nil provider_cls = double("provider_cls") provider_cls.should_receive(:new) do |machine| # Store this for later so we can verify that it is the # one we expected to receive. received_machine = machine # Sanity check machine.should be # Yield our machine if we want to do additional tests yield machine if block_given? end # Initialize a new machine and verify that we properly receive # the machine we expect. instance = described_class.new(name, provider_name, provider_cls, provider_config, provider_options, config, data_dir, box, env) received_machine.should eql(instance) end it "should initialize with the machine object" do # Just run the blank test provider_init_test end it "should have the machine name setup" do provider_init_test do |machine| machine.name.should == name end end it "should have the machine configuration" do provider_init_test do |machine| machine.config.should eql(config) end end it "should have the box" do provider_init_test do |machine| machine.box.should eql(box) end end it "should have the environment" do provider_init_test do |machine| machine.env.should eql(env) end end it "should have access to the ID" do # Stub this because #id= calls it. provider.stub(:machine_id_changed) # Set the ID on the previous instance so that it is persisted instance.id = "foo" provider_init_test do |machine| machine.id.should == "foo" end end it "should NOT have access to the provider" do provider_init_test do |machine| machine.provider.should be_nil end end end end describe "attributes" do its(:name) { should eq(name) } its(:config) { should eql(config) } its(:box) { should eql(box) } its(:env) { should eql(env) } its(:provider) { should eql(provider) } its(:provider_config) { should eql(provider_config) } its(:provider_options) { should eq(provider_options) } end describe "actions" do it "should be able to run an action that exists" do action_name = :up called = false callable = lambda { |_env| called = true } provider.should_receive(:action).with(action_name).and_return(callable) instance.action(:up) called.should be end it "should provide the machine in the environment" do action_name = :up machine = nil callable = lambda { |env| machine = env[:machine] } provider.stub(:action).with(action_name).and_return(callable) instance.action(:up) machine.should eql(instance) end it "should pass any extra options to the environment" do action_name = :up foo = nil callable = lambda { |env| foo = env[:foo] } provider.stub(:action).with(action_name).and_return(callable) instance.action(:up, :foo => :bar) foo.should == :bar end it "should return the environment as a result" do action_name = :up callable = lambda { |env| env[:result] = "FOO" } provider.stub(:action).with(action_name).and_return(callable) result = instance.action(action_name) result[:result].should == "FOO" end it "should raise an exception if the action is not implemented" do action_name = :up provider.stub(:action).with(action_name).and_return(nil) expect { instance.action(action_name) }. to raise_error(Vagrant::Errors::UnimplementedProviderAction) end end describe "communicator" do it "should always return the SSH communicator" do instance.communicate.should be_kind_of(VagrantPlugins::CommunicatorSSH::Communicator) end it "should memoize the result" do obj = instance.communicate instance.communicate.should eql(obj) end end describe "guest implementation" do let(:communicator) do result = double("communicator") result.stub(:ready?).and_return(true) result.stub(:test).and_return(false) result end before(:each) do test_guest = Class.new(Vagrant.plugin("2", :guest)) do def detect?(machine) true end end register_plugin do |p| p.guest(:test) { test_guest } end instance.stub(:communicate).and_return(communicator) end it "should raise an exception if communication is not ready" do communicator.should_receive(:ready?).and_return(false) expect { instance.guest }. to raise_error(Vagrant::Errors::MachineGuestNotReady) end it "should return the configured guest" do result = instance.guest result.should be_kind_of(Vagrant::Guest) result.ready?.should be result.chain[0][0].should == :test end end describe "setting the ID" do before(:each) do provider.stub(:machine_id_changed) end it "should not have an ID by default" do instance.id.should be_nil end it "should set an ID" do instance.id = "bar" instance.id.should == "bar" end it "should notify the machine that the ID changed" do provider.should_receive(:machine_id_changed).once instance.id = "bar" end it "should persist the ID" do instance.id = "foo" new_instance.id.should == "foo" end it "should delete the ID" do instance.id = "foo" second = new_instance second.id.should == "foo" second.id = nil third = new_instance third.id.should be_nil end end describe "ssh info" do describe "with the provider returning nil" do it "should return nil if the provider returns nil" do provider.should_receive(:ssh_info).and_return(nil) instance.ssh_info.should be_nil end end describe "with the provider returning data" do let(:provider_ssh_info) { {} } before(:each) do provider.should_receive(:ssh_info).and_return(provider_ssh_info) end [:host, :port, :username].each do |type| it "should return the provider data if not configured in Vagrantfile" do provider_ssh_info[type] = "foo" instance.config.ssh.send("#{type}=", nil) instance.ssh_info[type].should == "foo" end it "should return the Vagrantfile value if provider data not given" do provider_ssh_info[type] = nil instance.config.ssh.send("#{type}=", "bar") instance.ssh_info[type].should == "bar" end it "should use the default if no override and no provider" do provider_ssh_info[type] = nil instance.config.ssh.send("#{type}=", nil) instance.config.ssh.default.send("#{type}=", "foo") instance.ssh_info[type].should == "foo" end it "should use the override if set even with a provider" do provider_ssh_info[type] = "baz" instance.config.ssh.send("#{type}=", "bar") instance.config.ssh.default.send("#{type}=", "foo") instance.ssh_info[type].should == "bar" end end it "should set the configured forward agent settings" do provider_ssh_info[:forward_agent] = true instance.config.ssh.forward_agent = false instance.ssh_info[:forward_agent].should == false end it "should set the configured forward X11 settings" do provider_ssh_info[:forward_x11] = true instance.config.ssh.forward_x11 = false instance.ssh_info[:forward_x11].should == false end it "should return the provider private key if given" do provider_ssh_info[:private_key_path] = "/foo" instance.ssh_info[:private_key_path].should == ["/foo"] end it "should return the configured SSH key path if set" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = "/bar" instance.ssh_info[:private_key_path].should == ["/bar"] end it "should return the array of SSH keys if set" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = ["/foo", "/bar"] instance.ssh_info[:private_key_path].should == ["/foo", "/bar"] end context "expanding path relative to the root path" do it "should with the provider key path" do provider_ssh_info[:private_key_path] = "~/foo" instance.ssh_info[:private_key_path].should == [File.expand_path("~/foo", env.root_path)] end it "should with the config private key path" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = "~/bar" instance.ssh_info[:private_key_path].should == [File.expand_path("~/bar", env.root_path)] end end it "should return the default private key path if provider and config doesn't have one" do provider_ssh_info[:private_key_path] = nil instance.config.ssh.private_key_path = nil instance.ssh_info[:private_key_path].should == [instance.env.default_private_key_path.to_s] end end end describe "state" do it "should query state from the provider" do state = Vagrant::MachineState.new(:id, "short", "long") provider.should_receive(:state).and_return(state) instance.state.id.should == :id end it "should raise an exception if a MachineState is not returned" do provider.should_receive(:state).and_return(:old_school) expect { instance.state }. to raise_error(Vagrant::Errors::MachineStateInvalid) end end end vagrant-1.4.3/test/unit/vagrant/plugin/000077500000000000000000000000001226132634600200645ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/plugin/v1/000077500000000000000000000000001226132634600204125ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/plugin/v1/command_test.rb000066400000000000000000000101461226132634600234160ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require 'optparse' describe Vagrant::Plugin::V1::Command do describe "parsing options" do let(:klass) do Class.new(described_class) do # Make the method public since it is normally protected public :parse_options end end it "returns the remaining arguments" do options = {} opts = OptionParser.new do |opts| opts.on("-f") do |f| options[:f] = f end end result = klass.new(["-f", "foo"], nil).parse_options(opts) # Check the results options[:f].should be result.should == ["foo"] end it "creates an option parser if none is given" do result = klass.new(["foo"], nil).parse_options(nil) result.should == ["foo"] end ["-h", "--help"].each do |help_string| it "returns nil and prints the help if '#{help_string}' is given" do instance = klass.new([help_string], nil) instance.should_receive(:safe_puts) instance.parse_options(OptionParser.new).should be_nil end end it "raises an error if invalid options are given" do instance = klass.new(["-f"], nil) expect { instance.parse_options(OptionParser.new) }. to raise_error(Vagrant::Errors::CLIInvalidOptions) end end describe "target VMs" do let(:klass) do Class.new(described_class) do # Make the method public since it is normally protected public :with_target_vms end end let(:environment) do env = double("environment") env.stub(:root_path => "foo") env end let(:instance) { klass.new([], environment) } it "should raise an exception if a root_path is not available" do environment.stub(:root_path => nil) expect { instance.with_target_vms }. to raise_error(Vagrant::Errors::NoEnvironmentError) end it "should yield every VM in order is no name is given" do foo_vm = double("foo") foo_vm.stub(:name).and_return("foo") bar_vm = double("bar") bar_vm.stub(:name).and_return("bar") environment.stub(:multivm? => true, :vms => { "foo" => foo_vm, "bar" => bar_vm }, :vms_ordered => [foo_vm, bar_vm]) vms = [] instance.with_target_vms do |vm| vms << vm end vms.should == [foo_vm, bar_vm] end it "raises an exception if the named VM doesn't exist" do environment.stub(:multivm? => true, :vms => {}) expect { instance.with_target_vms("foo") }. to raise_error(Vagrant::Errors::VMNotFoundError) end it "yields the given VM if a name is given" do foo_vm = double("foo") foo_vm.stub(:name).and_return(:foo) environment.stub(:multivm? => true, :vms => { :foo => foo_vm, :bar => nil }) vms = [] instance.with_target_vms("foo") { |vm| vms << vm } vms.should == [foo_vm] end end describe "splitting the main and subcommand args" do let(:instance) do Class.new(described_class) do # Make the method public since it is normally protected public :split_main_and_subcommand end.new(nil, nil) end it "should work when given all 3 parts" do result = instance.split_main_and_subcommand(["-v", "status", "-h", "-v"]) result.should == [["-v"], "status", ["-h", "-v"]] end it "should work when given only a subcommand and args" do result = instance.split_main_and_subcommand(["status", "-h"]) result.should == [[], "status", ["-h"]] end it "should work when given only main flags" do result = instance.split_main_and_subcommand(["-v", "-h"]) result.should == [["-v", "-h"], nil, []] end it "should work when given only a subcommand" do result = instance.split_main_and_subcommand(["status"]) result.should == [[], "status", []] end it "works when there are other non-flag args after the subcommand" do result = instance.split_main_and_subcommand(["-v", "box", "add", "-h"]) result.should == [["-v"], "box", ["add", "-h"]] end end end vagrant-1.4.3/test/unit/vagrant/plugin/v1/communicator_test.rb000066400000000000000000000003511226132634600244750ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Communicator do let(:machine) { Object.new } it "should not match by default" do described_class.match?(machine).should_not be end end vagrant-1.4.3/test/unit/vagrant/plugin/v1/config_test.rb000066400000000000000000000022661226132634600232510ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Config do include_context "unit" let(:foo_class) do Class.new(described_class) do attr_accessor :one attr_accessor :two end end it "has an UNSET_VALUE constant" do value = described_class.const_get("UNSET_VALUE") value.should be_kind_of Object value.should eql(described_class.const_get("UNSET_VALUE")) end describe "merging" do it "should merge by default by simply copying each instance variable" do one = foo_class.new one.one = 2 one.two = 1 two = foo_class.new two.two = 5 result = one.merge(two) result.one.should == 2 result.two.should == 5 end it "doesn't merge values that start with a double underscore" do one = foo_class.new one.one = 1 one.two = 1 one.instance_variable_set(:@__bar, "one") two = foo_class.new two.two = 2 two.instance_variable_set(:@__bar, "two") # Merge and verify result = one.merge(two) result.one.should == 1 result.two.should == 2 result.instance_variable_get(:@__bar).should be_nil end end end vagrant-1.4.3/test/unit/vagrant/plugin/v1/host_test.rb000066400000000000000000000001601226132634600227500ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Host do # No tests. end vagrant-1.4.3/test/unit/vagrant/plugin/v1/manager_test.rb000066400000000000000000000046611226132634600234170ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Manager do include_context "unit" let(:instance) { described_class.new } def plugin p = Class.new(Vagrant.plugin("1")) yield p p end it "should enumerate registered communicator classes" do pA = plugin do |p| p.communicator("foo") { "bar" } end pB = plugin do |p| p.communicator("bar") { "baz" } end instance.register(pA) instance.register(pB) instance.communicators.length.should == 2 instance.communicators[:foo].should == "bar" instance.communicators[:bar].should == "baz" end it "should enumerate registered configuration classes" do pA = plugin do |p| p.config("foo") { "bar" } end pB = plugin do |p| p.config("bar") { "baz" } end instance.register(pA) instance.register(pB) instance.config.length.should == 2 instance.config[:foo].should == "bar" instance.config[:bar].should == "baz" end it "should enumerate registered upgrade safe config classes" do pA = plugin do |p| p.config("foo", true) { "bar" } end pB = plugin do |p| p.config("bar") { "baz" } end instance.register(pA) instance.register(pB) instance.config_upgrade_safe.length.should == 1 instance.config_upgrade_safe[:foo].should == "bar" end it "should enumerate registered guest classes" do pA = plugin do |p| p.guest("foo") { "bar" } end pB = plugin do |p| p.guest("bar") { "baz" } end instance.register(pA) instance.register(pB) instance.guests.length.should == 2 instance.guests[:foo].should == "bar" instance.guests[:bar].should == "baz" end it "should enumerate registered host classes" do pA = plugin do |p| p.host("foo") { "bar" } end pB = plugin do |p| p.host("bar") { "baz" } end instance.register(pA) instance.register(pB) instance.hosts.length.should == 2 instance.hosts[:foo].should == "bar" instance.hosts[:bar].should == "baz" end it "should enumerate registered provider classes" do pA = plugin do |p| p.provider("foo") { "bar" } end pB = plugin do |p| p.provider("bar") { "baz" } end instance.register(pA) instance.register(pB) instance.providers.length.should == 2 instance.providers[:foo].should == "bar" instance.providers[:bar].should == "baz" end end vagrant-1.4.3/test/unit/vagrant/plugin/v1/plugin_test.rb000066400000000000000000000167461226132634600233120ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Plugin do after(:each) do # We want to make sure that the registered plugins remains empty # after each test. described_class.manager.reset! end it "should be able to set and get the name" do plugin = Class.new(described_class) do name "foo" end plugin.name.should == "foo" end it "should be able to set and get the description" do plugin = Class.new(described_class) do description "bar" end plugin.description.should == "bar" end describe "action hooks" do it "should register action hooks" do plugin = Class.new(described_class) do action_hook("foo") { "bar" } end hooks = plugin.action_hook("foo") hooks.length.should == 1 hooks[0].call.should == "bar" end end describe "commands" do it "should register command classes" do plugin = Class.new(described_class) do command("foo") { "bar" } end plugin.command[:foo].should == "bar" end ["spaces bad", "sym^bols"].each do |bad| it "should not allow bad command name: #{bad}" do plugin = Class.new(described_class) expect { plugin.command(bad) {} }. to raise_error(Vagrant::Plugin::V1::InvalidCommandName) end end it "should lazily register command classes" do # Below would raise an error if the value of the command class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do command("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the command key that # a proper error is raised. expect { plugin.command[:foo] }.to raise_error(StandardError) end end describe "communicators" do it "should register communicator classes" do plugin = Class.new(described_class) do communicator("foo") { "bar" } end plugin.communicator[:foo].should == "bar" end it "should lazily register communicator classes" do # Below would raise an error if the value of the class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do communicator("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.communicator[:foo] }.to raise_error(StandardError) end end describe "configuration" do it "should register configuration classes" do plugin = Class.new(described_class) do config("foo") { "bar" } end plugin.config[:foo].should == "bar" end it "should lazily register configuration classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do config("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.config[:foo] }.to raise_error(StandardError) end end describe "guests" do it "should register guest classes" do plugin = Class.new(described_class) do guest("foo") { "bar" } end plugin.guest[:foo].should == "bar" end it "should lazily register guest classes" do # Below would raise an error if the value of the guest class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do guest("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the guest key that # a proper error is raised. expect { plugin.guest[:foo] }.to raise_error(StandardError) end end describe "hosts" do it "should register host classes" do plugin = Class.new(described_class) do host("foo") { "bar" } end plugin.host[:foo].should == "bar" end it "should lazily register host classes" do # Below would raise an error if the value of the host class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do host("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the host key that # a proper error is raised. expect { plugin.host[:foo] }.to raise_error(StandardError) end end describe "providers" do it "should register provider classes" do plugin = Class.new(described_class) do provider("foo") { "bar" } end plugin.provider[:foo].should == "bar" end it "should lazily register provider classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do provider("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.provider[:foo] }.to raise_error(StandardError) end end describe "provisioners" do it "should register provisioner classes" do plugin = Class.new(described_class) do provisioner("foo") { "bar" } end plugin.provisioner[:foo].should == "bar" end it "should lazily register provisioner classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do provisioner("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.provisioner[:foo] }.to raise_error(StandardError) end end describe "plugin registration" do let(:manager) { described_class.manager } it "should have no registered plugins" do manager.registered.should be_empty end it "should register a plugin when a name is set" do plugin = Class.new(described_class) do name "foo" end manager.registered.should == [plugin] end it "should register a plugin only once" do plugin = Class.new(described_class) do name "foo" name "bar" end manager.registered.should == [plugin] end end end vagrant-1.4.3/test/unit/vagrant/plugin/v1/provider_test.rb000066400000000000000000000007271226132634600236360ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V1::Provider do let(:machine) { Object.new } let(:instance) { described_class.new(machine) } it "should return nil by default for actions" do instance.action(:whatever).should be_nil end it "should return nil by default for ssh info" do instance.ssh_info.should be_nil end it "should return nil by default for state" do instance.state.should be_nil end end vagrant-1.4.3/test/unit/vagrant/plugin/v2/000077500000000000000000000000001226132634600204135ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/plugin/v2/command_test.rb000066400000000000000000000170451226132634600234240ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require 'optparse' describe Vagrant::Plugin::V2::Command do describe "parsing options" do let(:klass) do Class.new(described_class) do # Make the method public since it is normally protected public :parse_options end end it "returns the remaining arguments" do options = {} opts = OptionParser.new do |o| o.on("-f") do |f| options[:f] = f end end result = klass.new(["-f", "foo"], nil).parse_options(opts) # Check the results options[:f].should be result.should == ["foo"] end it "creates an option parser if none is given" do result = klass.new(["foo"], nil).parse_options(nil) result.should == ["foo"] end ["-h", "--help"].each do |help_string| it "returns nil and prints the help if '#{help_string}' is given" do instance = klass.new([help_string], nil) instance.should_receive(:safe_puts) instance.parse_options(OptionParser.new).should be_nil end end it "raises an error if invalid options are given" do instance = klass.new(["-f"], nil) expect { instance.parse_options(OptionParser.new) }. to raise_error(Vagrant::Errors::CLIInvalidOptions) end end describe "target VMs" do let(:klass) do Class.new(described_class) do # Make the method public since it is normally protected public :with_target_vms end end let(:default_provider) { :virtualbox } let(:environment) do env = double("environment") env.stub(:active_machines => []) env.stub(:default_provider => default_provider) env.stub(:root_path => "foo") env end let(:instance) { klass.new([], environment) } it "should raise an exception if a root_path is not available" do environment.stub(:root_path => nil) expect { instance.with_target_vms }. to raise_error(Vagrant::Errors::NoEnvironmentError) end it "should yield every VM in order is no name is given" do foo_vm = double("foo") foo_vm.stub(:name => "foo", :provider => :foobarbaz) bar_vm = double("bar") bar_vm.stub(:name => "bar", :provider => :foobarbaz) environment.stub(:machine_names => [:foo, :bar]) environment.stub(:machine).with(:foo, default_provider).and_return(foo_vm) environment.stub(:machine).with(:bar, default_provider).and_return(bar_vm) vms = [] instance.with_target_vms do |vm| vms << vm end vms.should == [foo_vm, bar_vm] end it "raises an exception if the named VM doesn't exist" do environment.stub(:machine_names => [:default]) environment.stub(:machine).with(:foo, anything).and_return(nil) expect { instance.with_target_vms("foo") }. to raise_error(Vagrant::Errors::VMNotFoundError) end it "yields the given VM if a name is given" do foo_vm = double("foo") foo_vm.stub(:name => "foo", :provider => :foobarbaz) environment.stub(:machine).with(:foo, default_provider).and_return(foo_vm) vms = [] instance.with_target_vms("foo") { |vm| vms << vm } vms.should == [foo_vm] end it "yields the given VM with proper provider if given" do foo_vm = double("foo") provider = :foobarbaz foo_vm.stub(:name => "foo", :provider => provider) environment.stub(:machine).with(:foo, provider).and_return(foo_vm) vms = [] instance.with_target_vms("foo", :provider => provider) { |vm| vms << vm } vms.should == [foo_vm] end it "should raise an exception if an active machine exists with a different provider" do name = :foo environment.stub(:active_machines => [[name, :vmware]]) expect { instance.with_target_vms(name.to_s, :provider => :foo) }. to raise_error Vagrant::Errors::ActiveMachineWithDifferentProvider end it "should default to the active machine provider if no explicit provider requested" do name = :foo provider = :vmware vmware_vm = double("vmware_vm") environment.stub(:active_machines => [[name, provider]]) environment.stub(:machine).with(name, provider).and_return(vmware_vm) vmware_vm.stub(:name => name, :provider => provider) vms = [] instance.with_target_vms(name.to_s) { |vm| vms << vm } vms.should == [vmware_vm] end it "should use the explicit provider if it maches the active machine" do name = :foo provider = :vmware vmware_vm = double("vmware_vm") environment.stub(:active_machines => [[name, provider]]) environment.stub(:machine).with(name, provider).and_return(vmware_vm) vmware_vm.stub(:name => name, :provider => provider) vms = [] instance.with_target_vms(name.to_s, :provider => provider) { |vm| vms << vm } vms.should == [vmware_vm] end it "should use the default provider if none is given and none are active" do name = :foo machine = double("machine") environment.stub(:machine).with(name, default_provider).and_return(machine) machine.stub(:name => name, :provider => default_provider) results = [] instance.with_target_vms(name.to_s) { |m| results << m } results.should == [machine] end it "should use the primary machine with the active provider" do name = :foo provider = :vmware vmware_vm = double("vmware_vm") environment.stub(:active_machines => [[name, provider]]) environment.stub(:machine).with(name, provider).and_return(vmware_vm) environment.stub(:machine_names => []) environment.stub(:primary_machine_name => name) vmware_vm.stub(:name => name, :provider => provider) vms = [] instance.with_target_vms(nil, :single_target => true) { |vm| vms << vm } vms.should == [vmware_vm] end it "should use the primary machine with the default provider" do name = :foo machine = double("machine") environment.stub(:active_machines => []) environment.stub(:machine).with(name, default_provider).and_return(machine) environment.stub(:machine_names => []) environment.stub(:primary_machine_name => name) machine.stub(:name => name, :provider => default_provider) vms = [] instance.with_target_vms(nil, :single_target => true) { |vm| vms << machine } vms.should == [machine] end end describe "splitting the main and subcommand args" do let(:instance) do Class.new(described_class) do # Make the method public since it is normally protected public :split_main_and_subcommand end.new(nil, nil) end it "should work when given all 3 parts" do result = instance.split_main_and_subcommand(["-v", "status", "-h", "-v"]) result.should == [["-v"], "status", ["-h", "-v"]] end it "should work when given only a subcommand and args" do result = instance.split_main_and_subcommand(["status", "-h"]) result.should == [[], "status", ["-h"]] end it "should work when given only main flags" do result = instance.split_main_and_subcommand(["-v", "-h"]) result.should == [["-v", "-h"], nil, []] end it "should work when given only a subcommand" do result = instance.split_main_and_subcommand(["status"]) result.should == [[], "status", []] end it "works when there are other non-flag args after the subcommand" do result = instance.split_main_and_subcommand(["-v", "box", "add", "-h"]) result.should == [["-v"], "box", ["add", "-h"]] end end end vagrant-1.4.3/test/unit/vagrant/plugin/v2/communicator_test.rb000066400000000000000000000003511226132634600244760ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Communicator do let(:machine) { Object.new } it "should not match by default" do described_class.match?(machine).should_not be end end vagrant-1.4.3/test/unit/vagrant/plugin/v2/components_test.rb000066400000000000000000000010271226132634600241640ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) require "vagrant/registry" describe Vagrant::Plugin::V2::Components do subject { described_class.new } it "should have synced folders" do subject.synced_folders.should be_kind_of(Vagrant::Registry) end describe "configs" do it "should have configs" do subject.configs.should be_kind_of(Hash) end it "should default the values to registries" do subject.configs[:i_probably_dont_exist].should be_kind_of(Vagrant::Registry) end end end vagrant-1.4.3/test/unit/vagrant/plugin/v2/config_test.rb000066400000000000000000000025231226132634600232460ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Config do include_context "unit" let(:foo_class) do Class.new(described_class) do attr_accessor :one attr_accessor :two end end let(:unset_value) { described_class.const_get("UNSET_VALUE") } describe "merging" do it "should merge by default by simply copying each instance variable" do one = foo_class.new one.one = 2 one.two = 1 two = foo_class.new two.two = 5 result = one.merge(two) result.one.should == 2 result.two.should == 5 end it "prefers any set value over an UNSET_VALUE" do one = foo_class.new one.one = 1 one.two = 2 two = foo_class.new two.one = unset_value two.two = 5 result = one.merge(two) result.one.should == 1 result.two.should == 5 end it "doesn't merge values that start with a double underscore" do one = foo_class.new one.one = 1 one.two = 1 one.instance_variable_set(:@__bar, "one") two = foo_class.new two.two = 2 two.instance_variable_set(:@__bar, "two") # Merge and verify result = one.merge(two) result.one.should == 1 result.two.should == 2 result.instance_variable_get(:@__bar).should be_nil end end end vagrant-1.4.3/test/unit/vagrant/plugin/v2/host_test.rb000066400000000000000000000001601226132634600227510ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Host do # No tests. end vagrant-1.4.3/test/unit/vagrant/plugin/v2/manager_test.rb000066400000000000000000000115341226132634600234150ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Manager do include_context "unit" let(:instance) { described_class.new } def plugin p = Class.new(Vagrant.plugin("2")) yield p p end describe "#action_hooks" do it "should contain globally registered hooks" do pA = plugin do |p| p.action_hook("foo") { "bar" } end pB = plugin do |p| p.action_hook("bar") { "baz" } end instance.register(pA) instance.register(pB) result = instance.action_hooks(nil) result.length.should == 2 result[0].call.should == "bar" result[1].call.should == "baz" end it "should contain specific hooks with globally registered hooks" do pA = plugin do |p| p.action_hook("foo") { "bar" } p.action_hook("foo", :foo) { "bar_foo" } p.action_hook("foo", :bar) { "bar_bar" } end pB = plugin do |p| p.action_hook("bar") { "baz" } end instance.register(pA) instance.register(pB) result = instance.action_hooks(:foo) result.length.should == 3 result[0].call.should == "bar" result[1].call.should == "bar_foo" result[2].call.should == "baz" end end it "should enumerate registered communicator classes" do pA = plugin do |p| p.communicator("foo") { "bar" } end pB = plugin do |p| p.communicator("bar") { "baz" } end instance.register(pA) instance.register(pB) instance.communicators.to_hash.length.should == 2 instance.communicators[:foo].should == "bar" instance.communicators[:bar].should == "baz" end it "should enumerate registered configuration classes" do pA = plugin do |p| p.config("foo") { "bar" } end pB = plugin do |p| p.config("bar") { "baz" } end instance.register(pA) instance.register(pB) instance.config.to_hash.length.should == 2 instance.config[:foo].should == "bar" instance.config[:bar].should == "baz" end it "should enumerate registered guest classes" do pA = plugin do |p| p.guest("foo") { "bar" } end pB = plugin do |p| p.guest("bar", "foo") { "baz" } end instance.register(pA) instance.register(pB) instance.guests.to_hash.length.should == 2 instance.guests[:foo].should == ["bar", nil] instance.guests[:bar].should == ["baz", :foo] end it "should enumerate registered guest capabilities" do pA = plugin do |p| p.guest_capability("foo", "foo") { "bar" } end pB = plugin do |p| p.guest_capability("bar", "foo") { "baz" } end instance.register(pA) instance.register(pB) instance.guest_capabilities.length.should == 2 instance.guest_capabilities[:foo][:foo].should == "bar" instance.guest_capabilities[:bar][:foo].should == "baz" end it "should enumerate registered host classes" do pA = plugin do |p| p.host("foo") { "bar" } end pB = plugin do |p| p.host("bar") { "baz" } end instance.register(pA) instance.register(pB) instance.hosts.to_hash.length.should == 2 instance.hosts[:foo].should == "bar" instance.hosts[:bar].should == "baz" end it "should enumerate registered provider classes" do pA = plugin do |p| p.provider("foo") { "bar" } end pB = plugin do |p| p.provider("bar", foo: "bar") { "baz" } end instance.register(pA) instance.register(pB) instance.providers.to_hash.length.should == 2 instance.providers[:foo].should == ["bar", {}] instance.providers[:bar].should == ["baz", { foo: "bar" }] end it "provides the collection of registered provider configs" do pA = plugin do |p| p.config("foo", :provider) { "foo" } end pB = plugin do |p| p.config("bar", :provider) { "bar" } p.config("baz") { "baz" } end instance.register(pA) instance.register(pB) instance.provider_configs.to_hash.length.should == 2 instance.provider_configs[:foo].should == "foo" instance.provider_configs[:bar].should == "bar" end it "should enumerate all registered synced folder implementations" do pA = plugin do |p| p.synced_folder("foo") { "bar" } end pB = plugin do |p| p.synced_folder("bar", 50) { "baz" } end instance.register(pA) instance.register(pB) instance.synced_folders.to_hash.length.should == 2 instance.synced_folders[:foo].should == ["bar", 10] instance.synced_folders[:bar].should == ["baz", 50] end it "should list the required plugins" do instance.plugin_required("foo") instance.plugin_required("bar") expect(instance.required).to eq(["foo", "bar"]) end it "should list the required plugins only once" do instance.plugin_required("foo") instance.plugin_required("foo") expect(instance.required).to eq(["foo"]) end end vagrant-1.4.3/test/unit/vagrant/plugin/v2/plugin_test.rb000066400000000000000000000234771226132634600233120ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Plugin do after(:each) do # We want to make sure that the registered plugins remains empty # after each test. described_class.manager.reset! end it "should be able to set and get the name" do plugin = Class.new(described_class) do name "foo" end plugin.name.should == "foo" end it "should be able to set and get the description" do plugin = Class.new(described_class) do description "bar" end plugin.description.should == "bar" end describe "action hooks" do it "should register on all actions by default" do plugin = Class.new(described_class) do action_hook("foo") { "bar" } end hooks_registry = plugin.components.action_hooks hooks = hooks_registry[described_class.const_get("ALL_ACTIONS")] hooks.length.should == 1 hooks[0].call.should == "bar" end it "should register for a specific action by default" do plugin = Class.new(described_class) do action_hook("foo", :bar) { "bar" } end hooks_registry = plugin.components.action_hooks hooks = hooks_registry[:bar] hooks.length.should == 1 hooks[0].call.should == "bar" end end describe "commands" do it "should register command classes" do plugin = Class.new(described_class) do command("foo") { "bar" } end plugin.command[:foo].should == "bar" end ["spaces bad", "sym^bols"].each do |bad| it "should not allow bad command name: #{bad}" do plugin = Class.new(described_class) expect { plugin.command(bad) {} }. to raise_error(Vagrant::Plugin::V2::InvalidCommandName) end end it "should lazily register command classes" do # Below would raise an error if the value of the command class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do command("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the command key that # a proper error is raised. expect { plugin.command[:foo] }.to raise_error(StandardError) end end describe "communicators" do it "should register communicator classes" do plugin = Class.new(described_class) do communicator("foo") { "bar" } end plugin.communicator[:foo].should == "bar" end it "should lazily register communicator classes" do # Below would raise an error if the value of the class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do communicator("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.communicator[:foo] }.to raise_error(StandardError) end end describe "configuration" do it "should register configuration classes" do plugin = Class.new(described_class) do config("foo") { "bar" } end plugin.components.configs[:top][:foo].should == "bar" end it "should lazily register configuration classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do config("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.components.configs[:top][:foo] }.to raise_error(StandardError) end it "should register configuration classes for providers" do plugin = Class.new(described_class) do config("foo", :provider) { "bar" } end plugin.components.configs[:provider][:foo].should == "bar" end end describe "guests" do it "should register guest classes" do plugin = Class.new(described_class) do guest("foo") { "bar" } end plugin.components.guests[:foo].should == ["bar", nil] end it "should lazily register guest classes" do # Below would raise an error if the value of the guest class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do guest("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the guest key that # a proper error is raised. expect { plugin.guest[:foo] }.to raise_error(StandardError) end end describe "guest capabilities" do it "should register guest capabilities" do plugin = Class.new(described_class) do guest_capability("foo", "bar") { "baz" } end plugin.components.guest_capabilities[:foo][:bar].should == "baz" end end describe "hosts" do it "should register host classes" do plugin = Class.new(described_class) do host("foo") { "bar" } end plugin.host[:foo].should == "bar" end it "should lazily register host classes" do # Below would raise an error if the value of the host class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do host("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the host key that # a proper error is raised. expect { plugin.host[:foo] }.to raise_error(StandardError) end end describe "providers" do it "should register provider classes" do plugin = Class.new(described_class) do provider("foo") { "bar" } end plugin.components.providers[:foo].should == ["bar", {}] end it "should register provider classes with options" do plugin = Class.new(described_class) do provider("foo", foo: "yep") { "bar" } end plugin.components.providers[:foo].should == ["bar", { foo: "yep" }] end it "should lazily register provider classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do provider("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.components.providers[:foo] }.to raise_error(StandardError) end end describe "provisioners" do it "should register provisioner classes" do plugin = Class.new(described_class) do provisioner("foo") { "bar" } end plugin.provisioner[:foo].should == "bar" end it "should lazily register provisioner classes" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do provisioner("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.provisioner[:foo] }.to raise_error(StandardError) end end describe "synced folders" do it "should register implementations" do plugin = Class.new(described_class) do synced_folder("foo") { "bar" } end plugin.components.synced_folders[:foo].should == ["bar", 10] end it "should be able to specify priorities" do plugin = Class.new(described_class) do synced_folder("foo", 50) { "bar" } end plugin.components.synced_folders[:foo].should == ["bar", 50] end it "should lazily register implementations" do # Below would raise an error if the value of the config class was # evaluated immediately. By asserting that this does not raise an # error, we verify that the value is actually lazily loaded plugin = nil expect { plugin = Class.new(described_class) do synced_folder("foo") { raise StandardError, "FAIL!" } end }.to_not raise_error # Now verify when we actually get the configuration key that # a proper error is raised. expect { plugin.components.synced_folders[:foo] }.to raise_error(StandardError) end end describe "plugin registration" do let(:manager) { described_class.manager } it "should have no registered plugins" do manager.registered.should be_empty end it "should register a plugin when a name is set" do plugin = Class.new(described_class) do name "foo" end manager.registered.should == [plugin] end it "should register a plugin only once" do plugin = Class.new(described_class) do name "foo" name "bar" end manager.registered.should == [plugin] end end end vagrant-1.4.3/test/unit/vagrant/plugin/v2/provider_test.rb000066400000000000000000000007271226132634600236370ustar00rootroot00000000000000require File.expand_path("../../../../base", __FILE__) describe Vagrant::Plugin::V2::Provider do let(:machine) { Object.new } let(:instance) { described_class.new(machine) } it "should return nil by default for actions" do instance.action(:whatever).should be_nil end it "should return nil by default for ssh info" do instance.ssh_info.should be_nil end it "should return nil by default for state" do instance.state.should be_nil end end vagrant-1.4.3/test/unit/vagrant/registry_test.rb000066400000000000000000000062141226132634600220250ustar00rootroot00000000000000require File.expand_path("../../base", __FILE__) describe Vagrant::Registry do let(:instance) { described_class.new } it "should return nil for nonexistent items" do instance.get("foo").should be_nil end it "should register a simple key/value" do instance.register("foo") { "value" } instance.get("foo").should == "value" end it "should register an item without calling the block yet" do expect do instance.register("foo") do raise Exception, "BOOM!" end end.to_not raise_error end it "should raise an error if no block is given" do expect { instance.register("foo") }. to raise_error(ArgumentError) end it "should call and return the result of a block when asking for the item" do object = Object.new instance.register("foo") do object end instance.get("foo").should eql(object) end it "should be able to get the item with []" do object = Object.new instance.register("foo") { object } instance["foo"].should eql(object) end it "should cache the result of the item so they can be modified" do # Make the proc generate a NEW array each time instance.register("foo") { [] } # Test that modifying the result modifies the actual cached # value. This verifies we're caching. instance.get("foo").should == [] instance.get("foo") << "value" instance.get("foo").should == ["value"] end it "should be able to check if a key exists" do instance.register("foo") { "bar" } instance.should have_key("foo") instance.should_not have_key("bar") end it "should be enumerable" do instance.register("foo") { "foovalue" } instance.register("bar") { "barvalue" } keys = [] values = [] instance.each do |key, value| keys << key values << value end keys.sort.should == ["bar", "foo"] values.sort.should == ["barvalue", "foovalue"] end it "should be able to convert to a hash" do instance.register("foo") { "foovalue" } instance.register("bar") { "barvalue" } result = instance.to_hash result.should be_a(Hash) result["foo"].should == "foovalue" result["bar"].should == "barvalue" end describe "merging" do it "should merge in another registry" do one = described_class.new two = described_class.new one.register("foo") { raise "BOOM!" } two.register("bar") { raise "BAM!" } three = one.merge(two) expect { three["foo"] }.to raise_error("BOOM!") expect { three["bar"] }.to raise_error("BAM!") end it "should NOT merge in the cache" do one = described_class.new two = described_class.new one.register("foo") { [] } one["foo"] << 1 two.register("bar") { [] } two["bar"] << 2 three = one.merge(two) three["foo"].should == [] three["bar"].should == [] end end describe "merge!" do it "should merge into self" do one = described_class.new two = described_class.new one.register("foo") { "foo" } two.register("bar") { "bar" } one.merge!(two) one["foo"].should == "foo" one["bar"].should == "bar" end end end vagrant-1.4.3/test/unit/vagrant/util/000077500000000000000000000000001226132634600175435ustar00rootroot00000000000000vagrant-1.4.3/test/unit/vagrant/util/ansi_escape_code_remover_test.rb000066400000000000000000000005531226132634600261350ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/ansi_escape_code_remover" describe Vagrant::Util::ANSIEscapeCodeRemover do let(:klass) do Class.new do extend Vagrant::Util::ANSIEscapeCodeRemover end end it "should remove ANSI escape codes" do klass.remove_ansi_escape_codes("\e[Hyo").should == "yo" end end vagrant-1.4.3/test/unit/vagrant/util/downloader_test.rb000066400000000000000000000027021226132634600232660ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/downloader" describe Vagrant::Util::Downloader do let(:source) { "foo" } let(:destination) { "bar" } let(:exit_code) { 0 } let(:subprocess_result) do double("subprocess_result").tap do |result| result.stub(:exit_code => exit_code) result.stub(:stderr => "") end end subject { described_class.new(source, destination) } before :each do Vagrant::Util::Subprocess.stub(:execute).and_return(subprocess_result) end describe "#download!" do let(:curl_options) { ["--fail", "--location", "--max-redirs", "10", "--user-agent", described_class::USER_AGENT, "--output", destination, source, {}] } context "with a good exit status" do let(:exit_code) { 0 } it "downloads the file and returns true" do Vagrant::Util::Subprocess.should_receive(:execute). with("curl", *curl_options). and_return(subprocess_result) subject.download!.should be end end context "with a bad exit status" do let(:exit_code) { 1 } it "raises an exception" do Vagrant::Util::Subprocess.should_receive(:execute). with("curl", *curl_options). and_return(subprocess_result) expect { subject.download! }. to raise_error(Vagrant::Errors::DownloaderError) end end context "with a UI" do pending "tests for a UI" end end end vagrant-1.4.3/test/unit/vagrant/util/file_checksum_test.rb000066400000000000000000000012311226132634600237250ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require 'digest/md5' require 'digest/sha1' require 'vagrant/util/file_checksum' describe FileChecksum do include_context "unit" let(:environment) { isolated_environment } it "should return a valid checksum for a file" do file = environment.workdir.join("file") file.open("w+") { |f| f.write("HELLO!") } # Check multiple digests instance = described_class.new(file, Digest::MD5) instance.checksum.should == "9ac96c64417b5976a58839eceaa77956" instance = described_class.new(file, Digest::SHA1) instance.checksum.should == "264b207c7913e461c43d0f63d2512f4017af4755" end end vagrant-1.4.3/test/unit/vagrant/util/hash_with_indifferent_access_test.rb000066400000000000000000000017211226132634600270040ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/hash_with_indifferent_access" describe Vagrant::Util::HashWithIndifferentAccess do let(:instance) { described_class.new } it "is a Hash" do instance.should be_kind_of(Hash) end it "allows indifferent access when setting with a string" do instance["foo"] = "bar" instance[:foo].should == "bar" end it "allows indifferent access when setting with a symbol" do instance[:foo] = "bar" instance["foo"].should == "bar" end it "allows indifferent key lookup" do instance["foo"] = "bar" instance.key?(:foo).should be instance.has_key?(:foo).should be instance.include?(:foo).should be instance.member?(:foo).should be end it "allows for defaults to be passed in via an initializer block" do instance = described_class.new do |h,k| h[k] = "foo" end instance[:foo].should == "foo" instance["bar"].should == "foo" end end vagrant-1.4.3/test/unit/vagrant/util/is_port_open_test.rb000066400000000000000000000022541226132634600236320ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "socket" require "vagrant/util/is_port_open" describe Vagrant::Util::IsPortOpen do let(:klass) do Class.new do extend Vagrant::Util::IsPortOpen end end let(:open_port) { 52811 } let(:closed_port) { 52811 } it "should report open ports" do # Start a thread which listens on a port thr = Thread.new do server = TCPServer.new(open_port) Thread.current[:running] = true # Wait until we're told to die Thread.current[:die] = false while !Thread.current[:die] Thread.pass end # Die! server.close end # Wait until the server is running while !thr[:running] Thread.pass end # Verify that we report the port is open klass.is_port_open?("localhost", open_port).should be # Kill the thread thr[:die] = true thr.join end it "should report closed ports" do # This CAN fail, since port 52811 might actually be in use, but I'm # not sure what to do except choose some random port and hope for the # best, really. klass.is_port_open?("localhost", closed_port).should_not be end end vagrant-1.4.3/test/unit/vagrant/util/line_endings_helper_test.rb000066400000000000000000000005571226132634600251330ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/line_ending_helpers" describe Vagrant::Util::LineEndingHelpers do let(:klass) do Class.new do extend Vagrant::Util::LineEndingHelpers end end it "should convert DOS to unix-style line endings" do klass.dos_to_unix("foo\r\nbar\r\n").should == "foo\nbar\n" end end vagrant-1.4.3/test/unit/vagrant/util/network_ip_test.rb000066400000000000000000000005771226132634600233210ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/network_ip" describe Vagrant::Util::NetworkIP do let(:klass) do Class.new do extend Vagrant::Util::NetworkIP end end describe "network address" do it "calculates it properly" do klass.network_address("192.168.2.234", "255.255.255.0").should == "192.168.2.0" end end end vagrant-1.4.3/test/unit/vagrant/util/retryable_test.rb000066400000000000000000000046101226132634600231210ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/retryable" describe Vagrant::Util::Retryable do let(:klass) do Class.new do extend Vagrant::Util::Retryable end end it "doesn't retry by default" do tries = 0 block = lambda do tries += 1 raise RuntimeError, "Try" end # It should re-raise the error expect { klass.retryable(&block) }. to raise_error(RuntimeError) # It should've tried once tries.should == 1 end it "retries the set number of times" do tries = 0 block = lambda do tries += 1 raise RuntimeError, "Try" end # It should re-raise the error expect { klass.retryable(:tries => 5, &block) }. to raise_error(RuntimeError) # It should've tried all specified times tries.should == 5 end it "only retries on the given exception" do tries = 0 block = lambda do tries += 1 raise StandardError, "Try" end # It should re-raise the error expect { klass.retryable(:tries => 5, :on => RuntimeError, &block) }. to raise_error(StandardError) # It should've never tried since it was a different kind of error tries.should == 1 end it "can retry on multiple types of errors" do tries = 0 foo_error = Class.new(StandardError) bar_error = Class.new(StandardError) block = lambda do tries += 1 raise foo_error, "Try" if tries == 1 raise bar_error, "Try" if tries == 2 raise RuntimeError, "YAY" end # It should re-raise the error expect { klass.retryable(:tries => 5, :on => [foo_error, bar_error], &block) }. to raise_error(RuntimeError) # It should've never tried since it was a different kind of error tries.should == 3 end it "doesn't sleep between tries by default" do block = lambda do raise RuntimeError, "Try" end # Sleep should never be called klass.should_not_receive(:sleep) # Run it. expect { klass.retryable(:tries => 5, &block) }. to raise_error(RuntimeError) end it "sleeps specified amount between retries" do block = lambda do raise RuntimeError, "Try" end # Sleep should be called between each retry klass.should_receive(:sleep).with(10).exactly(4).times # Run it. expect { klass.retryable(:tries => 5, :sleep => 10, &block) }. to raise_error(RuntimeError) end end vagrant-1.4.3/test/unit/vagrant/util/safe_chdir_test.rb000066400000000000000000000014651226132634600232240ustar00rootroot00000000000000require 'tmpdir' require File.expand_path("../../../base", __FILE__) require 'vagrant/util/safe_chdir' describe Vagrant::Util::SafeChdir do it "should change directories" do expected = nil result = nil temp_dir = Dir.mktmpdir Dir.chdir(temp_dir) do expected = Dir.pwd end described_class.safe_chdir(temp_dir) do result = Dir.pwd end result.should == expected end it "should allow recursive chdir" do expected = nil result = nil temp_path = Dir.mktmpdir Dir.chdir(temp_path) do expected = Dir.pwd end expect do described_class.safe_chdir(Dir.mktmpdir) do described_class.safe_chdir(temp_path) do result = Dir.pwd end end end.to_not raise_error result.should == expected end end vagrant-1.4.3/test/unit/vagrant/util/scoped_hash_override_test.rb000066400000000000000000000017521226132634600253130ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/scoped_hash_override" describe Vagrant::Util::ScopedHashOverride do let(:klass) do Class.new do extend Vagrant::Util::ScopedHashOverride end end it "should not mess with non-overrides" do original = { :key => "value", :another_value => "foo" } klass.scoped_hash_override(original, "foo").should == original end it "should override if the scope matches" do original = { :key => "value", :scope__key => "replaced" } expected = { :key => "replaced" } klass.scoped_hash_override(original, "scope").should == expected end it "should ignore non-matching scopes" do original = { :key => "value", :scope__key => "replaced", :another__key => "value" } expected = { :key => "replaced", :another__key => "value" } klass.scoped_hash_override(original, "scope").should == expected end end vagrant-1.4.3/test/unit/vagrant/util/shell_quote_test.rb000066400000000000000000000004431226132634600234540ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/shell_quote" describe Vagrant::Util::ShellQuote do subject { described_class } it "quotes properly" do expected = "foo '\\''bar'\\''" expect(subject.escape("foo 'bar'", "'")).to eql(expected) end end vagrant-1.4.3/test/unit/vagrant/util/ssh_test.rb000066400000000000000000000014001226132634600217170ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/platform" require "vagrant/util/ssh" describe Vagrant::Util::SSH do include_context "unit" describe "checking key permissions" do let(:key_path) { temporary_file } it "should do nothing on Windows" do Vagrant::Util::Platform.stub(:windows?).and_return(true) key_path.chmod(0700) # Get the mode now and verify that it is untouched afterwards mode = key_path.stat.mode described_class.check_key_permissions(key_path) key_path.stat.mode.should == mode end it "should fix the permissions" do key_path.chmod(0644) described_class.check_key_permissions(key_path) key_path.stat.mode.should == 0100600 end end end vagrant-1.4.3/test/unit/vagrant/util/string_block_editor_test.rb000066400000000000000000000042441226132634600251610ustar00rootroot00000000000000require File.expand_path("../../../base", __FILE__) require "vagrant/util/string_block_editor" describe Vagrant::Util::StringBlockEditor do describe "#keys" do it "should return all the keys" do data = < #{Vagrant::VERSION}") }. to raise_error(Vagrant::Errors::VagrantVersionBad) end end end vagrant-1.4.3/vagrant-spec.config.example.rb000066400000000000000000000003031226132634600207770ustar00rootroot00000000000000require_relative "test/acceptance/base" Vagrant::Spec::Acceptance.configure do |c| c.provider "virtualbox", box: "", contexts: ["provider-context/virtualbox"] end vagrant-1.4.3/vagrant.gemspec000066400000000000000000000054431226132634600162030ustar00rootroot00000000000000$:.unshift File.expand_path("../lib", __FILE__) require "vagrant/version" Gem::Specification.new do |s| s.name = "vagrant" s.version = Vagrant::VERSION s.platform = Gem::Platform::RUBY s.authors = ["Mitchell Hashimoto", "John Bender"] s.email = ["mitchell.hashimoto@gmail.com", "john.m.bender@gmail.com"] s.homepage = "http://vagrantup.com" s.summary = "Build and distribute virtualized development environments." s.description = "Vagrant is a tool for building and distributing virtualized development environments." s.required_rubygems_version = ">= 1.3.6" s.rubyforge_project = "vagrant" s.add_dependency "childprocess", "~> 0.3.7" s.add_dependency "erubis", "~> 2.7.0" s.add_dependency "i18n", "~> 0.6.0" s.add_dependency "log4r", "~> 1.1.9" s.add_dependency "net-ssh", ">= 2.6.6", "< 2.8.0" s.add_dependency "net-scp", "~> 1.1.0" s.add_development_dependency "rake" s.add_development_dependency "contest", ">= 0.1.2" s.add_development_dependency "minitest", "~> 2.5.1" s.add_development_dependency "mocha" # This has problems on Windows, we need to find a better way: # s.add_development_dependency "sys-proctable", "~> 0.9.0" s.add_development_dependency "rspec", "~> 2.14.0" # The following block of code determines the files that should be included # in the gem. It does this by reading all the files in the directory where # this gemspec is, and parsing out the ignored files from the gitignore. # Note that the entire gitignore(5) syntax is not supported, specifically # the "!" syntax, but it should mostly work correctly. root_path = File.dirname(__FILE__) all_files = Dir.chdir(root_path) { Dir.glob("**/{*,.*}") } all_files.reject! { |file| [".", ".."].include?(File.basename(file)) } gitignore_path = File.join(root_path, ".gitignore") gitignore = File.readlines(gitignore_path) gitignore.map! { |line| line.chomp.strip } gitignore.reject! { |line| line.empty? || line =~ /^(#|!)/ } unignored_files = all_files.reject do |file| # Ignore any directories, the gemspec only cares about files next true if File.directory?(file) # Ignore any paths that match anything in the gitignore. We do # two tests here: # # - First, test to see if the entire path matches the gitignore. # - Second, match if the basename does, this makes it so that things # like '.DS_Store' will match sub-directories too (same behavior # as git). # gitignore.any? do |ignore| File.fnmatch(ignore, file, File::FNM_PATHNAME) || File.fnmatch(ignore, File.basename(file), File::FNM_PATHNAME) end end s.files = unignored_files s.executables = unignored_files.map { |f| f[/^bin\/(.*)/, 1] }.compact s.require_path = 'lib' end vagrant-1.4.3/website/000077500000000000000000000000001226132634600146305ustar00rootroot00000000000000vagrant-1.4.3/website/docs/000077500000000000000000000000001226132634600155605ustar00rootroot00000000000000vagrant-1.4.3/website/docs/.buildpacks000066400000000000000000000001601226132634600176770ustar00rootroot00000000000000https://github.com/heroku/heroku-buildpack-ruby.git https://github.com/hashicorp/heroku-buildpack-middleman.git vagrant-1.4.3/website/docs/Gemfile000066400000000000000000000004451226132634600170560ustar00rootroot00000000000000source "https://rubygems.org" gem "less", "~> 2.2.2" gem "middleman", "~> 3.0.6" gem "middleman-minify-html", "~> 3.0.0" gem "rack-contrib", "~> 1.1.0" gem "redcarpet", "~> 2.2.2" gem "therubyracer", "~> 0.12.0" gem "thin", "~> 1.5.0" group :development do gem "highline", "~> 1.6.15" end vagrant-1.4.3/website/docs/Gemfile.lock000066400000000000000000000063301226132634600200040ustar00rootroot00000000000000GEM remote: https://rubygems.org/ specs: POpen4 (0.1.4) Platform (>= 0.4.0) open4 Platform (0.4.0) activesupport (3.2.13) i18n (= 0.6.1) multi_json (~> 1.0) chunky_png (1.2.8) coffee-script (2.2.0) coffee-script-source execjs coffee-script-source (1.3.3) commonjs (0.2.7) compass (0.12.2) chunky_png (~> 1.2) fssm (>= 0.2.7) sass (~> 3.1) daemons (1.1.9) eventmachine (1.0.3) execjs (1.4.0) multi_json (~> 1.0) fssm (0.2.10) haml (4.0.3) tilt highline (1.6.19) hike (1.2.3) htmlcompressor (0.0.7) yui-compressor (~> 0.9.6) http_router (0.10.2) rack (>= 1.0.0) url_mount (~> 0.2.1) i18n (0.6.1) less (2.2.2) commonjs (~> 0.2.6) libv8 (3.16.14.3) listen (0.7.3) maruku (0.6.1) syntax (>= 1.0.0) middleman (3.0.14) middleman-core (= 3.0.14) middleman-more (= 3.0.14) middleman-sprockets (~> 3.1.0) middleman-core (3.0.14) activesupport (~> 3.2.6) bundler (~> 1.1) listen (~> 0.7.3) rack (~> 1.4.1) rack-test (~> 0.6.1) rb-fsevent (~> 0.9.3) thor (~> 0.15.4) tilt (~> 1.3.6) middleman-minify-html (3.0.0) htmlcompressor middleman-core (~> 3.0.0) middleman-more (3.0.14) coffee-script (~> 2.2.0) coffee-script-source (~> 1.3.3) compass (>= 0.12.2) execjs (~> 1.4.0) haml (>= 3.1.6) i18n (~> 0.6.0, < 0.6.2) maruku (~> 0.6.0) middleman-core (= 3.0.14) padrino-helpers (= 0.10.7) sass (>= 3.1.20) uglifier (~> 1.2.6) middleman-sprockets (3.1.4) middleman-core (>= 3.0.14) middleman-more (>= 3.0.14) sprockets (~> 2.1) sprockets-helpers (~> 1.0.0) sprockets-sass (~> 1.0.0) multi_json (1.8.0) open4 (1.3.0) padrino-core (0.10.7) activesupport (~> 3.2.0) http_router (~> 0.10.2) sinatra (~> 1.3.1) thor (~> 0.15.2) tilt (~> 1.3.0) padrino-helpers (0.10.7) i18n (~> 0.6) padrino-core (= 0.10.7) rack (1.4.5) rack-contrib (1.1.0) rack (>= 0.9.1) rack-protection (1.5.0) rack rack-test (0.6.2) rack (>= 1.0) rb-fsevent (0.9.3) redcarpet (2.2.2) ref (1.0.5) sass (3.2.10) sinatra (1.3.6) rack (~> 1.4) rack-protection (~> 1.3) tilt (~> 1.3, >= 1.3.3) sprockets (2.10.0) hike (~> 1.2) multi_json (~> 1.0) rack (~> 1.0) tilt (~> 1.1, != 1.3.0) sprockets-helpers (1.0.1) sprockets (~> 2.0) sprockets-sass (1.0.1) sprockets (~> 2.0) tilt (~> 1.1) syntax (1.0.0) therubyracer (0.12.0) libv8 (~> 3.16.14.0) ref thin (1.5.1) daemons (>= 1.0.9) eventmachine (>= 0.12.6) rack (>= 1.0.0) thor (0.15.4) tilt (1.3.7) uglifier (1.2.7) execjs (>= 0.3.0) multi_json (~> 1.3) url_mount (0.2.1) rack yui-compressor (0.9.6) POpen4 (>= 0.1.4) PLATFORMS ruby DEPENDENCIES highline (~> 1.6.15) less (~> 2.2.2) middleman (~> 3.0.6) middleman-minify-html (~> 3.0.0) rack-contrib (~> 1.1.0) redcarpet (~> 2.2.2) therubyracer (~> 0.12.0) thin (~> 1.5.0) vagrant-1.4.3/website/docs/Procfile000066400000000000000000000000451226132634600172450ustar00rootroot00000000000000web: bundle exec thin start -p $PORT vagrant-1.4.3/website/docs/README.md000066400000000000000000000015231226132634600170400ustar00rootroot00000000000000# Vagrant Documentation This is the repository for the [Vagrant Documentation website](http://docs.vagrantup.com). This is a [Middleman](http://middlemanapp.com) project, which builds a static site from these source files. The site is hosted on [Heroku](http://heroku.com) and then fronted by [Fastly](http://fastly.com). ## Contributions Welcome! If you find a typo or you feel like you can improve the HTML, CSS, or JavaScript, we welcome contributions. Feel free to open issues or pull requests like any normal GitHub project, and we'll merge it in. ## Running the Site Locally Running the site locally is simple. Clone this repo and run the following commands: ``` $ bundle $ bundle exec middleman server ``` Then open up `localhost:4567`. Note that some URLs you may need to append ".html" to make them work (in the navigation and such). vagrant-1.4.3/website/docs/config.rb000066400000000000000000000030431226132634600173520ustar00rootroot00000000000000### # Compass ### # Susy grids in Compass # First: gem install susy --pre # require 'susy' # Change Compass configuration # compass_config do |config| # config.output_style = :compact # end ### # Page options, layouts, aliases and proxies ### # Per-page layout changes: # # With no layout # page "/path/to/file.html", :layout => false # # With alternative layout # page "/path/to/file.html", :layout => :otherlayout # # A path which all have the same layout # with_layout :admin do # page "/admin/*" # end # Proxy (fake) files # page "/this-page-has-no-template.html", :proxy => "/template-file.html" do # @which_fake_page = "Rendering a fake page with a variable" # end ### # Helpers ### # Automatic image dimensions on image_tag helper # activate :automatic_image_sizes # Methods defined in the helpers block are available in templates # helpers do # def some_helper # "Helping" # end # end set :css_dir, 'stylesheets' set :js_dir, 'javascripts' set :images_dir, 'images' # Use the RedCarpet Markdown engine set :markdown_engine, :redcarpet set :markdown, :fenced_code_blocks => true # Build-specific configuration configure :build do activate :asset_hash activate :minify_css activate :minify_html activate :minify_javascript # Enable cache buster # activate :cache_buster # Use relative URLs # activate :relative_assets # Compress PNGs after build # First: gem install middleman-smusher # require "middleman-smusher" # activate :smusher # Or use a different image path # set :http_path, "/Content/images/" end vagrant-1.4.3/website/docs/config.ru000066400000000000000000000026621226132634600174030ustar00rootroot00000000000000require "rack" require "rack/auth/basic" require "rack/contrib/not_found" require "rack/contrib/response_headers" require "rack/contrib/static_cache" require "rack/contrib/try_static" require File.expand_path("../lib/redirect_to_latest", __FILE__) require File.expand_path("../lib/redirect_v1_docs", __FILE__) # Properly compress the output if the client can handle it. use Rack::Deflater # Redirect the homepage to the latest documentation use HashiCorp::Rack::RedirectToLatest # Redirect the V1 documentation to the GitHub pages hosted version use HashiCorp::Rack::RedirectV1Docs # Set the "forever expire" cache headers for these static assets. Since # we hash the contents of the assets to determine filenames, this is safe # to do. use Rack::StaticCache, :root => "build", :urls => ["/images", "/javascripts", "/stylesheets"], :duration => 2, :versioning => false # For anything that matches below this point, we set the surrogate key # for Fastly so that we can quickly purge all the pages without touching # the static assets. use Rack::ResponseHeaders do |headers| headers["Surrogate-Key"] = "page" end # Try to find a static file that matches our request, since Middleman # statically generates everything. use Rack::TryStatic, :root => "build", :urls => ["/"], :try => [".html", "index.html", "/index.html"] # 404 if we reached this point. Sad times. run Rack::NotFound.new(File.expand_path("../build/404.html", __FILE__)) vagrant-1.4.3/website/docs/helpers/000077500000000000000000000000001226132634600172225ustar00rootroot00000000000000vagrant-1.4.3/website/docs/helpers/sidebar_helpers.rb000066400000000000000000000010611226132634600227000ustar00rootroot00000000000000module SidebarHelpers # This helps by setting the "current" class for sidebar nav elements # if the YAML frontmatter matches the expected value. def sidebar_current(expected) current = current_page.data.sidebar_current if current == expected || sidebar_section == expected return " class=\"current\"" else return "" end end # This returns the overall section of the documentation we're on. def sidebar_section current = current_page.data.sidebar_current return "" if !current current.split("-")[0] end end vagrant-1.4.3/website/docs/lib/000077500000000000000000000000001226132634600163265ustar00rootroot00000000000000vagrant-1.4.3/website/docs/lib/redirect_to_latest.rb000066400000000000000000000010251226132634600225300ustar00rootroot00000000000000module HashiCorp module Rack # This redirects to the latest version of the docs. class RedirectToLatest def initialize(app) @app = app end def call(env) if env["PATH_INFO"] =~ /^\/$/ headers = { "Content-Type" => "text/html", "Location" => "/v2/", "Surrogate-Key" => "page" } message = "Redirecting to new URL..." return [301, headers, [message]] end @app.call(env) end end end end vagrant-1.4.3/website/docs/lib/redirect_v1_docs.rb000066400000000000000000000011371226132634600220740ustar00rootroot00000000000000module HashiCorp module Rack # This redirects the V1 docs to the GitHub pages hosted version. class RedirectV1Docs def initialize(app) @app = app end def call(env) if env["PATH_INFO"] =~ /^\/v1/ headers = { "Content-Type" => "text/html", "Location" => "http://docs-v1.vagrantup.com#{env["PATH_INFO"]}", "Surrogate-Key" => "page" } message = "Redirecting to old documentation URL..." return [301, headers, [message]] end @app.call(env) end end end end vagrant-1.4.3/website/docs/source/000077500000000000000000000000001226132634600170605ustar00rootroot00000000000000vagrant-1.4.3/website/docs/source/404.html.erb000066400000000000000000000003331226132634600210230ustar00rootroot00000000000000

Page Not Found

Sorry, the page you tried to visit doesn't exist. This could be our fault, and if so we'll fix that up right away. Please go back, or go back home to get back on track.

vagrant-1.4.3/website/docs/source/images/000077500000000000000000000000001226132634600203255ustar00rootroot00000000000000vagrant-1.4.3/website/docs/source/images/bullet_1.png000066400000000000000000000033601226132634600225440ustar00rootroot00000000000000PNG  IHDR22?IDATxڽMPgsRD ZH>$ * R][jN/0 1 "2*K/8dé=vFL;ԙN;}6.a77ϻ7-Sy-3ZofN3Z'W&ۦvUgRON%5g14O'h ;ZnF!< 4Im3uGuN[^7 ' 'n= LF@exD2 i' Ly8XNCe/ /"Ijjv6Ks4A&! ̈́R_Jwˮwvd5p4r2|:t 69мAƛjեW dXLKGYҟMQ*B%_Yd$*St|4D:4GͿ,%qʮ]$ ʀ1t@a"cw>vBD(8N($DL5.1/1!&P!t@{MSvHn`G/QVEF@COF:Ji0l.rrRsXj "! _:^zxVzP*M @i:OQ%2LOPMq+i}M,S;AU3";`E8 u@EiBQt<.ceh\̵cFG.E2kZ=%@~<=V+ 5-WHIe>JA2ˬD: =갹"Pu Am)ĐPL!0a5ڙ aB}زC1=jvJi;ҏiq{B{0 Qc kh3cƂћz ,@(vB(:cأ== bfb|+kl}PDD&6f[}tLcfsF~;BL6 ],'!2!, PPs09{ ޿^? ^ 1rgu1<ƣvhEjW?ޅ̙fLj#5 څA&r&gA-nge2A:1/)t̮Gno[wvWx;GKL*HlNg_LN'Q;r fLԿ,k-@r:ntb0<G L \5OkC$fGe! f;bEH"b C1vC q{Uh*u_Jl]JliC mG!H^9{MW%e`ݎt;\1m!̙V%#$jbC0ԑW3-UrzRrF P INSgyCHz SZSf;!`̴vi͉;)d4&J5[St@0$!)3g_fZ<n)4i͓ B0v$9Hu!3/zJkҎP 1˙3o~ʘiBCJm& aTzD^޼}N` x50WGә׋?Yfyz8mh0vZ8RO6f `;0a e} #@K6r\#P v;z_|:$Hyά]]skt`(m;![ ͸10yjN!I-c(b4BK<1(<̤V`[ۑ?ӊ I:қ7bXA v<. KӚamjУ&N6Q3af}|R0aLvԘP3I&4²!33WC@>I$]_|1iGl8ӄq.tVo=CC@У\d~(⳻,ڡ.^C'$`0Zo߀w⵻P3t-v!k B߆ 1O`,nxELK?& Ȟ~u9nG8߃̳tp 0=_02f30"y➉k{F|8xA\` nyF_%޿"q7Xa(m>V4BX5(a|$?j,g#H?G#Z鸧VE CvC cێ|I5/:1 4VDh+1,CH?= ujΆ&Ҍ #lyC0CK|;4өս*N[E Ç0y4䴢RUTTE a@҆ikiGyA;m>Blv^#ӲkfZ.}* `t6ts^cY@ L>"ʹ7i~sɺV#d7,QQWQ;qi:֎y- ~jV%(ȷ(|3*kp@;҅|ѿ+iIIjN͕[x C p8>;˚z" =nM7(Q2c3[wꏂ| A;eQ|^hKiQۃP;K5e آ|_7%Y!:a0!V~'=ayњCkakc#3Sl?l1嶃PCDlҏ99%}Yk\<{1|`D]իUq˜d@IENDB`vagrant-1.4.3/website/docs/source/images/customers.png000066400000000000000000000400721226132634600230620ustar00rootroot00000000000000PNG  IHDR+feuPLTE.HatRNS@а0`pP    *[hҴm/#5lA\1BHWzǦ-'kZ&]c=յ:jbn܁|ۊX{qMD$vC?YFf}ay(3sxeTNw,.Q7RلVJtoKrEu"SIg_~d^mq;IDATx^!kqO;l[CAo,a6Šh]si&|% fp¥3}? Oz#'.+ѸZ1mF~|5`(/Y1q${ZԫveRyw,w0h.xXoC$ci_"1$P~J?Dž*CtSuOK-%!n{0:P(2E{Ke/Y EM<LJO{9p/~IܬWG 텲PwnWfE'0udʝ:{(^Ea{!?u߰8ԶL 梟؀/gr/ WՐaiukA``9Dz:wdQy`e G3巯T0=F#wuMnJ gp g fWf*":Xĕ%}Kda+?)Xu_`Ek ;;º&tgښ*A,3m EJ4? Vs-4k`Jԧ J  \7;_s09k-W6;1RȄ VyK[@X\4`ٮ`=[*o{*uX7罏jO ș?N{rye;Ž9X/>м,XVz@,%(\w}y:+XJ:VEdÒ@bɳc%CIB6%(=0/,UA0撨)9+x'/~jjUA}J\֎`*(kW ɱVSQ_Lb;`S`U0^wi)-J͹Kxc. `K+W}*Ľކ` '.۵$Q <-Tڗ ߦo@ņWeMX!X...˂5EsYN-3e7C|8 ,_E7*{}{sg>u,#G`7?<HI~bʴNC#gV,NgQ>]72 ­t"!ioK`9Ұm7"@zZJ߯%!hVu, )lyx^cb3N}eOj{U9Td_O E-E^W2in **KLҎQLŨWm>X#X$%`ِD:SXvFi͂5u'3`^"\=;/ viurgЋr( @Z9V,<snuWB6~z穡7b+~a ίa-;kVy\( nd( )`P2ijXZz7vfH$ [~#G Cһ8حSN Yd.% I6Ǔ OkIO^8dPV,e+LOjGDq$}jLwPKJ]{,BrPLЭlMoG?jC3*L-I?:ay#T$ sW_\ӪNn [!,Dʟ?A2k+=x'iI>sYi0;4Xρ*)2>) %\+lݤ~ABGh 0N٣܃5FW;jS[&>قuƂ%iX&EpPg+բfI=_$8'& #F&\qܛkZB0 >!-ua4J7ك !0 Vܕ!7JU*3u^_E'l2CNoR;0d}d3Yאŧv pY~Z_'bdA:#xV>?h`?;d}*B;q]nH&v '߂ wBl)A,{ax?z^;l-Mn<鬖0э܃B풬,[]:^>Hh,qc5\L-I,:dQJ& ~"Xw%{4$>X:;;nH7eIzyP8,%UW|IXD3QoClHNNequF$anox3m.d;C3![rkcS]hy!Q@՚BT!+#G!!X c?\@V<& =K̹\ ,/'<#K&S #^QIJsW6B’܃4ףc n_1[fU \:N m04UF+Uۘi4uKle%y 8Ғ,a`A5&`?)ӷZznk+\o"2}92䦧\k]*pEbz{y6B1w8 LkLΒE6HJ`}gVA9Xʁ!حK"XYF4dIG0~!MXꩠ45FY=[ƴJ3qSzZڞ[X]Åyu^dZx oL?c!k݆ʅDd '}DCLKaD_3x·/Q?P8(5 1i&~U氒V|tL wƿ`:ظº Ղ$ ,I:$c5 $7f$901'9 $Y)XÍ^R=v ]8Gd&`Z dWm`}!Al$uR5R{l,}SMR"a,%81 3SD90;!uPRBq` WBhU9EP5+X,Au}@srp"X /[k8 NSvtjˬ*7VP:"C6K_,"XԼ`u' JL(F+"X^$˗QJX"0/sz(ե $OOZ5gl27LJl%'cN*Y˽4+zfn #E4T^kvn} ~|>BVw j sVF}xU,~} @;p`%X" $>O*T9 e~$t,@׫ ,7,k3Bc;;/*E1$&g@[BJ_M?^DS[~Y| yuK3Z5)$GGDavwRWZ#Ó< *HЁq}H͵s&GbPeo\S/'cC%f,>ɀ~<0U˙ D`3mx\`lE62D㙓GSP˨8كŮ`"Pn%h?>nG\u6hcLyXiiе"I3&Dz'-iZ,Sį񆉱q3ÔbȢrx  yPC.?C]j$Rfq;;2X{`>Ѷ|o25c,MvlwD`KwCO $aQ`֊diү~ݵZ_IIk"jQ3*>!ƞSxkWqSh,{i$ b "Wj$p >${ xU|"Xgīxvp?7PWcv  *0`-LbNƨ `=۞VMn6?EejW@CG_veZ 'Rݤli= }l쨿D?>_A/PlQo[KQ+;^q[3"^]ʿmG*x͕`=}չ,RƉByFF)@بQ8 I5 v/CSvK߷{%Ym?T=4lEV_ vm駺Vpm8 3oXX7C}h I;ӎœ 3y_n+O젥L` f-J/,Wpp>8 onaa{(Ifϼ. P<-/?2<}b Ouz_A _!{y#f**Qo%#RhJW iˆRZCے%oy} ֜{u!L |8#58+DGnT!n;dXM :r;ak"]+7x/)؂Uc DקN?4XKp۳DOACE[SWC᰷ z孢4(> ;6{ _51ն<GT0ڴm̱2F' SvB<:2)j MoQg&7yPt`9- 3*0u Ǧ N2cu^?Y\l4cݭ޿sQuo}<7Tۗ =lsݱKkǯQqPI8'PTf5Y)ASSKp *AhƦzۮ{SAte#;ye+-go5mM+|ll,Zg+_mԁMɍd w}MyH&{ҞҪMi&tFT}x؜V س~>@k,{p*@ș (pF'9mS(Ef<2QT,r' ȴDPTO0;|I0wDk`T(_^q<姑lțےj]#w7 ~{r}ջ7i-{‰T{ŷFW:OhR䍦Ov&$8}[[5edC03+"7hW<AQ B`H !!eebBH{ QH/zȋ'/O?qi%92ܾaQ@25r=7lluFnAFxok5;_s?0kXBGXc PL!װ&Kҟw_H˧A!05)a '(Xְ=ٰ(vn؞Z|&Se:hRRְf*eò(be@s n< !pi9:RfM,lƶu00\E\2sɑvϊO`rT1%X" +xlx [p<)!]W*X.Yu*.l6%XhYx;HXk;|V=X`hy8uzkʚ ">5x-)XiW\jz"[Ztuy{J)yhzG~`&SJšPꖌNT~ԝΡws'[L ;X%WQ-J餏(Jq?Bo-LLrP_id*ճiŎ.Xo/K7]jR{'f*/=3F'u߻!0$6z3}Zڑ٧0.'xjyd Ͽ̍~sOj2&t3{Ĕ/_ٸ9 v J}T*Q@t9X8+ g5?ڵ,4XjO忊ܔiA]o< 7F8P\B] *eǼ*JA+de8M!5ɅgB)J2>Fhf|tڔmʇQLܮc]6k8\- $ˤhMs2"gT;3<2$%yVQuZ1B,kؘq5kM]}#4-&#Ոoq?I T3V>\ B{Y^*IV{BY<zI}6,xxjWzœy{j6XG`|Lign<`ExkFסtt: Vg:GaMԴpv{!)t5`Kۊ:Sp7ژ *'Fn|H1v(*6v_,z.x ܄v6-I(ljuk]F4 J&V 1}%㔂z4SZj@m0/XW}IY1Y1>TKJ3K7؜#y@V[egb`E Vb7:1&wOuDhJOъ eBc03t#§yPW1L}?ON`YЈ mhaJJ)b?}|$"6S9ya­hͻ%FK."XCi3@2f_,˫R K}7+2 .$@z\D W$b낅ɭ%DDG`Yֽ]`3Vq&MF?版5?ؠ1)òX;~q XD+0w(Ycžק0* қ65AR>?] )~e<-'Ghِ<55bt2ˠIK0`Ǒ GCJJ6Mѻ\ R{Rɨ{,T3ݬrxHd9ʿvH>kkr)!U;JSBU+ցDA(5\޶T;u!6(0&Y8i8/$\qF  ydX&M[(.0iϫ\e9kwD&w-IMCIm@$_6oò̏=Qᯏ]L,k&M^׈ De*>J2t(܊J8X?PodӴ` -w:XSpF,9x|TJZVv&*\f=6R}T o 滉vrkMLoVMIs; nFL4#\ đDRSȳVt0<@3 L-Yaf4H2:jhu 1O9*4Mlu>XUz$i16#M.$cBL 3Ig Q-ns LF (^i5iw=$7x:X%(`Zjw:X>dY,>"؛C%_5Q ~iƢ5[']d-9>YU!sv7)Gɘӕ~$2Rs'\,`=FaY5D7`)X0Xen~`Ni h!#.~m7P`ԃ;QOkPA?CZҠ 1 G.`j.>X\-B~Tݾ}`fC^2Hh`xdUiG!z X &'8z,`ʲK+ x*XN+% V0 E+B),V`!ΙI{o @QZU N(VDj ;C䦧ڔd{tO+a t/i2X`Յ]`^YG>X1=k-]!9hCFXYp\{A(]<,?`,YR D)*X(Ltn,ZK^HQ@2(~9gB| -=W:@_K$1`E56!ǂRdXޯq߬P_J>>Qv?%SjASd3sTz`W8_`PyU=59yfp H/UMv<vC+9As:L\fm[,ͪ\`iKy0X%ْa)3XxfSZ`P: H( ,v~EưvV ]˚&\Yh?av+,KkQdEM][~2<Dbp "w1v[Ɉ뎂E.iE zk袽K{YrwSPv=7Ϊ4Qu/cBrT0B`c? Vj,FQD t‚lԅEJ:¦Mzw:jY@C`+|%XH=/4@ PcX"N҆I E&JJ6K5YK];ItU#{X ?NM}.X ,AlAd׼Ha}s29$$'B_mn?$&u6/Pvou[U26dR) VYK,oAsCՉ, U X|1IW.p亻;X8Eț.a]ljS`՝DH#z[Q<pP_ +dC@,S7}^^lbʰ^l&t7B0;Lٜ Mnt_H8-IC~׋7mʝ!$`iH=y# ƒP=O.E@PM"Ky_y3ݑx.n iakA5Jѧ, A'Q`E9߱`_ M:A0[dٚJȀ*mkcQ&v_}AdcMd%XrD)ꏒ ^ОR s;X)D[l02 ?h.Vy~!0U6w8LF4mU5Lc﷏ 9]n0b{،I F)G:ՐgϤAs<,`PȽ♇481K,$M6c(vҀH齹Ēh >46MvayO@d.p/#]T-F==X]yz좂yd7dbHs+r͓-PF`)ǯKm_"?l0ChLJU@+ǜn$77m\xPX:x;n]U`б9( \q4Urp8_[`Zy{9/yۻ\.$XP Spq-LP9SfѢK`&Ciᡴ1EzFGQ ִ,2dl5XG4*kCM$5mJPqgXmcļLl*ƔGI@l Y[" #i:7 ܄XvԹ,d-$Et|'!}0 %a`吭Nj{u"{z y|}$ӞGЛ;B;7Zz3Qh(#T,J#XeJ,S0Q7u4c"~1T5lp&S;ӽ1ɥi]BUJ|@mΟ]paAOXi5x7u>7ߪкeNJC (\ũSͅcNXHLҦr}gBQ*%LM{,۷U/oeoHwZ0jC* +m=-O PI mPᴮEPoݙ]GL` Ax{--]qUIѤ9Y$];5T:QJa@6r=Rn3lN*ݱ!CU,Ҁγo'/kn} . '-ꀚ3&2Zmy2g>ږݚ?q䪧5#IENDB`vagrant-1.4.3/website/docs/source/images/customers_small.png000066400000000000000000000263321226132634600242550ustar00rootroot00000000000000PNG  IHDRo0PLTEUUUwwwmmmqqq|||vvvtttzzz}}}~~~fff~~~~~~}}}|||{{{xxxyyy~~~zzz}}}}}}~~~~~~yyy~~~|||}}}~~~~~~|||~~~~~~~~~}}}{{{~~~|||~~~~~~~~~}}}~~~}}}~~~~~~~~~}}}~~~~~~}}}~~~}}}~~~~~~~~~~~~~~~~~~{{{~~~~~~~~~;tRNSЀPp@ 0`   ! $ ق7mcNThH|(w3nȴ>z#Xǖ[-:+d{YK%^j;4u6oJ8<)ig*y/q捁&'G."xkr_9aDV,ZÈvL¿Ӝf~l?AI2FW=Q5\e1UR]bCsωBO}ɕ͊tMSE˧(IDATx^ tg_&=$D6D$"!$]D%-(U}WK-UTK)("ڢV7Ֆtu3$ϡ3s$'s߹ѫG56FN6U>l㇕V ") z=ŧԁ^>vi[8ɂ,TT`“P@F kCM^RczdbOy'$G N2ܱ4 Ŭ4I$;P"iqΨj|]]dZdfi}r# CuSe_+Wk_ BZ4QC]{bggZ=&P`տv맬Vha ,ƭ0 5W²܆=ՕpY1dS*m[ :KM7cY0U֚SRh@b! r`py& -LYLS帧ו7-b)jڅkĵJXOdZ-f}3mYintgVu?9Fy >Hٜ3@pͪYZpI:a^v'|&hL\{*տR`J5J9!y Zj3ؼ;aFO0ゥ%0·o t)DFGOuWov_`vvtujygOxO ڜdZ99YNiڨO)[)pnPzap@K͹&F#_0fr>_w׻AcZ+SjkS֤=ZU{Ҕ 4d&곔>N(PRaIR)/ܑo?Nw(+a\ځKK~q!+/j](%\'ktC ٖ$[5W-]Lk<<2jybNHr4%drh zTj1H"FDz  7j/xj >S7u^ztM eSZm(l>?ԵZ:ԭOZ~ R$]7Zwr?C f!_ eMr~%'m2>@&mG֏}}>sx??>f:ȱMIx3å&Y`FM~@o8wG0joi/ɨ9"IaFJVjMu04VR N!VfW]6@sH6lA y >!$k6"kN)YkT3<[!A?瓺zBzlEF'#*v*k=n+E\kE_3$b'bZ'ܙ`Nz݆^-~P }dcȴ#rIFo-2ͣb75țb8Any0VdZʃ1-w4%At#y0j=6.dXSC-&ȸ#b~o]gDYdЂG h )u9 Ta`GIyFJ r NZ0 gX?Z9jųbXfT+ΰTQj0 )|HR^5uY'zڟX 4(SkU*]ks_RQ_őP2;g7&}U*ɛӝ,79kX#ڸ'>#ƵN$[a'`yekB21ERE1|ɖe6kU8B8FpAᵏ;1;4EesLDޟ6Brmi9ˀڏ)=l廒w-~aޖBКVV 1곑F橁P}s)VkYYjzu[_4`kue˙$Wh!j_/E nc͠xq!. 䀚Td=ZQLI_İƑ;zl ׊ Im7KJx}ZL* f2M |ETVOjfY(Epۡ{x[41w&tKku(_S?ٳcd̖v1z DPlQt(_k"kFRsО @4.66FeHZsmlCkp2{,4_}Q9E\I,gav":Z kFy:Ȑe_v .J6.W\ϙV|lBږ~wo^K֡ej5VkNt%V9jV'}ְu$GmZdrV+%(g>{AolTkK2u/: pVyxBCcEje& gT+]e46eYn܄He=.@o$LL2(jcggg-`Iv$Z˝pҷ<=ZI1)oikUN܊S`e_jSVXIK_ A&($eRjTKjOG95P4#_C&ZQps0p\k6R@Jmh_n O IWsԺv:5 . p_,&\к>Y%3ٟJuJ] x'0_@M^M=U131ן ]?IɨVdR o yRO>jEM6Y)a˞@ f}T 4&'hІՁʎ]5Sp9hl `.˩m\/TdԥT@<K$˚ QE'jPx!;a/bZ%ڙVLHGOԊE$$9]֥T^(E.F6*rȗҲUF9XU*Uu]i൹ 2#Um|>rW Q܇ )md1$V[3VJl#_暩jDoxiDt;7YiDdp|,2}'iUW.ߓz=-H {?Kg衾X95GړFTu\j-`:M8r2bfkY\5w2>֜Oo`Z~ma`z/摱-!;?Ai(A]"9PTKd+{EKNj `ݟR5MY?rY ΰvq1^>}5 R[b̘+?)i۰_AZ9R71fɗNRc;:j9U=[Ypretyoh5m0JJmŞk}gZY]\ )=)8{K>7FϹ AT}UՊ9^U|¸VBc! XƲt }46N{wַC= | ZbgHӖ;BrU)Őux A0ϖv[7"MpW =f(3-_RY;weq?h֌*HE5ˮeQPB!A 1DS-@BYTՂ@{,^aQRQ AʣL\s8gsJ=B[z96O~|Cuda}*`ZbF|b*@P1`'|Go;*Vhwr;4P}+YM+~;^,#$eX_ KR y͇T:_?.U?%Jz>#+׼ ԧ|)Y# eII\}'[K&4jMVgs>|`aŝٰ8k6ŜSHXቜ6r֛To`^d Բ:ïzyΚ8.#rNժOu gɄr^}fNKmRmJZX|}zd-:BR3|-g^r}۵%4ӫCR :Wu>!ᵾ|RyxҐ>VRrHZsK[,>y5DZU&Mjx ,4O1Q݈%V>33]YRnSlp[O~U82$e{<2phLǀlMC4٤41Là6@='|UCIҡ?Q\bbcQT|jpU? KYPCEw{+l-sbϺ+ ޛFVH?Ń/(V+rDCPdRp7mPJ-/g]w+"'S5J2{u(ERbT ,L:p1#U,E+f(XBޡ]Ssxv,hp }3vx."oAt".La}$ bgٵ^Fo֣V]b>Cz֮k5ҀUVpZ=8mCAjk Kw7B(V\: ` AڄjFfk%3d*Ph})6n^0vdؙF.C= Rz"kpIE=`)#St(dX @%v?ݶZc|I@,oFQzGs PL`^[wA ]OuP=o(W66˵Ff6NgZ8myܵ⺾9b%Ksu6d"OstyXI](^k}{FX͛V'/O*KE1Tr'-Ku0p\㓻zjw-=T, kd8з=J13g-@{ӯgkcHf#>Ѿ$Z_ Kn&EI֎ŸOzJh]k/[kDY,5pZr4l%^DohoQ @.P,ʕ8jM|AF$Mo А6J{ w38%jC2c( G@p`cTeK%fD<ǾfLH{.O{ݗ/hs `Vm?^'=u!|;j㘞>?˯QZͅTZcCuV.bzRmr`kn.P+T ]Fe3%*d%"p':@)m(V< *Pki(kȄp#Y*D!ZŒ/'=P^-o$6kB|1ZhY_ AG6fjZEW>_p*PeLSpds@IUR7TB\bmY8j} hzQl6;mO@eAՓBZ+RPw»cTDU^'z|(B(]e:P 7QZ-u0KhֆPT%2w\ gZ`C?[-VD&HV?NCd~Teh9N#ݜ]։N.l6:Ԩ5&@Z}S+'PYq|jC"K"?{ 2eZ֣$гzw;Iɸ|rGO6>+# E[# 'yZ!pV;]kTZݩV_GyQ+CF?_Bx*_ZV?emD'=zgkfjaLc?6zZsX2cZuPGYb}"/y$/Btri5V+G avMq$a֩ڥ [$֔*;{k6_|ܼ/LzB ѝo)j2J ε" b0v6ηyZםjM:/uHʲyJDjmJ2> L5:huֲZe._IoBr[j~xUᒯ(I,u/G%&ʚhrZo_h`[z7Z?_ \c*EjQ? WGd:׊Z(w-y?f{$6 V*'Jm8_3yjVC2,*~-6 oVbp ]-eԚ3IPڤ\H^O+*A٠ LC:w *mbހvM-KXL%"(Dj]AmCȈ[ԺOG_r롴apXZ;(}{`r'֊ȂeHMq넛ػH`òAԮܺiTj' *[PT6A0^=OBZq&d^sZ2_xlk0'U]^!hĞ6'^"Y4PrU=p>L6`y zjOmUH)sH&XԺʯI4ߞZ{pZͭ۫QVd ^~ `?C`ƊCpaF֔9<Ē܈!g[8]_Vq|8ZMJwxҿ7M2|{Ԥ̎]/Lʉ[׊SWdIZ<E?2EPIni]FhbFJԞLn \J+~ `T p1,j [E¤VL BbRZP˨ͽhքU^ !8Ћ'WK{ I$CIkK*Ȋ3kRswGC6(Q+𦍯V/.?@)NJ2{:fE*!h/Dr=u Zd,e<`}({H~p3Fo6H,,'s@qy.}\'EhCjEY(Ykj ~CR : ;{ 7濯Gi~f4Eʷ*6u -xXw (|ljKj}`YB)Ky}վm8m9h&o .}#+ z%;j]A"P@KB 6@uC02ϽBi:xd#>|h>x]@Z#MHln>5KoV q位埓&nP`'S F46jŮѴi7S,Q-ZoR+*d<عG:FpVήޯ@nz)@|o2}̦n7~B 8j]EQRHFSيrorXj@.\$li9IN_?I9m޵syRƠǨ%u LHŷ4!0S(_@8oc^z1;ϒ $'^2%!̨;6@<("G}eDS0JZGEJmp X']08*y U(u:=fXbC SHl3j Ҙa wœJ]6/bIZR>vֹH\uIENDB`vagrant-1.4.3/website/docs/source/images/footer_background.png000066400000000000000000002117761226132634600245460ustar00rootroot00000000000000PNG  IHDRWPLTEEi "4tRNS  , #-*")&+ !'(%$0/.123Œ-IDATx^j0@Qx$$`u37C2H{^^7!# cSr,󔜸}U[^܍Oko7t[vMlGco`va@)qɔcǏ˗_QV6\Cojˇ $M/^ʉ+mXvҶ ؿҊW {}_.i51؟'nm]cjb ˔64s ]oi\g7sэ\ݪESY<絢}\|៍4=s.9Dɟ?Mt`ﭪhÜ뇘Ϫ{ucKemjvCSY:#ގs& ;gZUݎ:ZDׂgi;n;1@bkXi.ib]v9ʻ뱸[m.[UIb`='iW0'o\Qj.MI[Աpl$]OtUR Eべl*قºޕ)5AfmUuddAWqӶE;h{J h슫XӪvyJ_ Wh6UtAH7'׵=eY\i2;*؝FirLylWk<mĶrVAHŶSTh2AAK$FD!ädc)ҺUgp} ml"~Eq!VbGYbe1%F0[d޳6f ) Iqdl1daȖ۠.n+H:&Sljmsy['8jFь펊!jlR:dy +ak-ڍ9-Ólќ&Gֆz6.HFl8ݦ!6R:ڤʸ*;:( [`WZ:A?>Ŷ=6=sڒIhOq,6{>e2,8ih1lvb[nۙ--4 ;gxz@l޾6¢Ƕ@=k>NCs\ Z+8vۄZ~0["i4MLhG1yc&m_lBlZ8M6?\-4vA/ fsYf]qtɖˌɱc rt:{]ds(ݐ-.cXz{Falr. )NKb-7vmZxoY)ǯ'Fn٪ߦEyb1͐4h(a -å9dvo-Yԇ3Vl3Om^ , =Sdh4:Kc[ 5"B6gSvcoۡǦlA8߂?h4t+ [`h8Mre7;>n ]6}#;XF#U(A4_?-w(`)p4~_i[޺EFЭGw8hgA|q{l)hg-׃mI{fƗ$FѣtdT'w(u|q~#b+ŋM:% ~[~V[ߦEo1|F0[d;I#cI6?~9N|'4q60qu9[r{[3Ś-]hФ2kiY\^lF`^k9Im؎M; Fчy 'XSհ}^+'Z`>vt-&6?l#lGWd{"F6Dv0f<zfa(L@j+ybPb6r<̭Yh4%!p&΂-v&d66kڔHF $Ÿq~B[ܺalYBvX*l.[+m*QY@Ah4! ZcXVa^[-7IڕN۵k"եl&iQ^fv2Z5 $ʽz!#?NWiں!mD6{mkB{Zl% քVd]XdOôk4:c"dl$ GոBb͟lI=[Hp.uUF FLr8d'@Fbh-tl6e?ƇϺ36l~zl\ $ VՂh<k0!;`%6Dl8N =uיͮmRJF)í{pyk6oA`IbӅhAh4l6bMHLAY5v]j;b} !\6a[ͪVltX Ftr #?M,?RH p*l:`ّsXsxv6߼o}d]xjlyP+tKlȀSv݋m6v>t U o-6gl28q#L]UX3dF d8CkCrֲԖv)+Dmr[ljߵk4: #gY,7^lJͤ6+f;`5mw)x܍/Ђh<3h4촚$ Z"DU:tn[mm|XO Fn6)'bk4-pn+ }|~l9BS}6r[Zz]Q߰k4|1jlO$|I,嚈\q$=YW2 ކam+PLVfi=iAh|ѭFxB m:`kv1M6X3zʈlqnˏLw6j>5EƦeh4 ;PBʭT+Ag4M*ZcY M['ɉ,p]&OI۝HezkmƦJ26-rtFCmcaW*Ƃ"dǴ1g=0kXqVͽ݋m&ɖ2Kh%цxQdxw.`{img16Ezn ~h43\#>Z`w ,6oq]H>:ܮ,,AϿTm`=* vu]yK£l>5>̭y FK wq}, g}tee,*&,wH^?^O>`8뺲9lmZ$֧*;pY s}őE7 Fض&Wq+.J ؤүҨKYe6 b@T]xtR: M˄m,6$xJs۴jCm<ɮm/hv!6f[֊ ag6bEr{|ނ9_hod-hG8jͼN%yj6ylvdW0&#l폎@_AծxZ_Wl9ںmFa-[StFraѭhb'ɳvvuLuo*ȮuoQ-ϥNQ6g[ەƦN-Na FI >?,CQdQe@vRfwZ`Ѷ>U϶Llѽi F4pt c2&xm#%dSXӬn{o ̦Eٰ=26OXd/Fs[Y 9d"i~ OE*1+lUs~dG?ԜҊbUzy![bvII4-&)>Z"kئv.b}Yf/e,M!򂾻]m?Zv8twnЦvͧX;6M'h4Zs@coq"Ti YfemVp+},ۡK苨چew|{pwgy>C#o6&O6c,˝h4sa8ODg/f+(E] <ڶD9t0y2'kgT#8MmƖsZ>q zQ4oqV ͦr6yׇ@bۖy Ge :mx^{}U8K6*[wA; 0(lFIJAh4:$%ߡƭ.8v[ 1M7L-fyb[|A.5cY:*Z:tͭ"cCRjgXkq9ذvHGF!)qow6 dsq׵zSjlSu;\X]?Bmn vty:$ةF0{,NH?_ƶeikFlcX7!Y[͈v.g,j,;Tl⒱f ;F-ܿ|B<.|tfgKlfձYc7XZeo`.V{uY7Z:`Ւz;ȭu Tvv43AF(3hȁ0Yn0ZƚSZxvYstL&o+^֬}._vȀIa}vk I;$ةF@nZ42qhtH*5ȭҭ-p'66?M<,=7=d 8֬l"tP u^ծVn*ZXhmbZb7 _9l:42GT$ r'nտsZ;'YW t8gr5ޏpԣm`};ƒzZjb=-`j:ɖmv1?WT )uee6=Ge5bߐRg݀Uݏ)ߜ1ɡ1Q]٭~޶3H:Ħ׻YU;dJjV'Vl0kYatBxTda}iV[VgdmʟI )!朥hi6뗲,|ckͼ#XVODmoa5^l|jFFV|ArtSšm#Lv׸xm{ln׷3kZ~oY$൉fAb*w?\h4Ԋ]=dR[*#e,LwV Lb"[rnׄ Z~cG[ƖۛX,vvi6dyhQUXଙblap!}3pps~ߤC{yU0`2ﶭC[E*mUɭn5‚>n5 Em;k ƗE,+j-p>KC._(/Ku}^;`׹&2++a0[؎_|^h+DPvd,p6 .b{lV=RhTq] ѕ-ʮmb]W:t`,_^=0kE=&g񓭥LؗUFru2d(Vn;6!]z>*$y}Z9W,3ExF[ѵ'gWm!]'\0kwtEoXhWMԳG[,Hl3olb5pqWMdaC.hGW(>!KZѵ'v>k_>7*al#jӳY+Ղ r LA v]מ;ln G׫ٵ!vS"Ⱦy] XվJbGڻ lԶynUjҕXRxcۊmVeَ}m]}^])a ,byl;f[h J#9Ėڢq6iuaƞeNm,/LeO$'n|^/iA3=)9-JΟk4:$Gn ;VOOwJOO|6=v0 p# ށ'ﱻwൠk49 Gt+{ށdz߁7؎W۳ϫirԣ 9o5 ys;}] 80V: 캟+ugKMu)_S]W>r5p#Gt&^pI9$3ɼ'#'fs>r3OXo꣕uzLJ%!#][#=ivwx]牸ukHx3\nsX6 ~O){&Sۈ-G~Ʉgpm~-8k{X&Еt&˒}Tm&5oK˭bwg c*ۅ6e4[A/- +nn6Ƅt0oT6 $ r0mUZ FB‚wuHؚL0hm&֧oMi\` Rk -mR݀E\YuNiyz*gk6%[нVk\X;U=GȴktHj@A'],! AkIXò/<{?]3nLWݘ?g-y֜ajꆣ6Y0Eښ'=0we5Z6oo@z.m.o\5:$UT_ .R[ٖ_9[~koYlAh&ضu+[݀Uw,iexijz-}~)ƵݤlqdízU5iAy k LF z,.ml6kqlqt{,CگYǂų5";K&*lQCVv%f[ e:`ae :jX 3 omVs Fw-*,EJ)(Vvd-ӡŭ!'a]7-MK-pJ27`u#WjS(뒮qMt7`֛XFȺ[ͩe[U#O!Ncҥt.=zlcaV{l#vmFZCl*Cۍ0&ԺEXZf2jڊZO]CRenv)Yd+rXs[YqaCpl^ \7cm壥N߰;`uAmt))9`w X%7w\mX a[#tg~FoP5zxTйnZ$Vxf[-31w\m!kښXQ9}\^d_cN]I,s.a*)e>[,3z'"9yFAOAhɓ{ܒnsj3آ&52 86#,p$ثؕ] pTgɀUޕ]Sson X栉 k&cc߳Yf3zl]A6Fj3\I]x_An[WvVmMu"&“]-m笷x9NAAyHA皲?(ZZѶbUӭn;cclr$?.mN2ͳ*i\v T;2wLĵM7[b`9UU5w$*\tIE6}..ؖĚknQͫ- #9%7Pͻ>]6Id#nՃm{D`3ZdM%EۄHj`j5);ytnXlln;׭kKZC=ؽ7X>}eM[/ϫq]۽kv/vl{F>y]\Qv}^d7ߞ|[MVe=8y55s[CȪ0 ~XxSvSg[X3XXۼ.|: ޿_qdچg[\7]nqtt*Kj3V_vI:2j_p5c +km9IN/Cpw rfUNbgXXc~ '/mrcm=-l"nő5|ץ6;+yd} ]}Ey%ڎ叻WdĦWkm~!'5+MvOlF轥j)_iXyAha2뛷+&XcmM-k[gQ`uHk`;=YwTI#3z˰zxu^m]hef_S(>ty7xn-X߳7ZdGLJ!aVehObiP o-tY %s]m6NkSb]GWZ=pXCm;Ѻ> շ}1otterȪԲ ;e-o<^jB w5~:BFWh([ ޲[r Gf1". 0OaaJWk.tt7o{o(7ۂ+cɻ=kt.b ܪ+ٴG,Fh4ޡ@8&a k3jRYd,xpm3ڣӆ=6+ k}킬Zߦ?vt-ֻX"oi6^Ut~\rVm6Z?SAhCC|FjX^뭼0#&#fu>Uxj=]UH\7"g|5Rg7{&Omr"O=O],[ƍ[[\Z;`xnK|zAh'>-6tJ,Z;"8BzlZ54ޭR[\9: o-;o.D~4zޗsöښKM4=Fۢv?=*i$ˉ F=`^ߗQmua9l<,GZ8m6NjOTy_έ|W{mfu l`ao2°mlfllu?ivuׅ َZ߯azo٥oasoCWؔjIWXb]k63dM52i#+(IW];0dlAfa_Br6筿y>ybX vUsLb,-Nl_.6[-[#b7d)WX]WZ:`b~ mRt #e";}mxv6[Y;[ Z: ˸&b=1>jĺfo|Qvwo|g_=Ue*έk<ܪ͜x~o[O9-t Fm =y0l<ަc,G6bki85{ܶqFۖ;Wi3w|Cn?JjԮU6`}c YvMGچM5x&hAנg.w,7X pT6kancc-g$]=nih[tlAl9hQVd8Ħ_o2td(w7YZnTaU2Nl|qtMČ;` LvdUDµve2?l}3ߋd7Vq`zwOkkbH/4LkW^llwS-[+˴t-4qY)okQl׭Rʮ[նf /y`k_l!Cmԛ:Z_yj>[-UͶ;d(}ӬLdGGbwR m6-Z{@-6ւf#l3"kb`!jXFl[ֆ%_[%c777X56M*Q}oˋtpi!-RM [ֶm'X+k'YۈaS Nئbcf2,XJ.mYAU&[&jNd僶g+g?K2ʤ"c2=Xk66\hwkև,vbNBCRBв>zZsN֒zt\[j!ynR_|`UkumP5f;_lfI *z:&7vK ܭlǖ϶5nkE$3eVKeApʛʋg oW;k}zmOdvvWni@HlVbY Pb{r=zJdM;@j2.Fj'B{IG)kt.+e۳:d\qεZ ҅Hxs5 Jl/ l%KaUrP/3 [amr5:y5犮UR@ÞB{88rWjR[%y/;[-hQHCd>Z|@?$4U:[Z5/8ہZ-VCEAI5ԨX^=;V5Ze΅ގzy,Wlu||tj+tYabamͻ6\ w[%C- \UYG-Z5,WGoaVca[qHEc E3€g3v*Ŷbc%ߟ G8\QEW d Xx-y-Q 6йΫvegXuL="lFq \peKG>Ԏ;l֢GqߢS)i(Z-M;,b.d2֊@l|hm٢5Zlq dq*| yUTѕr5a{!;k3o@GEC]:9Y9,U7F3 Y bd cl}kO 豩[;KUZ-[dpb;z9H b%ߎ{[>+l<Ċm L> Vp~ V)nA;^vQ-~YZ׷'s2͡o+OpDEUְ}Z%, c md x y^[ڌZEVӻVXTN ˙ؚ ,ɱLSvC6j:4X1h\UQEWn}tٗ+l{H! aɭ9즗=ZG 1i){*Q5<[nOj3Xn rc[Ҕ=%KY+6p&šj෕fl}iN5 6KκX[X>MVM,ƵZUtx۸,y-7S)VУaI4cCv-ڷpX_߱X#Q 6_gvQdm镔-Vw=h ?6lLfkm^iGX>MqXCCX<`Ll

m['Qv#mii'GS*$y@=: ⿧Y뵅W;ưZ%Kwj5Y$l'omVmƖ9;cy EC=lK@+}sPvTjD+b#r"N?κ??M޾Smd6j[|o{XY_ Vlmz=-׎%dwQƒ[:ҖMCq 9[>-5 l.\Bn=N^Q8-5rЊ5̹rk_aVY%Z ˰E7qYO99Wx-8r"|7Zn[ZyXजEB8`)?Asr[fWY*=ن:l+^lVPΕkέ b^v~x%|@P&bF230y5;>`Ϸlk k]Ŷz?k$;Wt^^<ѢuxE:Td-Bkf"cZdl Kh)Hjvma]`uc˯ֻtMVl;O;Xʫa'ܚ=^×/Ukԃ=bv(y9e-_F;0yd;^~%#VmpSg5m!1nOk_f~:e,`E*N_tY'B>lrNH[_T0ayxI=OaUy쏟';SC5k<A+™g[Kqx;0[;Iy_k)|$@E%v|);WtT@h $#N [X-w08oX,!)wWdwg-&m>_k1ŝt&:yDcKݵUmzWb;ư L^(ovoKv:2??s:|0ʤcwNT_ H>߷bw Hq*mԬn+rf|v xx[Lqb(l-a`;⽔cg.N6S7fj.Tێ|z?xgv/>vty%x.yp59ԙugؚv|l;[|]ylvg.W~#+sWۯ6͢ju?r;ٴ5~{Yv%{kn×Κ>ΟeB;{Щ>EQ١Vwa5W&`ߎ`Sgx3h]b*y3 S5>[?ۙj~gK#/wݍ闭 SVͮG t>`Ȕ<{0 sU&)7}~ vhlpLlKm:[jev϶tVpz<|WZl󓵰I; Z3ؔ506%FV(y5';XbM; Y4jա~wklO_GW gyc([{n{sL@{>`UpԀ r6Y+?(\&P|QbfLdK̬wIoD;kK *iֽ$j^[_S9g,pnmJ1X+g=Js\3Օ6߫I/w;vd ,Oq?;bʱimV<$D9,G zչq87ntcAI䚸 z,iƦ[;M7v&M`{l[Di5lzBQEʭveЊGrkD| ]41 ,&j_qoA}QC}P7eb}iaߜώeFCV= ŲT) (MtXO!UۙFk՝6ۉ pjE*]`u?ܢ ;f[$Xy˧1jRd!<{ixa*x=i ۽fyS,l)*Vf;!rVP+=$ GsĊ-VEz vw9z[uubYmagt$5e80Z-8UՎZZ'nmE9L6a$W!rm,_Uw_{y[c9(@z$KNP'Vg ; X͖G2>irK?<0&e41J?dc+x5FZnv$z@/ZVH-q}domd6Zj SCW˚v*;jtr+vUq _~kCR] (oLU-Y:mMGolYۿI&ᚸ k픆< ; ؽf^[rpo^;Zd,?,/;t$kxkg^;; ڊe܏2PJb{loR+V;aA*|xkZgQSnۿ_T`;}˸o[H:)XV~H`,v1C+vaay}:~S0 ts,;VJ^8W8&EZGZlN6?/!܊"RjXj_Tk-UZb `y0tz)[+ldv!*Rm7{Ywɺe&+!Ayջzk8x(Nu5ۚ,~6#bX$7Z۰a4;gQ0 ~'InMPTbj(Z :ڝ*-}3wkWkCz=FJl9Z:w5bkM;IIH;[l_|3ڛV$Q :r%c2V!®u).U#K a]gD !|4w]C-Obu-CGkvS@Z* 'rE61[b~jdRK0!+ K"Vom*ۜewj\sbOSWcہVv}h av(AmŶYEedvtLV#|HNNdrrӚ-Ev`a Z|VmZli4ӂwH@{n|j&JxD~Τnr%<".Ǚخv6R7sd>v2;Jmv|^n旝Iu&tU_>Va;X``9{PU7~I,6uXv$l@+h7YlJ[뼙F,`;[l"3ذtӝj Xj 6:V8X,/ 7.pWn‎MgR#gu^KIʦݟ,+ tP5~˛ɢJT``LPVf6Ţ̋-(A;{X1r2VcKWʿQkzlzX#~g2/53$*`<$4jWSBMpwB؛*&F.J&׷YjXB LݶtXk]+()$_0\Dr%!]#:8eD;؋A/rXQrWojTJ BX-dO6J Xʔ>[akz~b '-[c;!Aj/%^#p¢7sX;|:EkHѤm[K+wTpċ5auW``[j+jlV[-;[j5xxgk#-sּpJ62\T *7 1h\Uy˾+_5SH@ڼj ;(ےEEW s}EXX/u^ $E+gΫ8bvgtZPEW-j[NDGYh:%WeKu^5};5X;΢ShU_2_HU)nX!Dey-y-I-lI])V4K_Ԉui tr(̔Ÿ&CZPEW Qz-[33Lʙ'&v ێ+5T5`.{ŖT`j&іel$q{ȢX7. x-rL?pNNfwѽaw~˭CY -_g1}uȺ5q\h ]NڡrW J6Cv"õZ ΫSX Bvٖb[dR^GkVysZ~06Z~.Ӵ!ޚ6Яq&]~Pp_؉N).cЊ?Deovl~oI`Õ}6_ݻ-W74>ig+jĒBЁ>[9Z1_(3:[<`b/Tѕ[[ΜX`9S'fGY&Xۺ-y5tJ`C eմҟ+bc ؎joX6rZ؎M5bHw R)6w[Zr5XG.dӖ~ =9tGtZyDy-5(χj3xA֙H"ZcTdZpEWjrW'"yنgl)|-NΫj8o5,Z{[>pTmM_E J ݍ~1ŝbUE1jnKk6ۻk@Cݺ(ʛ4<#uFZѯdKm~'fa6sٙ^Oޱ;N6C#&[2O![E)wjzWn {ۡE^"Pkh;*&+>QDjxY2~5Ö0:mv EyE ]c(XcieՎ~5E ;fv8&{9:]R":hwdH;Uɶ,a1YlGQvk0ilMسx;XƱX;B+|ED+$s_^gt[n'o [N>Zt\wVlYN>i .$`sȳ6" ;e+Awfomxv6s:,vqyV'[,89<1C.̥,ok~~^~]P,riJb`߼bR| BB8af9%VEۼmW4^bsk<V_p{. ʹH} ӊQlv$ܪTV\loXr&:6S_onJa,ͦmv+ \}^i=GM'VfQ ɠ$R~y9‚eev59ʒ2ОXOD)N=~9T7Bh3S_^`Pd600؏f8[l^J'םn>Pk9sb[-ClY˓ݧX:68(pP6_rjFg&EDZdVES×֪v5ϵd/yKqHjf-\Cd^NW"#DzZpKkZR[Ս={H[ *MSF_m͋@  G'kXTV;۬-Z nkT|e?`dFCԊIEakB՚Jn4;zc"{SE_;JHirPEl9}>`HI(ep]X{F-R|8*2\]﷯vLˢPoo7d0|8*[>1bHO5k*0DZ M ypD+vs b^vle-u/," %i_ov/7ֵܸlsoV%ڽO^[_-bkɹf)j1@0@8!VOxHu-ê{8[3@qi͞ZlG4 |aIK92RVvDO+^p)vGkzX^dv!#G؉Ŗ Nb]~kmF8 r pxW-Z{"{EauE2<͖V 7;TP`S`apT|8+V^;]4'*o CQ-ip2Yn ^{$~Tmw/lE:I'-|-۟lةUn_c-V ϖX-ևWŻ$CNÒZ]s~r"ZScʹa7%-E{ 4ة);mvu/[mY,NCd_k",Ζ5QwXr Pu)V>`W NA8!LB'X,@GuZFڮ;鄫¦9_3`$KCDkPOsYKqp[\>f E_ˇr@状iv/™YkOIX_>MoO-@p3 ") C:00 \3jMeVWhp3T϶`*=*eNO m5<%ы5ۼVbz:ɢFA29ɚ,`MжI#~sY5kn+%/$+lu?$<\/eǯNQhvdxB2D - /8Xiv/cw/֤Y,V7Xhk֦X,tX\0XwoLdԵdVumE]e*\gaME]C- GNVϳv'{H4mB~?VNkvj4K-[ڄNۊ^P5X ^%Yl볨4\UcKm+5lY&Jdܜ:iGV%Xl D*ڞ-c ÎkNQ-yVmh(1\𭤊x G.~2[`a/u5@<_t촗y-F+J̱TѕrV  HVz-iLzmk1 *F UUhרG\,ΠL\âk  <ъbmX`(ɮd< Nrd"Azw1,N |w`rFy 5vaEfR5X\9Ql+E9w|l j(.M,pi |tZeWr#-A-,*WL[ኩdaqN j&סE2׀CJalt#rVSd'd]cXDNJT om8[GZmR,yTIwZA]A9Yj5 4LF+Faz6ʖe{kΫlU!44m_v747`ŽeH%dt:YD2)3\組[>\`KK(8rW=m^~oֽ5ТOv.Z5rz-׈T O \rWKD@ÑLSUVyȚ^v;c ۵[jݹcyBLs [zʭX:{ d:u_$Yk ޑ,%%rm5X ّ,cV3E5{,0nN ʭٹӎzى\^;Xk}h*omj=YEG_b\o[—XVkQD ֱZQ-Si`- t=$(JǂlIvݡ5BC5ExiX̗5Z)̃5_iX-T}ҁR.K0SNsPUu`eSj9E SGۢݓ*4q^S29em:nFK"a圬a}{>"l YLmGaB29N,hi}!ls7ۘ-heK Vk&kЊOZ^:iX`Ch\BV--E,-E+^Do.+ ElGu@ڸ$x5M3OHlY|]/B;jtCi_, uRZњҧ&avr} )nN 0aK a^)|XGx$~ ('eTFUhd2./Dϱx ayzP޻Z!"  v|{ )|dr`F .ֿ9,w }o' {񺄽E~BHMŲ=R[!boTZҊNٯу-. fN[BAj%;xB2ejOZ{| Îuvр6›m/-@?/G6#loљFB8f#5'/$['yA;~2Ӌb֪BrH=d։ĶQ/V-^|^ZgZ+9^G/Cִ+YK?6!.lɶ"]8hbYT;;,S-n&9 5;ٲ&Z$kL_3ۉJX:1,A BltS XlGPW[WrsbăhtF "vlGb;H&'kl96rk`1kz%dvu>6OH0=.9JKY_cCl7k*O+gU/ImfbowY2w^oTEYr>±rSR{{a(wloYu 8xXCiwkkEEk}.3*TYo[#eYؚ ZQʟx]$K[h+'X6i>ں-ǡhgj/ -ˡ~M[6~U+9UZ\vy}t>P4gw}vj?Fl'K4Px-Z=hl,JNvvYI-PZe64v!$8zqYE *>QW#<[jMbA~rbOc`\YVKǓ= #&| ʜ+Rʖ?4 恠*,L٢$R`pq]DbG e9H&;"N{YZE?<4{qɶ ZY{x aUSi'rllȢA2'5IkY okΖ[s|_Ζ&!. z[~*گr;_ lfMkٟa5qN5 .>xvS+s~~$[aߢqxZQmesw + 6i|khcPY5b 2Idz5(*Xv-cEM\صMgBt/Pcyv KN8~xcvg,Tx\3S(D$8qKbgpp!j*D]mkn~궶p'tԈ-{p@G4ԎZh}g[Bhwb;LokP4r#W; X>Hu?kO1/j -jBlNShE2 LF+U)%K a}谛 ʙiQ0r XއmvKֱC8%ZVw-"w;ZIGQ t@/,m_[\W;NC+'Վ"$i62Xհ$V lvd2_uÎVaah4uRJ&Sk3hQ}(h|8*e>`]- _ ]s {3aVՎVmaeM Hlv$t#Zf[eܶ.kӬqRVZbu,yZq:߶Nbͥ:,R4nR)VJS+$vΖlq87(E+oQ0lk޻,WW6s-:RlR,Ra7_*L~9 6Ekˏ䚳V~nBvA=-~StvYװ/ H&+(!춥e ٓmIe~*oB괚܊վ^[k8c߭)& lxZӬd2~-v{5bHVuEBo#H)k嶅rK5q˙qVu4MnA(̭G"|8*eJ" ˏK/]9-.4u Z=@ТVJ}lHI&YTh@8["ZJ&s| Zab;P| ;ZN au~[{-UمV܍"Oܖǚhg)n4 LV<$t6:"dq8*2ʭHÓu$%TZF٧.{}Zj2aVtJS+-I{Yhg:ʖ[K a 4^ʭlH5hE2yXN&,mn?ȭN[DZqXE+ 6 ~k_${g1{UUX¦_XՔ Ewy/vk6$tʖ3bN,%C3N;&M-oި H&G.`.6CjLS*/R醲~nBz{i8./[\heۊBfq8*pXwUStS-aQ0ou8ZVcyw|!l+uҫ.~M /Wָi5qS-f!(c :oUB\@i~HP>7+$?YހζJ_*+}J(Md0,7k$A {ڲآ , -Iv8:,XqwUF2Y`rPjm*y޴kn`S_&U@#ko%9T[ŮVa;xú nck {jvy?kmnLaq_㵪|;m:k@lۅVuSز}dsڹ~1~WRm8~GLqU{l/r"?na\_Gg@4(#KW?}‾\ K Zy@`+Gٺ] vh.z}Km{X@ŶZ= 苾r)F<>=ϔ{ŴVa۸[dD)wXɔUּLcKWN?-])4^2CM&)x=oro]SOע)/St}ha[+Y ċ-,|:,G,awbeeBEqVT 6->ĚahqZ! d' X٢Z([2|Qܡ/N,mEq;/[m ] B־OoyՂ-olmvm Uk`˛`he}޶^ٶ+۶f`ɦQ ;YM5PɆpʕo=$}5ݶimkWmk;mkʷqa0) eҋҔ\F\Xqݶ[42I |l-|p-k'9O8a~RaѺ<[+-"xԽ@aeem^ 쿮LVXrׇW%ecas=J/P_3Y 5_C IZuvGVPU(j`貓$O9E2쇖oE2cVP5n6XOXWQU\{үq_V^Uh4كCxʢ,1vH=|8 p%:]Z~J:,R[zѶ'ZMɲGيI d2x8 F~8KgYY}&?em+l{2<‘#PVڱUNV>UJzNSky;k<G" T+XN;##&r\H&cLV/:>uǧE暾 ;j7!8ğx|*l%ǧݒ ?$(%ٖ[9-/i/i5b\K7, ,0Zέ<,|kﴚC6s,ۘjY硬;/URB8k}Vl2R=LlrkflNLjaX6 qjloKmyYwrSR{{8Jj/ww^[U>{Jj}:s]bi[ݴl>`^OZd-4z.W/v9zjtcEzҌn2>{8jX6|<H6z9ɳ Y#w̵m̗Ў|=z/ouDΗ^ϳ[flW/Kïd쪔Drs`QE4\oSp}nۛũ_.]3=١Ėg:e Ybsʠ>f^mn}LXQ^es5joÀWˉS(lӮ^Ub|Qoyթr*틭z.K5Yb+b61.,NcIg}L>d{g{'Y Qc2@bU/T,x\3ӅKsX .dp" l5 j-vl@ `6LIb?ƮĀ}MeqY㲪M4DZtfUJ}c=WH."[JLw%gsz~jEH 2pvKry6`̶B'o?/Wkz$;lwU ^%DvAWzf$غ`q'a)VbM`d2Z1G^lHm;lar|wv5s -\akBsbMd Ŷ;ob2H;:xշn[3(!|wc>-GeduNΒWzlە7aVdöۂ-~/5e5e?̖Z>+2ܚ.;!;l)smw_:myX-ˠ]\BltJffU~RDŇY>?F+G []:܊ bx,WW|8*e5lQҎQ %K;uYNZ܊9KXԔvF5eCRvgXMdn)hhc)֤/{4U)ez3^l7tvڱ׶ #-;C ,{[S܊7#] H&Gkl3uo]A9CR^bZao^{K6*k KjNَv絚l0  ccDjXM1E66ƆyմZ?wɑm9oĖvYXŖvuYS_ZpTXu)'wdLi%n(KޯG9r!jzٵhFYЪBhy–܊4ömi+T-TkO:%ixSJՔ=&n_lc oi+ܯr$ڵHs"l`9iSUAw"*؂O)ᨰfg9OPd2ZfLZemmیr%;x6hly#Ah9?GX^RHEEVVZDDAXGr8(V,*G[^_`qv_mAnggq,+Xff-v:Qa桑Al͏C.Qn~ U%JON~xۇGǖ>{w/x~}VcO4r۔VfikۍԖXj󉞏Z\xeUv أ*أj=G%wq6Rۮ6m4%ķE'ffqd\?g .p{ R: 6"l7}xVy}nEWV/1vi94J[ֈ,+W XsK jW9,RlGxu~˓ŲTlEm, UX-.a~:EVJ .pؓ}X*;TMZgN[>$Y?ۙÖA{e3Sl$ Y ެc^MWK󪱟;!X"k\dQo>k*a[ؖ_iAaM"okډj]q'EՎmv$2\ [R۳-]l9z͵_jbY\}p [*Gad)SuZ>ReUlgb;il ^nLvV;l#W<Zd.WfR5X$i hM?tmBATC6 X+H[v'XTtw1v\~F`b]*R`;BZ՟aQ#Vc[V~Kx,Vm᷼ =luwo۲.bUœli1y+NڟO,`c,XQ0.`z\RO,Ltb-YE%V])ۛ]L@r8r8M}ST K-c;[󪩢kw]>rUlV;NC+'0#,fr&(KҶwetQ Y$:[8oF;ULqRn%çFXuЏd)Ts+BZJs]Cmyގ-8){e1vig9ڮOuV#Z:@ˡtjnE>v&vdb-v䰓Y ˷r_:W|:t|aIಅrӴ܍[K5f 3C jd ~yrf!l)t3<,5rjQҨ~;e`yaV[we6!Z8;_;I:N_$v/kNhC`;aw4CxC*:VqZȖsa@x [(a7Qv$X_H%dQv밋(;f'|! Ko'3u8ʾ鴣E>vLsDkg U2ve]vQvg*e6Ž{Yׁ͖q\@`;-YA+-' Y#R!tD)($0`'vNX?ZRy닱+Y2 Z he۰meyf;DQjreasYy;:j>wjCd% q6g;Ԯ~y"AV"d l+.Qu6{7kK4- 0=klö)C!`',>ž>'l9_sx,EmfdxHb;uG#*jooG *#H&' ;~';[Nk=Βm 4IvǮaS/.>'ԴH9oyS אbo5ݦB29 lB7d[M?`9vtX`d[󅆇.`c}d[luHD.;3×9lpX: x;6'Xb{dFf-o)^?ۢrA29 okgrH+M@YEZ!hr..r${R 1oa͇3HDG u mRDVm?;6~2ػ,6V&g V؅Vr{3H&[$qk!b{O`c_N+nt~H_!m{HlUj7 myw -o}\ v=6Qvqfa [ElavFo4XV1Z?S]݊`a}\lo{Ljr[nJa\__Zz6я޴B;x y@GB\h=LhߥvutM^.ſ[TZ V^rn-jj,6~a1E)H=9lz dGb[ m#Ů/vw ;TX9 K`Sog['U]ꪇu-rz쾴rk uv)tkaN,*ot bD`;x:k N#-^iݾ=^vjzD; t.7mۋ`EHd<,Bؚ|8Fx;Igujs_ ZԵ}#N+ٚ2"hr%bR5f˰k>N؍* ()WK lR%,E#, Q@/(؋a;xtmB'X P(kkX\MѤfKQ9Xp=]HxvKr`hq!Nn T&,5,]t ,d"$RlGqy8*Z;n ,l5"o`lr.2;_?5'͎l$z}V?skZsevKT-׵5t"YN"r`Vm-_,`Ke", ە"eGH;,ZYevM= Pjآ)[:`l}evUEBOb;k.CKpF_,8v+>-W 0㠦 KR`-EyE=]:ln[tXͶ-p a>DM-TmC=صÀ4EN]qc(&h\Sܰx ާVAZLxcwG)[EKC뱡gj\82hmPr,xy"[©nEU)5m&l~ZUJ ᏴYLi! aBg+)/9Ȓr_{=q.dauJ&S+ac!e{}Z/lHq[y[.hXBjͣrm^˯x&X~yֳ8rB[)::6u5a`J?KJ& ,G~۾]GCw[HUmv#lᶂULȵ V"aT5/god_*h9x` {0~^1.;$]},RHjt KsG/cr&4,~ygۓq ajȎV[ڀk~)/SXƽuH=--el׎#m-5,]ˇXb;ﲊBH&f hvYNd#`5,+xHm{Ex-/$[&'ԝ7<,ư/r_S2],<`v~owqebEkE)wgX fvFVX -ɕ@̫-M ɶ7KC!G} :lf8zAr+._g.0#FЊ2J2~5&#_HNO:l~G_sYezc4[6iQyV~C]..d <{Vx۔[|~_W${} ?$m.$Հ?ۑ>%d}3X`vjG 4xD/4^FB8/x)x6hMRH@d.RgX$So۫4A(ɶȠ2VE[Fb5&^lʝb +>i'Y/m^OZdO D;j!96k-~k r`}5N)wp q__-IP@gx_N۫j`ԷABX`g5n/Zbg2HlE~dw3+LN> $Zvlĺ~"!@^Z%Z]p\e'Z\6 }Y2Y @ kZ=Z'ߺxfծ6=l+e/XЮm842{l~bxnbn_Jb?GP>?v?U&~v-_-lwb{~UYzؼ<&gfU˯Ym;sܷfBlضz((w+5|c?{_r??R?]WuzuٮS-"0 9ucoi nv T .^l0{-ZdBR)p=QMRzfhy3i]A=z=5f&W/FZlle߳Y f%۹Ўr4ߚrm3HEfifӂTyonRj(,Nέ R>v`d,(/K< 5Rl?`w /Kco;²O3,O6ۤU[j/Ks-*Ftl 4kj]L^Z=\VTEHSk\`K>4UW/K-*-oRX#!2Z'&Y=Eg-Q֛E eb;.h(̯Ps>Ovjŵ to[ѽ:Tk>5b5t,y?Р*k_%vH62X`[VfGE-,Ovu?[ާg,66kWdQCa[gb;E3:g{+Y2}ד$oۃ7MMr<ز=,-j`*w,4FKU{K~yX|!p ^hl銶zR{^,M5b67貛;p6dF,? (j}vYesσDvcE_4Zd-O)*t<,aoq͊H&S+5:ՀV硡r)Xp2Z ZUt&lɢ-gJj[9=TA{pE.d_b;ߢ%,a;Y+j0|, ~Pfb ŋ8[N9mO^{t[~s{zXc{fԨF.Y/4ur_S2[,`Jk B{5t)vo %kv;Rc[~DWoq-~s+>y-Ql? ݐoFb,'Tr-zo35~{=qu>0[[Ja)uQ/v鴼-GQZilW ʭ|Ou*\wNyCInו*vf %aoq]v֕*/)F6KnWj9Uh5,gr2m涏^k ֵ:5g5Z̮V[YӝLF+:a۠-صak:f,6~lUٺ+zWj,Y^.ꦏu["`xL:D;&q> ,udm'۽|A>(,}a)X6SOx5kvvn7 hu8j:7seVsd#gV^޶.{[.ֵݼtHum77mN<~9,b:8d]VnX$`t0't5^i اh2n~ۊN=p a)mzXxE 5pk5ɱƺ [LkUNث4|ͼr_nN%y]J \YBڤev@vBpxq^Vlofm/S {AW)d5 SJH+̶,`{vi(a[.Òo?t@aX,@4X4ɞLJºJNk HF]nٯdvX $krB_r|I'I|p<˞[kF _(R؜^f+dr5WFnUK*.g'˖zFvPg;Jdz-EV,X]xg1,ge_$XfrRVx(XOLOJt@꠶v6ZfzͰ ]hdr5׵1bŅ.` 2Ov~'~K=[쪬ۛF6~l?qx 6Cb$!}tV: \nv_GfECIyjwGik?ڇ3ڥ }6^M{O~uEY?fӘ͖ٮFm?g$$fo]cɣongI'.gbfEn]^Ca~f}|Ŭa췽촊 `_ /X^^<-vS[,~/$,/~v0dᴚСfe H{Z ՏG8qgСǵS>3{GlNaNucX~i5eQޛ=ݍ V:t8ڢ|׷V#UFbmVw Ύv7Cֶf{6îU[ءv[q%\*t|cg0y\RfXKL  iSl\'#tp=-1%C6';gdg4)l>n*?mt8y^Y{4/*mG|2l}./V'Hm{kU5m- V{c< !nĮll7w~z{a{J#]%;:[[T lY^JDѡ#Yarkܳ]WX>KqLIxf"éKrȒ;t= +?3 KUxf)) ]lO8^}PX KqN|#¦l}>^l2I?qGg}%;OX ؝A`?ѥft?0ᨑN'e_ؽnrӂm_/GSB{d&Zn%XNU GY˷ä4:(e;ESCXڐ:Ȇ&H#?3ڢ^LSNϗcv`8ƘRS֗kԋ/t'K:X\ ׋סOy(e;k[m,6S/vZ^?`s9[$ry&I~٢;m$hQ }ij`VQ˒ XnH녲#¶ٓ@5 F) NIXa~׃lK>s7 $E)[zu@H*4]&tSJZT1_/[6p9*J.`1Ō3j3YYj`JeD5 b* [T-mQ Zb$TBC`6-l3k d%m_C2Rä`2F `D)EkzQxfa1:8LaIlTo P7 ]'R`dԅa«ur>ݔED=jy"mv:-ۗU[QdX$ 3YLe lKXfZNR LR $ۿjE)۞َ3ca#ESu>))5L &zazcyx: \R",up3Cll{d?כ\{ԛ-d(`2 }XΧw!KQ+YR >fz2RR*v\3,)Yd;"ό۞bv$l48Hp6[,>3GGz`F))5L &Ҭ-k!;ULk~lyFTJaX @trNI?K7O?ۄgTʖB۔ԅaͶ=R-ψ/5ŬK96r?ψ(2c{ډ`K!|Gia9bq[tml;H fiK-{/E.c6Y]Xl#fQ!K͒,uc֞3r ĝ=tq^;ǖ)֩TȎŬrN5#r?%)"m*0uRm,]O`y $E\Ki *6,79: E0Y]-6d).w1;ٍdJ K+,nPgUK/]mD?ۺ`󠝕E$w] abjȖ 6ܦ.qm`WyJ;ob wp-/v)% ~RXC/n(E(9bv&یl(”rSO,vs%lSr;Y۵h#֋vٸ^vSlW-Ϫl!Y,qA8R&K-J6jKE?,xȶFR رQliW;fRZ#K[0PGC4xkr'l 6P uR<ᅥ`dj?󔞹E^6;)gkJ}ؔB;!hnIf; nq^-OȾ oMp YLrGdT'XD@% _j:ŨD`[ ƋK$r.Ył5uhP,W0?sh' Xc,~]X/`ly뀿miFR*ڎ'nVJEiM tvaKB{[~wǀ.'})& C/BG[R K P :fZ]GPWmSG:t\JV j",tRv@`y0ņ[]GYJ1,ǁh,,N|%Vgq~Z@{) :IpNO:)}>ڦS>3) r"sΧQ#>ٖ&z祴{KWeD̵6;a6d|7#!Xο׎Ѧw͈[m/g[^ϜFlwymxZmQ&]nѸemhmfۺ.yϬ֊v`ūva؎ZOvv^SV7Plt@ pَO?򰌽#NgY8>67܏M:z6$˳U>˳hogYjңb0Fm]dP>m:\'M/CgC=g}6جOÉJv`k9Xp.A'֟,vmsg8[ бrX:b~ t  kI&+X/Hz&gNk ONό)lۓ]/m֝vsܟdk24D-d[GyZJN-MK'-pR@6Xce,Ҫ-Y†OuS(YyM V5EFly&Y]Qot;j1xr'Qu#]3sV'^,"G"ECS QJc_ɮ}um>l.4 8. >E4:}dے-洄eƶ,}.(lc^0dZ,aqKh(Gl* /M)T/[ Sy^+֛Sw,yۂP*['ZDK=G/=uBuHĖ#OlZbuld+[95~vR,yWV{#XZ`2>TJmVw@%`Ge핱ݐݕM˥Hu`, V}a%l$3zz5R 6:kQGl\-/wlgKvf-_ G:Jakml*KN${ eVM{eʒ:Z!]m(i,׮RX$p4pPoNEO%ObY]CS@8b-?3N\v(}&KdL &YKT Gv ۷d-YCv.avcKlۢsĦdiL=ڸF]EŖog-)l_Kٚ)beپ^d1$g3ۑ`i̓`1^lm7q ŴhH,`K-lv,l"ߏhlhZE{_=EIxJ>~TKCG9J6r4- ie(civۣi(HiTJX^_N'['j %AIw3),TQ@XXvldq^Js֕X6KG?>meaxf*bnwC/6di0gM>j5`)`(m)Bc'F8M8yLJp4f"y^J":*<& $Q׋6<Ƈ^cg_mYŗ?X/u'cx'C+lFvSXw]ҢpmN/+[6#3 ql_c ql"[nPZkN:l$kIzX@VL bKN Dһ ^8¢LNi]TI2X|_M OxͶ>lfS4Cao1"Om b/bk8/ [7ۙh9;,K5XZD> dOy5*#oó,*_C Tj5rl>8h)dPg]%(?:,i6rKbqKYowMh~f_ꝪE?ũ ?ہbTzbTR_Vy*GʎO|-T!j+,.Pio75mb,bYDwBwȖ컚Nv7\FvCt茧cRy#(X^/7BHꍒE Zs]60ԋ~zhSgF?+E?E@Xx덶w)MJ$rߟjUm/VJ;9Cw)kѦ&ۻ؉jK܂5%nK _վE)-20_}޿,g>b/&Q)k7l'6*if Nm^#6ۼfJ}OYj_DZzӈlfXI.ݭgn\kGlg6YbocM̺=|iV;֭v] ~[[С*NUlm1ύr-(t輱Z+=5+zJ|>-I${o;wS3Y/DzUw)h+Li7Xg,Ug'u5Kg=ڑ"}wgMacoSNW>-NKk퇝m2;B:;=ȐB7G46Xo]k,Z9W loz~q`$fK) dK7_lly{=[ShZUldM99E>j=N(bqFt|ᴍ`991%✸>j^, RfMBaU1F9N:}d V˖y\v X>wzYȺu͓]C9GvHIq(o3X:x/-ƱlY¢;tt( QrԸ%J@˶ Kx?Y~G`rTұS`l"[N"c>p9*J) Khk6::[^vl[PP:>j՜3l[ɚ 6Kz!ٺ`y" ,<3oH4ycPo)YiO/ bTVgX;BS)Vb[.yB)' F-BEJejBFHl;fnv=ZK@ 4G1D-~E%,M?.!юDž y̮`5ŏ*x Ŧx 9$l`մ),B&G/OxQz+ZZaRmXZ帍7P)B0Ϫ }ly7ouɌl1G**&X>ȇ4h*YMg#0%+BԶ{S=^k۰~VX*(`(uMTf[-oH  ;.փ7Aۅ?l"x}yMZmNV.Oj1F"n4[B[‚>mqG E'vg`)l쬒(l16E'!,e[io9zY/\n=X[ް`k3^lMT4 Kp-E?jӠ]ga&KamEmQdi6ER@8l_Qö}:bM-o; MxW>Sl-Fe)e "Glv%[ Y/XZͶ~]m]G"-Knq =jxKbyGu $ۇGZd)'efAAHӨm>2b_a&.vo&ݐBXEae˭-V~ * Y{* zrR.d%KazPWN6]w6?eA{MkhG öKȃtDn[yG;w\y(=Y*$K!|[BY*߈'Ꭶ1[,&v(-JׂoAr<Bh9+S퍒lmrS@8no+[X mJ߄-7Z|3X7E(8x̖lk%,di*`t6Z<~I;%] Yފ\wJdׂ?@"_k,r <5- dg"YS@-g'6vXcӿj ߗúvMv9j7em|8;-=x.m mdy8fZö L][ -&*v;E%X 쨫A"ŝ`) }Q@XI5d -~6-,c/ee,[ ΢mX45v\lm/t {g:Y-aBg\<$V`i8a rbsb3=|ةva- BzKabv*[\]mb,Kd1h%,YPJSiSt!zjNc?A}u-Ox+Zж`0"d;V;=[5g,8hZmhԚ 8w:Bu}LŎ̶㡶/X]b]Yq!Z3c|r?N|jIG@Ywq[eW_[Uh7+]^SU٭]%hXky;v:*;Y;Mv-|wmxH {J8a]Ydhm^ͮF;?MۇӶw_\MybGʮhg&]d,vl16?YjS`sdqI,쯯e&mlͶ0d6=;Zn,n&VCvwr|g6;Z?8lQln';5ػ67&{.6X;کb/#&od2ⴝù3ݙmj6X%MbZ{Ӷt[lt*∓YPvcibOgk؎.mOvRNu/{SYs8ࢰ͋ř5-н\tUp-bG3k{x؅ڶ/TnӲͣp&ƶ=k9 [nOcC#=E>]GYe=a3RX9 vmuI,X謃 /[Yg"M PXtB6z#NQ-a;lS|_T,oLa$ĝUl/_9Ŏ+[Rx',މCMvrӂEWfO=%{S^@Xp*۾>򯢔mla)élSEv픴x'GdwO2bׂ|ud)J)j OE C?gȄp^+6d%j6ڜlvXoh1g;5/y|^B ejx$O|"lk=i>doovuIs4˿`ljo)Le/vj>f)ۜO|OOSWS8n=X #[)D7Z 䶀0L-O|k0|'LDmOcEVo1 ӞRԵ`ME.8$bPWX fv~S>74k,u`J\pJc d[.n6dy8]dk (k-$˧+wXmXa'0)mϜWmD֖ Zžۦdy?]e0O}u,xXl]g3UajY3'jGW%Yj|nݶT5Z2z51dkoTJ!=VC<2B[Eܚq}X}sn϶f;[3vj޿,#lf;;[G468H0Mv;?ۧ>d7߉dYSCߧVSZζob8~7b8ULgO1ښ.M|߉_X qhx81,k&ܧDa ݚ&?[o˳]S{:  >Vk77>p}3ۍp[ W-# mϼΞvzSŤun¶6{#}][7hg l4jקJvY2là7\7m&oKnwUjm<ݫ-Nb9;2~4N%3]51maakl mhVd3ށvlmcmugoCDx\¹-*X"<'mM+X,9?\h*#X>XjŦE;lZ[Y,b/YFjvihqK|f6viQgawYh QH֦f;9Zp,od6{|uGNZ`mm6Gj\{.f0ўUd8Φg;2ɇMeMl'f{WkomG7/ł MVg{[ʖrܨq0or>[oCwk3dG,gXlhjm:Te˻/vdltԅ .dv ZK[E;\,Vu'rJvG';Y/m![-gt}%}Xv[ښnvv9PZa3XŻV_wB»`{);2[΋^>, 򅣗Rm^,M$ۂk#DZvN 4&$rJS؝>m?~%ZnZɪmHs 9= X|r^`%fiMX|唦΂/8ҥMm~dG϶,~ۮb' X|};Œ[mgt'RO>}%ە,gs8f<``g@th9[ח-LÎ2 dwҶk-%l^ήl@mѴ`)hvKdN+]R6X.ؚ`&ݲ7-Jۻ/ͯجf}XI-B JLٌd!,f34fу8̄J),F[-z6'U" $8Hp6Yr0[_ YMo;b`.۬['Kdl7J) lV{SΦW/i~ ۫dbJ[W#h?8fQ0iI;b})#Xlwdi8OsVH_*ل,b4Z?Jq(Y\%X{>l>E,5`,a"ڑ*Ym?bd9 LA:.ax#[w6EXXLZ}X'YO`.cxX Ѽ_| y6C/ٶ,I#;dSvv"7-r .E@mEm,5ˈ I,wԴ"vc¯nzϿ" Ef'Q;6X*VRQCR<6b;#KHnUbY"]=%ng;ٙdȵL2GOǠO/u2jJvRyn*lACNÖ/J*rS@jg!I. ^.[E_xQd8(j$ZD4jFdiuqe8:f1ecL&B]/>WG> ȢƲ'fۀJٻv"f)e[[mQ6nd,]ZwfEw-YLڤTn]lٺІ"j%)Ytv!"`lmȶ`-wl΢Ew-ڇ,=(E@fr~NYjZd)T٢Yʶu.Kq'Gff)+Y4-Ѿ^p_=iEr}>EۂE0nmvRn !|I~G.vh ly8_ݢ`v[fev^n"Om3 $[8`]%[/o ޯxC<-~vgŨ떗r*ۅr*-~ Phha*-X_,elMiھhܧCZTvɎ6,d}((JL,Q:]ڭWb*`z,+6Ci"/X(emQl#X mv]G-O`tvuQG`9;N;RX`\J3}~ةζld,Rه va Ů=f)X k-fiNaw8΂Sx6lkJnwf~{M>lĵ RWه{'U[j}K,K a]?l`{Pbj;ljT$8,*]l]-{O6QZ;=Vo?gTYBξ^l[cp& ''V.,va'&9߶".ZmSg ѿ#mQ+N`ȓ6?mbOmfu3M~؁"p^JiG%n v a*v)-BG۲ٹ"?~}WZ{i jgfS0Y,-t*Zg룭l}{[uӵnNv`ݣ͖VVR(cDt~&j^o1?[+]k-6+N7ڷ^ߣپ}{ZZ vj?.gly_7ɪ0ll }+6gv{f?Ϳ u7;-+ؙf5;7}5dmׯYv^lfeO+X_טW$k]`+V$frS͆Y6V$pwYg'ml]trm׷U{ƾR-ppr`V?CGְߴ1{WƸd+롷7M1t>'b8| s?YU S1t=Woۭ5^58W/[nZpcŞ[bmg?Ywdw?i3`s bG;S쾣㌑ն #p9"ng=YzKܟm;aNuIִ3j5rl |.w4îջ家pٴ ;qDZvUWj{o^|yW@mu8$:C&Cn`.ö;/z;ͻ X>KSd{ֳ䵑;8B,пarvoxk~&k9;7f<;?XOMqoL}:}Yry}0CW]ԟ%ǩf|}ZzXy?\|gHƴq\afn0ٮ͙ږ1cۣɢYؔn,vdֻbv37z0Y퍳aMEӚ^:<2[}cȷgv un\rTrI @_v -CHa_ɦ,sf{X,gli&SNm C>v"g;2v!o޽jWemӐ͒j灷КLTXSGa6dv;)g\vsFꈪlV's>m%{&|JoǾ^w1\֮f[ڳJv`ldhӶw[v)7_ [o[ppVxe{goyst9沝%jӐ BwS \R &[oL[pz[RNvӰ;"a,RإA%;kK֓yp/9 -.g5-v`|-X]ꛕrT/97K~m=,( ,y +Î.udGDsQݦv]V9%Gp/9Gl."b3=S%۵.u᳽d^f2A`ԍٺ`kQ{X4-HԴ=Yjۊ]" ϶_Ůf,>Qۈ[4KLR^`f3G*"7fͶ:Z؎q"ϖz],N]tMdU fF,5?c1g;ٽdӘ,/qSf$ȓz>AgoEmKa_{|X4KlE)l%3ж9Wrϖʖ{|\5Χ\v@s,ܨ1 &[d}I6,ܿnd ǃJRX g!%"Tv2Ynlg[+g3h^6r_ S)rz9T) LҺ:VuCZl Yvz>kVx4w+\JU;-iW,lrKVn VUOͶs$HQʻ`qXͫiI;b󸥁lQ6@lVnNfv]&hZQ{9ORw؇·]sdke,Ϝ^̅gMXf1H q (E#, E$R"|plsX:/%a頻0[îfhllCaڍh9mRӒ",s@; Nt K͒(,^>;vLv,oǿ.xb8۬bAi{,e3ŚY`)$Ջ瀰\o}NkvyGEݭkqK}v`K!d==|ؑb'C|Ucfۂ0[_JG 2$vƆ ]lX/o"=iG:MK7R,"0REơY RgGIa^j{  \.6l}>\l9i%]!($YFvHiN<8G-hjlÎkzX,6QxM`?yKLddXIENDB`vagrant-1.4.3/website/docs/source/images/footer_hashi_logo.png000066400000000000000000000015701226132634600245300ustar00rootroot00000000000000PNG  IHDRAGX*X?IDATx휻SA Q(viDBYXT+\XA-b~]- 1}ũ^/sܹs_yI$gf1&7ᱱjey8v3/LXyn * [ О7n2 "ڥB%h PRAh>K9CDЯsn x.!С!zx&2!Pc,m@jE8]\C=wp4-Ӑ pyqh4euFnBCx 0~|RC`Igq9 b~!DQt\J]k"CkysRZ= 5`2ЖvB(;-cu|@d%IAek.T o聼B- `PϢ(:r522 H }/sBuc^ؠ p eLhA&XpW A!( g/!ؗBP A!(¿#*F;J!(2Cwѐ!1KpDƆtRƄaysZo+wHr_} IBk&>+?B &~@7RbG{[քfxoA#xaBsqs6;-,+yfܡyaI ߢnQU!>GmćHLBsWQ˭ ,RA0lX,;_P<|HSk=Cw{Vk)=&|K֞ Uz=Q= )6m?T=0JDe)%D:B,>#},#KDԪD ݓ`ߚkl?&|Cxh_I|Uaӑ81[s,*{!rnb d ?c.Y;$G$_ Xƾ:W_x6>Ir"3}򃀍vsjx?jwbwkKq$^Fy__őǺkju`mEC"9crUa .y=caZiEƁS5\V:8hJ҅AC)8a68q sޤŢ"m9gb69B355ȼ!1E,irEn 'Hp{yȶ,a9ȭ=rzc1o4gaW<Ρ2:gr[8Dɚ0+GE_/7r5rz Lk 3Bېa^<{>^,j>6ӌa0+GFӼѓ3k 0+]E]hlzj¨*T] Y0(K٢+^M9 <q"\9*}ט&1 ,"4яSI --g/yCRA ykhAP<3@ f6+1e;cTHմn yDY>E`"CQ29[׀VEyD9d Y#$)Jll΢y&e!篝sZVz^yy(DdM[_ja"K^M6gy:n_Z r2M䬮/P&(k"Mb+ЀȫU8Ry$Ӄ_p`*q!x0P[6jsS'}UyfOCV8P.I^499>3(׾ͮ]*vLB}$)C lL}qfZɠk؊`asUW~֣!F8'Ċwk8{Jq/\iǞ{-*Wvl IENDB`vagrant-1.4.3/website/docs/source/images/get_started_background.png000066400000000000000000000267661226132634600255600ustar00rootroot00000000000000PNG  IHDRPLTEL6tRNS2"1 #/0 +. ( ! ,$& -*%)'=76K.,IDATx^v"׺-? B !@Ei{r˅"k߸و GdQdy@kEjST9D-0ɟ#E#@v0EUQ?6f)0n⋔˨ ['6oH4hxEUzEyw)pr|Q͍YvA_.8DWh4"&~FP?BV9YgDJnR^΋X|Dw18OtX$֬}hGUC+6i'R`:8 n @{#t~U5*2Ϣ2|?*k>,QU6JwEdOvTxU(n>A/*?esu&,#I\\ҹKwHj'6Dv7]AaPD 6b+ү[64^ۑilAUh_-Jg/##穲"_۝4+c)b7@k~sMh][AJy0/bͻ˨ 4?l$+/}gO6o b\5 JȎo:UCgL`V} Ju6jŪۋhϞZ.I0FR):Qc^?v;\h$5j|4ZK1C;Ry[༻ȢB($>D[7\,:jQ #7elbpԊL>sj<`m/ Q:Y)o^YvQ;tH` 3^f׌jm(O}lV٢{`8hƞ@E)X|<<u]#ڄCTeQê<}%5* &HaUo i_'Iy;||DQ8)0jŎ@^.Rl$Heͣt?zMy(n0p㥈jNjFR].F{ƉFRlHBr碆d#FRہA[:I5 (|Kkş1T\`9h8H{1nb#V8^f>:e$89>ˢ >L 0JϑN$BvYJ1 J:i= Hq*Εbԍv`[F2 J_E=̻`FRy @!zGR1*(lT#Bߗ`$հ*@/Zʃy_êB_xv$րCkov"IP* JiJH*w,-lȦX5bXu:|eVdOUyv_j#JGD#gYTJc#i>ߊEՎV0ϗNz ~T)T·,*к-\Xg$&P_ +,cVmEue'4G& /o^aٝL<Ռ2.:wTzU%@2R`9 FaPDe߾0J.HVsI5aQH*Z-mfiImE-9tK`),6g@߿hl$q:j4u8Nԏa~T`m I|rX [$ҋVhp5p=ħ%I)8:OE*R ZQG÷F6Ҿ~iDe}|\6Ieϣ^uWD#tTgY|h2ȞہT.X/#*mJ@k6-$jH*~,Gȸي> Jc:RHjFRn'@7Q2H2voُcҋD`,Cb$j7Ie^pݔHy$ET`r|ŴK"ObXp}|<-s0JE2AAo Q3$ fOY?pBIfz4jшa,a:.I4^'s>Ɵh-%ywcQ:'gY@%ew8J7 E*H;t&FRҷF| bpԊq?\do5'ݏOc" H ༻o;@q{ݎwc;:ט$Z)UGU|T" |vt:F| y,MZY;> :oe|@_NrH*fY|bx3JTQ!q.(~@aP{0ڼT#ŀ$ҍBbHj2|HjY`B)u1ߢT ۝'Q$ anbX-w+J/ 0ϗQ!)ywj$IM2y:Q@UaVs êi>XQl-fY59ڊ*` vxR=FRwXu{_~-bX'x_G})ҋNd{~g$ x9lǶ8oEE~ȢGDMՁjUwM^qsYF#` yԏ V(߼ۑT0 x-uh0ڻb+y{<1nn`Z~`H*dV׷CI\4QPub{Cnt74Jk$Y~琮Tܥ?T2I m#\4{ bYT˛"ޭu4(" uWŇGR't5"հ)pռ?9/]5U0ڻ"vC(:HHd}|dH)>x)RZ4"(Na1Rn?TP9j$AFR99~U|,;D{, _~|: H*;pE*FR+i"1z_w)pq.rݎ8L]U20zEEI'IMw0ʤُ r>~b-DLRtbͻ4k>j$U@q{݊ vi+MduQ:2vͼ-`$(vP k#͋QjOT0/#6o*.HHj;v~EDHeE#ty$d$Ѻt.HH*wkYD +Ih$IW;)>>ϸ:Qw. IeQ:DX\4wX {w_m$A 5#Rp]Ie?GZoX+#d}@6G \5_=Ć0J#6+J0J\H:FRYzQkҚ=ek>Q9h Q,_@+Uf+~t3FR?rRH,Hb~PF:, @4Hj#6T^q $IeԜDbd{WU'I@y9/"=ʿZG"~~"wxnH<?E7e@v\T#dzHb_([#ry֎ 55+zkIe6q~>R]HwugN(^0k*֏A{^]|0jp{+|g$lsQ:zZkFRrz^Nu Is(ɧE*R_H*tSTʃIhJyrlFR9y|6I5IJvhv86ϻ'W.q@gjDpߛeӬU42vX.0Jo-Xu{s-ҁmiy?lIuylc$YW6C>:9ɢfDwQ_@yֈ5'I}#L74JǷN4TQ26 ym$5-Io!Us"J=f")/oXS@g+z 4{?ph(voGg+)pc#ҁO!D#t6 ",0ڎ* Jǹ~/0.kWF).JaPD W>yM@%H*iN"~`]FFRϲePzG%Iu$Q:Fd#(w·,yԊ c$ˣs>(E$2 zUAvv| hx;W:ӵ"JG,#X2=*,V3F=ɞr$·Yt2j$#ڈXu#ڳ,d~P ~pE 4+IEy(Op[D V4(|(^QCFRgSɧԛ, =D7Q:z^9I5ʤ9Jn/{^^E I3;M+OFR+IOUwT;Ĭ;0^w:O9Q:V?-IE58e$Hj+6 ?W Qk(=>БTb Av0LRtbCAqЊm)~u+#FRo߭׭H. Dym%I)b}22&XYH*ҡU3wQTd\l׏7/`KH~uh24R(F?{^~/#Ս |);BLE SG4VIy0%yүQJf$mIîFx4J,m&6̻HgHjO*XD \4uljҷrk IѠM=w0 wYl2Ts]EIQ:):>Ivl5ISye`$&$HHyFRa5 A/vt6%=dDSFpU@chDM=0|&)i,V?0{'> f@+<0\O~y}҈y$tu!y,?(FR5'Q Wy'弈߁(}ŗ;=?wY$@vv|\:z)0(=dI5 tɧ^Y7 `=}Ȟf5=ނT E*~j={FR{畻,1ډZQ:z^]yH1T8oN8UyIel ƮQi$8j(Qyt)_H*h=<%IޤT`WQkIlnQ>v"-T}(=0&yl%^qzGRQ:Y#@[aizGR#w{\b$}Wf$ϗhy FRFIZ? S 0o."-h%¸يz]H*E {^,Zv"W(eS՘$҂f?hn $pu,4#dӣt/E$Il`!Jd y+ c=osWjvs>\dt< Lzz^!{c٢{M+v@tt=О=e@9N5dFR;#)W7bwLS4뿍Hz^1 b$&DHBkIA1,c"FLOFLH ͋lvԏOZDa$ymdžH*}Wt{^a9_[B9(i~kDRH0 pr։wsa$ ,*ze$p&JQ:hu$*2bIa/=\|H* J_źy. JVW(/o"u;f#ew_? 7QKIɧ5~kP>LW0ڎ5D/q"j#G{Y$8`0J UU#(5 7[Q.F9҂jGR|z^ElHjk;&y+\4Z׷_$64y?myH* J?ly}?bykl(i~=e^llMIh|ۮ(.y+@>(h͞,AE#`EuID'z^Q3WGB,6JfTl#J :tO¨9Z5y`^DLԵGRڈy+ۤW0 gYl6u+t.Sl==ڱQ yH*(<|kDRTqy={H Nl7@>޴H ^Wy_T}FRFRB1ߩT|}=0ibvBGR {^[E&@~yIp H*@v(||DRjcZ 4Fp> A[u=>H*UW~o~?\DZpGW=0J: M>]I ݪE-z^1 @WT7DRp#BGRD׭?Da$@޾~)")Xz?%Sk=X-.4^'EIي5|?ߊ'G ey{2f3J?lG`$?n5'xG]0 {(˓S~1J<bpԊzQp|u9?CXH*Ã,#=ڱFO;>Ы`$@;gwЮ`$@Mc$n٢{ͫH x$Q=ߔ(#,U9*Tڇ/uaޑTQ+>sXTI|^hP̋X7=I\Ozy-#`xkT(R~I:0ɟ"qbՈZTPȞ> emk1 ۋSbN{kv-J_H*or;T=W(v烻׵(DMk=i,e =n5(uQ:ET@kN|FRh5ZOb$u;*5/b:0.jl[5GjtF$y4J=)=mAe-@a(V~"ZwLyGOѠtoHV7\_n˨Q:yZVi@kv y(J@kC+=j_/Qs|?vϱni*h= FP$y>JiDm@'˼; Z(Q{<@kͻf9},c5~\RJem[ &jhb@q{r[給$Ju&Jl׏V(ȡu4(wmWV`5D畫fkUDym_ ۆ9y{AU@։E tcC*@ϫ(}2>FV0Q<6LMZ|.>(4 t(O;(_@FVK^\JW/Xk0ޘ:"J@ϫ(uW·Yz^%~5!EmHv@vƬQ:\TDyh$['z^.3`Dx' y})zͣ~(*JoF|NbqQ:jG)GWQwy}uWzWpua=Vz^i_mMԝ(= nirYm*p><ȢXvP~o+46z^DyeQ:z^E˨;xnG“(@P%7C(uyEQwyuQ:2wYl5(v(sz兣IENDB`vagrant-1.4.3/website/docs/source/images/icon_caution.png000066400000000000000000000016151226132634600235100ustar00rootroot00000000000000PNG  IHDRK@R)TIDATx;hQ/*" PBAl,M9 inc,Lڨ66> XbEE!bIHv2f33s'Gd󃀸PxdSÕ&*)5>.3JWiHU3,fУajTϹeES enbtx[X]X`>`7+n`b |cL4p.*([[ *s;XKUYp^6N-x-ݙblP/uxmkRs:,wNm X:s_k l` ;FoXQ\+j aUӎaMsﺱR].V&,@?\p¬7P.,ca,c͐LepyXyVf0? iRX`^3Zyofa$֨fa@fa*DPA,n_+W.T!0JMMM96:]h__wUe'|@liG`\rYQ2K߿ߊzRƹNt~GQK r*CPmtʀ𧫫&|Q:4+Wrݢ+T:tE0PeF\wRֺ֩iw,+W5$c*ذX1NPJU}l&V`X)6r0:iyšK+W5䂅W T#%-h`2+ShؿyDKh(+6޷z3ׁ)_Nj{k^eBQ`\r@UxPT0!i@[[H$RA5vU]݀UxE&;ט@7b?G}@PRsC}# r*3b+kX3VoJ-vPÎT LxS+ei7B߮ިڱ!4GuO${ܠuˍD_rj 5Z{I@uFZXq"1G9ņmHYjHoVUl+qEvP"p xIApU:|fdxO܋\+΁)luJ ?&wy ƍAۗT9^c`\r/E-.Xw!JN]4D:e4(ꔁ*4W\eQpyU[eӖ̴LꪑjNBՕ+WC":k Yku/1A.@S UW\e TsbPIQNUEOd*m}]i]r(Zsk%iȧ Ikgx/c|>Hl]H;ƾ\r(/y (VLxNY^c[רAujOvl k oSt!'< Т*e.5 -l{p +dM>@9Efn?ܛ5D{: HsI|(sN;oED#/s7wio*e}ƽ.-2z=OiSBCU N[{WoyVRM[W^3u5}]1AyApHy.H T1.V[le| <9_F?i`W.Nkى9PV5ޠ)6T)Ԣ. b(ݨڟSQx0y Eofy{8籊ɖ&ċ^GY҂(H*ܳ^dꐵ,cپ7(f"pMȗGV˻X~w[ G>?S꽭OY]kY* }a4dȃ5E|*}UH}ٲ"mYU`Os6K'XPz;kʇJK nHa鱰ka{nH:g&q礳Pvlyvws,4?V&/ך] ?j՝l_VV*)u_i JjYCCEdF%\z]&;dx µ}a xxxs8y'xFk濉PBS)㿞<w輎0a#'L52uϧ U2Ur|܇T"e{:7ݹ0ΥP¹t+c |@HR:(t˂34bbUeՉ+R L#W"Ղ6\Xju)Ӡ`^dK`>cޢ`uc-( (ˇ*R3KV] WC/wA% /vXˏ\:b:tENY RP%Vx`6R}*LӨ% \C">p ӎ5 CqAgӣ%wYdKU<o|CzL2Ŀ9Ch pu8GuC TT6jD-챞+}<adyFg}Y/1B4h f3T#@R:^N dKơ (ȴ~Pպ΋_n|DJj_C|bTPjy|(QvsI?$YVCeFCI.ꃏ!g 3:giyU}K{mpWW$gU&iA5n=ŏ_jsPUQY&dI>&Ml{Μ@C2T~2߃=S4Rpu$ 8]{a\v:IX(꟥ e.6ɠVN4j}MJb6}{UpKRgwa6W&G E?`@ZT*?I5_ʷ@6=>\otٽfT;\FS>զ 19ЏoI"Lr1P5F¢*^eW>@4=-)U!a%s=%E E-T~XiU x*Uu yrBkKFt-WT UT>V#)Ds^2*M^=`xŗ |e')EЖ+l Iz'ߪPilEgגߗ- ayng+{}Հ#,:dtf4kL˄'k+Ri OVP~cad/*fD]0Ƚ}@w29<p0u!GY4ߦ:tS:UzA󷷗Lџt4a%08]<NxC"(;%% Kh*b/3tQDBcw1vf>!;(,|?.+ _x/37R'3/yYV?KAwVz_r_i f/ޮ/ٽ_?(-p&MT{rc{1{Xsl@a;܇ UؑDHPjga(!2aQ%tˤ$jay J[*k՘q$Uut&QeCAv% w&֨<>D0Wj3բZ|A4r"hUlQ=kayGUh*^|7oՈtp hU zfKb5G>><8`UEaURJ\bɝa1>0`Uu@oezJTO쟢zM? QZ>,@T]G2q;v/Yl>$@TOE{O>Q܇jM;ǣLs 7ZW9t%E@} t_@iIENDB`vagrant-1.4.3/website/docs/source/images/logo_docs_small.png000066400000000000000000000071151226132634600241770ustar00rootroot00000000000000PNG  IHDR@~IDATx l[!Ƥ ik(++MXjӘ44+-iN cA?(j7J 44ڪs4q>8|I8>~׮=W:jwϹ>w9+WB4twUTN\L a?2=%8[#7S'0wt생һg WSEFz{{_lM4lGA?$kxuR=)kCG-a.ʥOS z0mcA*:{b_ V3:c\yCCC$|r0*[T=ЧR2ݗwR †ڶ QA A~.9մg!m뎎ibvBQ>-Ui, =Hc?x& @/.WJ?[0;k(.8ol;vv_ ǹ@q{HC^jJ" E;Jӫ- h_@ڿu[WҌCʋF %;Í @h2> H<~G ٷp%n>Q?4G%A 1gQOeaɡŋ@OTzXFJbbb];tU`sW}mg;Ȋ}U&rAH;0B^hn~ֹh aéQd2,53Aa剏;c.ۧG:+=$DL/_Es:PN517#3 #lВ,BުY].P0}iij "$dMgѲR Loh:7×ac>|L6!HC7ܽƻW9E>DF2JaH0Ǽو"v8UL|c "Cnw Y,^g/7 cUW.Y[ЗqmT8 uQ1+ȐFC -i@4*tzA4 TDvk 2kiýigDD|vuv(Qq2ܛ,whkkZhqeXND uِ-J7_:x{HJyĠpFќ,gmFe[wXU{x ?x@__Y%< ub푈6 2:Z_UvОGjo/tZ@uj|"`X/Z6B69b&.ZJ}~!|󦮩}Ş>N%un@H-V@D  (7cAC{5Z0?=U hq oT6~WIAo?wO)?ǮQ͵6) 8>zx#qmu$qq='J,2ywݶCiѨ[_974/SfNsCS9q/50Z!boHJx5'XWbKA4ZBxmptL37DݶKdmFqR,/Z>b10W/kx)Sx/ϘJ ީGE}' *'lFD "O-"dy-GJ-KXɀ0n}_my1/X~؃#y"l#֫[tȅi؊u(FػX!iR1EdU9-jDN)4~ y&_0EmB d7 bp0J;FɒBZ.Ζ=pD!T( #E?qԦ Ĺ[pk|+3\摲a&-ϦD\nYqOr^*fS ֛Q0xDiN -_J8͝e`[,[籓@XA٣"!~{h <ؒpNI'j*6u(#s̲սKpVnjkS>{Ao&D6R!:Q-:0ţ#=%gKc%0'Wa&[볌Y,Z>mbXA@ vD6"Z$@ND'2C#<'0%6}Q;?U%?Noyo`Z!hEo03#Faf g?<\p$ށ~uv뢵Y{qB@-/Q F޺3TZWk 9d.zlq"0 }vփ#(հWDv˶w4^)4,d ";s̼EAmE.D萙 f7 L(Cءyeث1_c]D9[lՌ23]NW,'Ot *lbTd<򑲉z?CfI4*6%\]hakwoKPNݙ=A\p{-]}ny!P#e'L]8mV8%r_@)ëLz;S cE ENJ@HrK4TfkD)EzwdPR2i;1 u/' =|3^4arߡP"=ܵ%~X đ4/-Df >"zw`"gM ݁i:ZDV?o[~q yicΣ'y5cN âZ-s|6qiwc Qt %#e4qJ#Wg(T ^޹ @_zwVDYQJ%"ď_hzwR$,0W"41WIx$à,-9e)iPk=k򿸦\wU*IENDB`vagrant-1.4.3/website/docs/source/images/logo_small.png000066400000000000000000000051541226132634600231700ustar00rootroot00000000000000PNG  IHDRC@A^ 3IDATx L[ٖ6SvSISM-͖h`iLH ˴LRikU2)ObjUy!Vɖ,Ę6,͏߳m`lM {,5~LliHGs?{ιd!$n;Xn(9><<,!}`g!̋ﰸ`2t:2LK3 v}üބyJ%Q?(+ߚ -e`zo, X[O7 C>nc).~b0z $B!?@`fMb&krq>$hh"kkmJ;dyhdd~D~(q㥳ɔ<R!z]˒)Vhn&=0>>NH dO%3z7Uvo7:65R0DJ@5W{;Dn # )Q+c+:u4 -8؍،66LF lQAGrG{ ]5Gb@\^ǟM dJ~nɅ2e͕vbcHt>lN0ڧE?8aC%~HqM#Y(I"?#)xEV89څ,p¦Y0;o:)-ƮY]}9K Izx> *=zA7<[m%%Aq|c7B!쇁$|` {"?% ,JuS.V ]i6}2ۯ5U_(};JA9[Tof Ɣq(Om(# %c:mNm'KILK. yF*mP g5s^ 8JU_Is, *h]/Ԣw/@H# ? { aVDxf%% p;w)C__Б;рM Q&Q2#Sml@\o>*)Үᢅ96v&)vGـrҼ~t8.^!7A;~xj!Q0kCM؝1 U:}ma/;?(]շzG*)~P4=F AFJ֭u+L0HjhGg\L0 ee0,Fʉ.F{JPHO\XFfٍJl 7lui]~}GEEi]1<:dT+N?KP{`TCk hbD'Ń-mBo}f0Z3T,[0Uo{r c4WDW CLØ~MRn=ms#Jar 9]14dʯ R,, Vo#0HI|H\ςqׅv_\ϟN:L3kUizї$ t0|*EM;G) Jr8z0@Ru8p_&1]UjȻcbcIIbs H- A?fBA'ufa(݋c^4ÀZ`8ZW K(0HץS}` qޑ&qUm h`{uݩ!4 GptQaf-L l/Ҏ,H:?\|9\FCC`W9)eq=yGx>˻?0HEz2`/nÁ=<.Y5 _ 0`|*}vTt_P ^'*w<Я;Æ`o[|m^%|Hvr[_ v~˭5֏Yժ;6vSpYz`(ElM~͆A u8KY/H 8a`EjfK<=CNî˓VjDM{.pAq;}P$"kiCq%a jVDKC[ 5G`G{o8C5`GF4 c*:`@Z* 6d x+0^s#|%ɩzDuĂgR0~Ҁ,S?_Lrj @o&€+ %L H{ctu n~c57- :3|?*ScY&ŇSzh I(QND=f:`i N s``OnV_З0@/-xbn s]ۍoQ i0؅\F ucߢґ0*h++| da}F7jB?&A.g\h>N{}IENDB`vagrant-1.4.3/website/docs/source/images/logo_vagrant.png000066400000000000000000000111521226132634600235150ustar00rootroot00000000000000PNG  IHDR@7H1IDATx tmkj_qZ /A_H UZjT+Q*dwCR5b0l#B23y7 $&$!n2_{3ٝ3dsd3s~{EeZCH6Vl ug4I`X~IApxxBZ[[拂A;ukR7"Dj0ꋢb=.hHẞT4%4_ P je4\j hhU'KQww7t:߇Q#Ξ#2ׯi^cnDO ^"@jZ73h()m 8!2Wq{!*iשL T{g4qճ*m@`*˭5͇rCbyl7NT{g46_kKmvyw__6{.Y.C|# rlyU%P*:W<#AP; d5>fv.;vyYxgƅhhf6׾i#}z~>g4M`u C[e(]]]0v_8;p=k/qGaGAuc! ؕk뜾_>r#;)x˯ͫ,Gfq;ϟ=`ҁ>Ea)s9 3uxVAwRo{lo_ltNI u-j>g' &%ѳh@PfwxٮehہyA75%j&8֕}u` -@fq}y.gP#s{re.F7hpΠiI0S2oP@^="M4yuJ=vp,wM=@ ѼR!/`MTWRuP&MA:KM+):/59טegopUik8J~)pX^ob.6ܼA\gNUY|pi3j4hێBvisT8(F)g8+ң l:Yלf~u~6=Dގӱ$nSPʰ+'v:jKG> !#&1н9ԄGo7ɄYqn}7H;(AY"ՙ|N 69en+=ٿrmt.Jų&%fބ aǿE6RmT@ִ.߳h?%ctF dg=6=EN'ΫO~z`гmyH\(Gj!LYt_(Rg܁7~*,U; r}R.qPdg[ku_/UV #V:[)hy}D[Z.2F90~!=r'3|1OuR'~%~pvV;bLc;Bf%Rg9%:دճ6 mmUZVfxv `%!znx`If#02CD[ )&I'X{<@収(a!fs(\E|Er .YCvK p+GkocYlV "ک~3]y-ǃ [ᵼV8(<%|>@Ykz]j0sɂ,CYm3%@"2q;Rۈ"3Nn})ag!m.i5/fr[ F0+q"=_EWr#dn=J\klu3a)A|vx| ;M $% fv~ƶJ_pF5n6N~ۅ$bNo(}3 ÚzSI4J(Cᾐ'}BrF6N#nzIDž6E$ऺu?!Rarn,LĜk+uELWnٕ:0P\x˖ 867r&G`5!gXx˰Y]k~Aj`Aֹ6/nB͉G)ce1S3YDSLq |N:8vBZ6uC\/pB䂰D=d%t W}sː~N?yeEoԢ _8NW=/sm=3ay>-R*s@%`\Ϟ"CRbd}Șx| ;U.BgBC(x\;ZPOԶ]AkRg٫~ZC>xOs 150KӚ2c Ž"cϾK 0[$tb"7\Mzpȵ/ZƻƬ9HWŀfzג{Y#8286gSM 7O~>aXk%}`p"t" ph<NVK@}BUYt AZ%CX'Gཋp\5xl6 tsQat" peCL9NHH l>#p Uk(w0N}Bt> ndt1ARu8ht|8Gu_xŽDz'$%ÊZC*qx|2sKX#@9g= ;6;"9-d&8rD$0G :c}Ea'"uXArK(l5Gx7r$qݑ p4*ROM-.. F.4ߧ+m4r;1^8@؊h#3 py:BP"Jt9sT؉㝌z g4m;!e=ϒe»p|{i_f#L6(1M$LFw;: Z{:NH1;Ggp<[Y|Mc3L]gqhL"o+0 3@5ARg"8̠1{ZWI<(̶ٔ\"?dNzc s!+x@i?pGϜ 9{/g`N$A 6=BA? &xP}0p0iإQ8{kY;k9 2J:+v;o8ZS`n+ `'OԘj9<"c` =0FןՃBn&l5﮶p{q=]R*x_B !ۈpX?Cj)>|Tg 7܏g}}Z$D<EP%y<dtM4N$0ӱ@}8}:g`&K=,8eIJ!kagQ.:ؚIyEm 8 SoYisBgTy35?0&{pA:~2)Z`S8>[n+~{:VL}Ob߆hvc feE[?/&Wlڠ=2\z} U*tL@n4ip<~ )M:f yaӎ؋`DL@&wDw pJWYh{d})e9MN lT3nz*ה5r> +Q'05i8M%(,?U=ܮcKqq=ISp >JNjy0MP14.8u|OH)ğS횭IӘQb\>Z|]z ^:jx4)RLtN o"_}FNoDj&/Fflj`4E xi[R;fX?,Mp4$ 7u2ISrCAK2OUv pgKhbh yo˵?njk,'}_{eѱWgi+t̒9IENDB`vagrant-1.4.3/website/docs/source/images/open_close.png000066400000000000000000000001721226132634600231610ustar00rootroot00000000000000PNG  IHDRAʄIAIDATx1 @XVn֑J} 62Wp@ _ Ky28}IENDB`vagrant-1.4.3/website/docs/source/images/search_icon.png000066400000000000000000000010551226132634600233110ustar00rootroot00000000000000PNG  IHDRIDATxڽYNB1UIcPH*ꋈqD(`<'lߴI mKo=07 HTjPшZ$>)zv{lexblJ2Iʭ ⠇(q #V{ܡ-U7z=Ϗq}D\k g\3mcZݗa`1ӆB%X`ҭ9Rs emGw.YZiTZP%!m|ޫd{J-/&Tӹ4v дPB7)ᚨ ٯ(Ե4&$,)ͦn&!ͫR fn>D}U XwߨZa[S6@ez3Qr l.+!huxbOY6j뢈|A?JefTڂǾ( f-Co3M'0kspq0K`.rGl*|~*OrM#&iʱIENDB`vagrant-1.4.3/website/docs/source/images/sidebar_background_docs.png000066400000000000000000005144041226132634600256630ustar00rootroot00000000000000PNG  IHDRu]<1TPLTE1=tRNS    \CIDATx^A S9rXIԪ=2"h?/[)9I8$."\8ԁY;!Mc¿3Dw,,GF 뉆  ϹҪv(\oҐ ?pLj07\+$|aܖǁ$ZDQQ܇|Lۢv0 AԜT-t~*ʵLySsljלGUX{Uyp*.M).xVYpZo.4oWE5ɂg4낗_?@p "tvgp٥K_$KٔbBL]1%)u4No* c7Ng6&>ԧ(mBiS Z릳R 1']pӄpa܄R& sM3 OI(KR 1uՔ*\:WJRdBLi2JgݨhUM.YDon"b*[ BciJ)f[/bb܄fvbZӛ§YRLiSq3M+\) 1u,MJ)BLwfVCf6$KR$b P1ttG'G[uyq4[#B V ǖ"b""44SbZ 1 _ip{# ,4F\Rv7!~dD`ߟaB)BLޛSzSbJL'{]{KE)JJ52džc&q鲌PӪh8=~ۿU &J1N ߙR HrJ)Flh_W [erM)Sb*ɈПOrKz*żgJQbfdo48ZYUd""q䡞+Z>q( 5dB=\wgJ&LYo&}BLD)\L9o_pvbRjG4T (%EQ ,}$"C3jЯ3#BMHC=W@CL=ꂂRbBLi=7a(BT'MtD)BL01A]gx`NL)68Q  b_&qd#BMHBFtcG|oYáӌi)`_NK O%J=R ( ;;Q 7d;B),*EiJ"o*2# (cJ8b@P8POgSb{Db;Q ExMG '4! hxc=X߄DBPZ!Vߖnw Q)DLqD1A`,P8 K2QyTa#M61oG[*q(ObM^gFFe E Ix˲( ˍ-X0bb L+D)KlRR +ŋߚ҈*/J%(&TIJc uz.$DLMihFBs&G:  / .9(% .b&l# ߄#>7"hR.]^(źJv!&bLb:1C-.bEB&8r1-5RtO)b 1pn)!DJb¸1z gJ-9.L!&j0 D@L ]L‡+1 | Ǯ,W["Ĕi/; ِ7!g!&"\b\y5aR!\> @ J%b"W &f!4$S:D)xK3Q6z#q3$ ة5Q<Ă]/ؘRhJbw4Vb;\e^5&&q$%Uǵ*[J2]_OՄ(&(Eo!{ BX J^CL/׾agb rk:7&®A)P(D1A)JLS۪c48ZsGˍbq!5C@4DT J!<}'%iK5"&Lܘ@vإyb"gFQ:'T' RPS whm,b&B̈́)Fm$Ԩ-Z4TE}7%&pbeBp =JX%nTJCm<& aR &(E J Y)e/2@pB4 1Am!LLcd,hs%qDi # 8}/1 8L%!&%+' q t "<cϒpR &(Etk p,bVCDL`DL ՇU1#8 ތ E18ZU"C-b v&&(%Ip"7IxJE04:&-:BL'(b"JAkR*9~Nd5q`j,Y)< @Ljd,h8"Ԉ#C8P#QL*# P L:I snBLHx0$ 1 RR ܿ7ᒈR &() PMs_胨JBQpU)d15qTmGj1Z'GL1A)TLe1fbm\}6 1 w0?(lKCZÄ6P $?LxЄB8A8(Et"&z6zr̛P J!bDLt k ƈc 5q|Ah8 o [GAbU41QXY"&~v§.!\!ode@ LLͿ!PbSR$[;@X9Bf 1ddB)%im"FG9HDh[/hC)`G;ݡꥨ#3kCQDLDL p;0]xï&<)X_"&4 ͟aJ)=B4Zޑ{PI͖)΄|`9c11!$4vsG~LB-h~'3[2&&4> 1rtd፥=%܍l/K{o2k 1WƒC)DLT)Q>eSV'7=J!`BL]5w7*&đ0 'GFG]M.Y1цcKRRh8644"$/poBL=6›&L#]g(DzM&Tatp/ iN(RJgBLYzaDVC8!R<mTJӕ&%12qCWŧٸTm8P͔ZpyptKJ*YsaNvM "(5S]t"'NFD1'{TJS53y\ a ,gS%SJWU勊m.h6&G;݅RU _G_~ꂋ,x} o¿d]QN~zyGM8!eWj9{IڻcjaFur_-m˛Mor?&į\⫂vsg.4^|o{2ڠO M{٥gMbrl$@buU vSb*ٳ$h8uvQvٱ7GF&&pLzMI\*8_RL)p{ӣݔ4$a— i&>éXvJ)TNd`ps!&g\#j'Tͺ"TTEs37,hݨ'G7GkY(żɂ$bsptK5!$/Dwt7at+b";<#p9N\B&45þG0RTG#\݅]J#S =%y$1& $eyaz9S 6}襭ڄODXs82ziJD)҉@p(WSda+yJ%gĄօ ] #iNGxBD qJ boD)(Xɲ O? E%(X!b҄ 4Q ,DG;{PWR4tY%E9㴒X \Ld,֤\"J֫b(xWJZV~~A5 \^BxЄt$a$Ki˔o߄\WVD1X AOvp"rEܷLeD E18ZU yb[-qӏO_M|p3M :g;O /& pA@)QxBIz4C$R,PY JbJňǰrq8#B-hs%qh8Pߔ3UJ_ J bz ۓp;0UJ=Xs<$<9˻?Cݨr39#C3DB ] P&Ĕ)1MɈB(i=#8t&qDI!&LKG1w7"̔ DW~y/D)U΂Gxx SN4z)g#bRTDpY2%.&$8RIGk.h8֋8BLc OL)(241x!;JOX)[~*i oP;GH 14*'&̔b^(RQJRЙa:a$Р`& 1!m!LLcdhE-_H!&GvG} D)JbUO$ 1'܎ 1a۝+bB7P0D0?HK XWR &( Jb*+N:b݈*B46WGnN1!$Ԉ#ِ_ )6OL)x/;EaB!&M /Gn!B)6 D)Uy/1Yv NjV 1 1ATLc MgQ0F>!&1 qFcRwv.?=^ `t?N,G_C<Ax7rt771Dx,;Q Z/U 1e7%ʏNjzɎ|0"&Et퍈rSq-!DL# uZ$H*N҆c;5׹G[.A\L?\ ߎ \)H{YTL:*UB4 {0UW())1UyV_$Gn)PegMW1 5 B8ڴ#$4v:A)TL-CJ PՅ(͈*ŖDLn@ Ft >N |Z(Ҙ,,&3(>H ᄏ /%\*&ڥ;%C2vO"!J'bj&LwcVq18֋8uGZX[6!&oBbI4Ɣi5gXR!}ps4mz#GFx*01-Ed1qEx##_Hx儅R,BĤ[M qۇl{,MP mI&N*hiDh-o# 58PӆcuJN܆(LiMi:˂5!ݨ |^&$ $bF=6 WD&•>*%+o 7$.g[:ݔ*81z&UmwG7GdG󛈣٘Tu Ҫz5uhJ)&6WE8i7N8i7 o$Ci &4HL"j XNB݈$̕HdOxBKJ ׅDr.<ІqASJbig~VSW.<^Bs K*ԥx\:Nڜ^fps}G+#Dz+fR > ±ǂnA)FbM/!!& PMPx%~sL@LٍJ7G Co]HAhqPlj~p2RHL}4S=8o"ܽ0FR95Z#\I)智tpP u]f6C:~: )h:uI=C(NGIu?`RP?= $l/&GĔt3(b\O坄ßD8;(Ŭ9RK)mDױ񘄊$8D! (5VFZ(8!j Ođ(E$J18̄n wL N(e[LbuJϵ*~@'⬺(euH8>#` J"9#bAG%:ܵuv*mD) 4忔pJ«nJV )] a:3c={^GB >3>J=b_:˝`\GP~Mv%Gus:kFwDh]0 TptbFuL_L{7aP3)Dq !_:.͐6lɓAɱ@LZ 68S/h)ԭ3 , 8Xl?/@)mS)X?cGßD80uułbүo^WbJ,[o8ǬU)PrmHL5sq\AAmGњC-7⨡*`Xd~U)k1ř .HxRت-1iy+&RCxza@)*)źJX67@L1tj='U ɃTJ15 4*@b-{;hLq9mB'.Ez|!"e<1~ga g9.P,g_z @)"&mA)[+Fbriwm`I" J1ZV<]qPAŪ[ ۡ[G`N,p㨭ƲqT; bZ.%Hxڝp{ ,0u1y9J1 Y+* Y i[.]ɓb1b|M5>ʻ?G B}*KHbjAz/bjqZ0iq)NgaS` pJSvnm}sn]ՐHHVkZ^[Ť׶ JQ1V(hQmNG QC? z-J4SQߔbZ\Z*4ւ'<]1YdW$FZyJH?AxQ+U)$ʭ>o76zmL薋]wU) t@sGPKogI{heG;4:yrцוVbgU*˯t οNk®a6 ¯i>tBJ16D1Ei "i(Z )[ ԕUBznts!G*&bCO ZG&<SyK_Ą/!\D)ddծ߅^+)ӫwzJFbr3wb cI(%"qHevGQf'qى?dvpU(E$r1r1] <'1#"",!BXfi/Fzb*E8|g5RsP z]1ZG|bU_$G2[U g!6,\C qh8r8Z%*&h8Wh8ూR,NkJO֕A΄ބUS4S9 nr!X Kbf|iHiZ•7 JJ1Oz2: ~@Bq X"qPCp8Z) GKQq4oG 5(8R9}+9J+8!j—5rO> "bJ3|M3o1!#! an/X4DfzՋҝV{N1N%ɳZC q4_$j zh-C(lpXE)*&0եመ@)DxŒs&Au^@LW^|B؟"\¿J1˫[}VEX @K7;<1RTLjaLq9AͯG>B!z hq8Z@)MXb=)Ů.JQ11a„>|ݪ~9 g3$Oie-%_V|(4~,B7҈?4D#?+ ш,,iāc#'4A't3aNF'tY[DJj&R J Fi?pq7G TN'*8u~$8}43Lo"@Ƿ!8a5d bddQ,J8B{a(h]K%jQm |thG*EĤ :{$ܜ gց?|MޣKJ1%nVۤ{[.b$ Jn P͠dd$)(S4uMĤ2X`8yA@b-S 4pnFbj'J_}W#0(T/o3صxՐu~:0MĤJ1y2S(Fnـ5Rw㨡8X!w*ˍbs#bXA)"&Uʼn-4Oo"UpDŽh"&Q #̈́6@)"bRwz\Aɫ2JJQTLAQBqPDG;VŞh͡X>csOR@LAJ!1gkLxdÄHL5_W(EŤ99(EŤ;ZjJUWb1 ĔJ5k!6 "&1I'{,|~b 4T)dy҇h* 'H3a@)zfF=;$PIjKJQ1i]Qp2 @ɱu t1S 08N<8R[g &o42bZ\f%JYX@턻ߞp`c5*eSL<_^AC8TPIbq X .orՐS2/U 1un뭦qP(-ɛj!e{hÑ,jQR6-@k6i - LJ_"bR=ǞjR#Gw'(BuJQ1i:W}MTJT)$:U )Tm{~:VZnsP3QŤb, g,Tpn(~`_t֚^wtqGǧY#lHRc<LtQ&/@ L O z ?! 2SIvB `)M7B al*Xeęi.IrNR$cl9uB `oN Ǹg & Ӛed$x6<|ׄ$|=aG 0,~O矒pRpA `")>tfL˄Dzs)[H!L$&L>[{x`&'<'HT2HE u(&BW@ `0b 0iH `)h ŤZ 7H1Ή4 QO -d&k(4p&"w'L'ify"4$DRb8rlR]Ʃ E`YƸL$08v G$0d~DR@ L  "A'7&hp[) I=H\) &&D8) g &LY28+pDOeڀ & G{O59-c҄*0RH!L0cҀv@ @ 448a" ABH AjLi҄pC S=$|фWkL>L= %L 5[hs V&kh5)րXk@ `p5 0|X# ' Y0BHt? A+ 0>a 'ް&Li= _u´?B`)Z}X/ H![!% RmGqbQ. 0RSu) &44{k8qN+b>}!0TVXx„~p6|h4%,&ar]B`JgO8%//$mT`mZ}VcL&BH aՄ!̊p0)c8b&B N!0M6Q)pc\C` &&L* sZF LxބY: )>L:$20.d1@ a +`!2 Rs) ?1+pD6c?0#aypd `34l[OG ~4I8'&<}1R&JH ?&|) M>)) I!LiV;: Q#%&B`%dPRH&!%R^A(7BxO; /&0aD=k,^[i0LN矒jwMK)&B#&i%M bBH3H!LaVH6pHAp$L 0mIÉW&ܻup0IÉk P,k\ ӣIń kчIޕtI/&H") &Bj1>T0HL 0RƠ.ef 0%Q70 H!L 0P⺅u)IHr)i8'+I[%|i0 %LBJ'!%:&'&Sp)pZbN!0Thtsrd EkQA a`),y ؆ckXR"[ i'q/a0.pbNt]`:k("%|ׄ$|o-ޥq!]a05& _ޕ- /%dlDJZzEz;2q Ckσ4j/r=\E_d圵Ez;ٴs'O {d{Kͪ9M^] RޔZZ$rdouyx=E^.^?\!ӓ "DZRH[4:H|@4# jGDR"}\0rPs8&AH,ٞ|W!0scND OÎHnV-/B &)Y4l-R̟??O7#%^H4 )D )X) &"0.#8p$L$%j/Ñbq酔Xv!0)0quUHL 0 }GHL }Z I a")q??E>H v?(ab.. o(&@ `1'NRISWY;g)|\[*#)2ƍIt]ltւomƵ\r$E`z X _J&^IMx-m^IsxĄ@-\LO'\f!+[lcRI!Li#% ) &G=2F􄉤psBJt{RW}oEN4^rW*;գO}&]~"a-sA{QS K,w 5.W!&@ `JRkEHLZp$L2 !0A{@DRIHa4[RGd H‡%Q/12 32O0O0Q gOx@\Z@%_(IL 0)4v`q0 RWX2@L q|&_J# L "&t{`U r,FlUHL0≄iac0BL(qZas&)\97Hx.BJYH!L4?ڴI!0賐S#ݳiH.&& 0.ȓq5RSDYy`4Hx %CCM ?ޑ0aOEHL?:aUo-X𘹙5I!)Ԧշ?ssZs"0"”3p>] p4.=!)<')<')< !D$ssBd"{ a<Dpg & s@>R$3I9I9HyVi(=I/ Lfsn3 E_uH!0s5ix*#dJ P[Kg!0qѭX0d&)S$\nLG ' R|[F Y:@ a*Og@ezDou4Iw[̀)rG*79 I1IAvL\+H!!Nz| ĂS DRcAsӂov(asAcL +HaW&)rhs?H %Ui) *@8%L$㏧+{4= Hup`HRH!p 0R /W)xKX`\‰M`\ߒp+8LO'\V}:S,8*6ajTHa$0=i iH!c81R! aRn 0ĆNRt66f~%L$04{~"aHR@VCtpihDT¤vJIz„}&LR(Q-zw#%DR|?%HL 0 )13Y2H ?:!%J)&Bi%%RӃY턓IH`8&#%N3#`a=' 0pAn/g!%! 4_`$ ^Mx1?Gㅄ/oN$ |lhp_}&;„ARU&B)al Hr)qLRaRh88BJ /4&yBRw6JLRpJ#觇ɞߜy˧ ۮ ړDDR8>=mD"a2LR'Lt~!%Y4OIHSR"Mh8 $00YFJ H 4+.&~55&$LK HK)C>pmzz>FJ, &@篴s)&Bp̟ulD 0- GDRp!I oE+H$0Ӌ _ f)IZ'|0eIHrRdN8B`j0I˾rKv$0t!096G),&’K@hm8i',b8&!%Ñ0 )\A aBLp„ Xf{ޫ'L@ $&)\?O8Hx>I8MR0ryHtߒI£$LRH!0/'ut HLFJ,=4a6PR"anRX2L(ҝ0S'2paĚA `2R@ `B!Lpb 0)q+ 0t]B`ikL|z9{ުi))?5a C© q&!%f}]ɑ죫y9X\U?ϒVÑ0U+xK={X5{oV}{ۘEMxՂ8mE dĶ7%\+u/wq5c[ݾ dg̣s_cr<˒lmc/n\"'Orjkժ9LWf ެ"'MxՄp<ޓ-۱ Y<c^chХlѾ: c:U3\g>I:y'? u3_}3Ym:W/-s[mn09mߓpss5uS2I-w]IGu\ uY$v]'XOiΑWs:']t$l_QWP>rӂ 5J:%Ӓu]'q[{Q*Qap|Nt#=lkR"BJLSw8ua+Qp[m&4!0K% v GaK-arS"Nzu*̷9FIg::>xG(mRI] lXµ;4ON gk\olMMP7:Jxl+& ĤC&co{\ߪ^$p)twՇ$mGOH}Rw7ߐ|,5 kpe#)&Bշk6b0θ̣U0pd_"0IR;I `")d162FR8 Hp݃H\\$U)0c].M=.xKG18LX>zp-B `By*a"Q;!%,&cZB `) &BK/'Ñ i w&BHJ܊ p ) &40 LL0?)m _K +Ix3Rb. I `)&b0h,ѐR!0qc?pSSS@ `)8ЁS)<%)Spj! H)$z"aSHSSSB5MI >$E`ZzL}H<'8(oHB `Ґ!&r]&v0'S‚iabiRߛ'L8;I3z=A€U &L}ן oB" N}P1JCV_7;R &g`JR5p#0ͅzÔIL$E`1RHTV0 wi +*#)IU$LZH.:L?:a$0Yg6nI[aA 2Ir߬շ?sbĜ>i\`j cG!L<!BHރPslPu)Bȸ4!8'!VchRn?%L 1y5Cc`U IHR3 ss$+[$H!DRq[B$$0+g! 0&V@RЗ,d/;H!Lh R JR\wu 㭣`#%N3LL>LFM>NH WNRQ I!DZ}# %d=&[Q Gj3I!L 0QC{C"!I &)CTC`)0i &J,[œ'C`A 4& &4&) 0W) kI ~Q;P`bqAp 0a:!%ACHgk8 0&R`BiX)q0 )1<@ F '\@ )i!0u53H!L 0dD`"1 R Ǩ2`&Jĸ?i!%`#%7> _4|Ge(8 )^АI&LH._&LK)T6RbIhǀ &vMǜ@ `)IHq) &AZB8HÑ0pX) a])肞0=@ `)(ޚ&|!&,/&D sHs4)yp[>)f@ aߒQ&UT`';,XFJE#RHLJ܁9~OA aR?@ a<0a8"ck81 0pu) Ñ0 )1@ a,Dī\#DR =a\>)&a|9&@ /H&BlK)y v}"0}L 0)= RI!L$%”KL HRӐd@H =!&pp/I!LҧúH♄ ӸtnUK8yytu LZ w?% RdBH a")?Kw8dDI a"),C`)x\uwG iewt0W̆CV0RH)X)$0cL00`٫$Y·@ `)KG!%dIX ^;%&BH!L#)LV&BH K^RLy Ñ˵#=@ `) &i8QV4v!%`#%& I!LY /&&qBJ &i8q 0_`#% I!Lp=Rж^`kG)aa-HU!0>䣇&- &Bh_Visʽ0@ `)҉\0 Ǩ=HL 05 )U4d_][1RS< 0sG-OY^&H |`4I0'GRS_$\ RHL]Vo9#{9^*96Y9mFJj XkLFJljᜍ\JX{u IsLFJhHx~O(sw$ 4a59kZU58^[]zNc_B`5bh`(1X-9z硡"*Ŵ(!a!g9D}#@\fWC|3X=30ɗy.NvC/0A|`ɎH^ҊK}A2kuLjԎ> ,]nl(E'Uz'c* =p(|F 5$ ?wvMn\\ EQs᪣UQO, JٵnDQcD+ZK5H)jFRqFakI!S8i Fol!+X ѣ{d(Db =4e o4Y4d*1: o'(xSrB™74rRDbj"1E=k *Ec(EPrDb@L%{p؊ @L!V٤wi?h6VH)dPM$t'$ulA:?&HJ@L"Ix45wR G(M( bgn'RJQ P iy8(EQ](vaˁQ8N 9@L&Vii `Ą'*lS跙+&$ܝ0(..,i9F?TP )\L2n@yXFM) 1 TB$b׬r[!8;"Q8Ґ8j[A+:Y$&.x4$&VkP^»HL\px Rt`RJ;Z ~F]\\^~H)JQBW,:FRJQH)3(E% ؁(-t:j[(Gm򅖊PɔB åGI;!.|1O Au]\.Ƀwn >hH).]L1ђ5D%$ST O* 8p[{钁Z-m,G[༗Vhc`cC~Sܹ,S[bʗ(% ..#dbJJQ Z?5F}Eԏ:RM1EJ1M\bHKQr:p@L8=$&+4@LvBcz|Y„4dH86z0Y3f+Ԓ@L0db:5=UQ2W iV+4g*(Ebr4p[|!h@'])|@L3\gñ9a Z`N~f^  ;& RU;HJybҕb1J1E1/ic({Pɵǖo[e8U//Z-Ė bg+4l >`0G JQ;mЕb' qnEpq;R5RsغR@LyU b*KR\L^eC:JGJQtT8ĭ <,lw1Ӹ)`LlP*&|DŽ8a~Xܮbr_~s'LL%l_c Jq1Ц JAiQ@)#(,5酘<QH)+=|O_/(!k1;o]) q <ĴF|_s ǿNX:R\L^?*.`̈́kR|d˹bb2.˟9{:OTW HLM1EK1 ߶:-t:j8 ql9b.q(l0v9pڜe,a'4J8CžF- KÒ4ڄOJEKͬNxLRb4KfOR9 (EyxN}H &NG:p\L8T7b3=8i٭:pNP '| +(#Q /IpqyLLo'' (ŤdW `IΕb*x5 I&n3(EZAT7BTB:SLbn8c˙{NXҸ`Lx{N-p1u&'$'%Lb℧tc&Е]>m4~ʁ_PR#( ]RT.& Q8ч :@>p8aVuk`뜄bzt1R6e+rpM)$b^.&B} (ӔbʓHLc% 4 -ɀc480"ǁI;pue\LnG489a=K+(E1i_b280~[8tHJє .&'6&z_sgM{uWbr5pIu`+=-?A.&b6B98WHsfnp!p 84wN2a1ٍLx}.MtqH'FJQTPJ!Jcyoo4T=LLM2Y.& °QH)z8Q?RtMWc =&&RVځ]_7v,$[>#q}#aڕ]Rc?#霄AL ?NbkFJQTP҈8 ֜?{ڕnmI S=b%[1Gm(-t:j 򅀣983VڲQQH)Pf8 .x7 nj_@Lp&S)HL[iEb sU SItq$~>1h])z >앉(d\LbW.&PbW> a987QWiR21++ګI^ S'bW.&PI^NH8OJQ {'Go>HGgJq1R4R:{83)E # p/zLL gִ8,S9BLXVt~pā_H8G[vธ8-ckH, 6t"tEb2my/axgps gN~7Lei/:Mi7QbJoR:ӤrJqY rJnI >JL4`ZNGQ 8ڊǰeqlpuCL6f㏚[s`N*Gw){We{%JQ% 6oo&(EѻR\LSc]EJԒ@L}dJq1EyyqN,x 88 Gn8>qR|@b2ธaV1 S~M/o&u?$aRZbzIÂ9(EуR+Qb:R)EQ1R\LQb/ J(z#h+q I G'4Gs=2p. 8m@>.XBǮ~:S &ykS˴H 3(E%L).&P^}P)=v$S(~[c"1 tD(Q8 GmY8 ^=.HL[=!ak[Dℯ cAu}\ħ Lx;qI):bb:!IS Z"1EOmG`l:ЗBaku#NJ)4:=횩TALN[ [{p"lm5g$|e15QS$|;#%e~Zj >H)RBbZZRI) $QHyak+4a6)KEpU  6*Ĵ4 !m [KKncbcTp[w4Ju ?M8B+֘A)*(Ej BbR#j5@)V+EQA) Ъyi2l9pTG8mbr8}[$Ö+EK!Sc -^]wF‡@L$|`]pA):>?EcPy%y9Db5Dbj8I-Nkc)Z(zPR#(ˣz#pmLL~U6nU 5dk)ssQ8(luݳ?(aPgjmvR)$bC > ? gL!\ %P(5HDZ q1Nhp [u@u8|-R XVb"l Mn oKo%X/؄])$N9a1bC&&W )9?M'O1ӽT PQwpm1VtVhi~ /؁ؚ6T[i>˂ pnP%8!D y*+?+ JQm(5RRFPۨ?(剔1RTbbtui+l.Ӑ:8}`}ꍀP1l9p8a/XV?$|PeVigs&JQC 3@LQb#>w2vi(HL eUO1]L!plOpO r ql95 JQ`,Tpp\L<w/r]ql95M89q ( vj#m/X pp])CU b2//VۮSjSfÁCQ$! |tÁ6XtnUqun Ĕ/>lY[Ư~焷B ]t1dK& JQ*5eP_;JQ {CD 3zPJq1y" Zsϰehvl9A)'d (xíSuÄyv40br4+8i .ᴘ_9]) rԒaRT>kiKA)J FQɀ tM8$!酘i8:< K Q1lg$u1%>C.}S8^=T#H)Ec"ĭuM#\m >@).&up_t8- KɀcزF2#q19 *x?(ptPNXp.iO ׯHٰ$y愝PSdW )!2&@Y(=yDUrR, 䁥õjN> qli:8 G'lmF )f(sv']VAL{T6% JQiJ)EQ.<@`7nѰJQFJQPHJє!8~vHL!p Gsp]2(E>$M=@~pT6n䄽` _&|IaRu@LOx YzńZ$1 TB$}/SjVL&&PR#( 4s4DG a28ZGB0Mu6:q@)>:LVǖ- -wd( NM$4Zpbh TB$b~(èQim̠bPRtn&11Y-Հhm偀9@)ݱāQ -XY!o\ g.K^ʻ@L~2e RA'4R4ePDJјH)%>ys)KU%RTpu(? Q?Kǖ4lp-^H)UPn/MLXp[a^:0 GsO 9@)7{BLp R| W% ĔO^~NT $(HLM1Y:vaZ19pU,x`ˀdDZzRT6Rnbb2ธ8jDZE #Q) 04PFVb:?!bfhyk D4RtRJsgOԒ>bJM鸘 W8 GspTgb1l& ,OG[('pyㄫ#fNI8 I g$@)Z)D[WtJsO1։ $H 3JR\LEODJ9>{R#( (Eiĥ} (O.~+8GspTomQV( ּt@)*Gs(f'bjo%|y:aӖb2,>MoI4hly 3@LyiL1Q>DkR)EQ!U)MQ?s6^u2p!p4@=87 JQ[L)Nz:p/=m% ~ {.&WϨSziMtK~0ᤄALS/ȏfh剔1^%.%RJj(E5@)R#(*ÐFJGspT]MWc pQ܉-S`b( ]G򅀣 d-3l}03W$)2 vG+Z6Y G*SzMiGG,S9DbjI >2pؿpɀpnX8bo]G',Q?c—% /xK{XO\ wIO_v2JpFJQ@)ʣ+Ť)=H-M8$RU-د)^p8G.<4VauP#lUAi3FxHJGؐ]>Z)53KJAJ)HIUS:$ nRHR$P )N9F)-SDڊ[yCL{ϜW)v.8p[ C' :7Bbڪ@Lq 5s J!1-I 3bJ4)1rI \ T JT$)KxU%z8*p3Q[OIhim1'WU.x+%%m+(E~% b?|ƄWpsJDH)%RA)SfP tRsE"b:IqʁK, 8xE+ ļz>|iX'X/e~iWPʻ^ii?Q8ZGIb /x_|3kڐ*pbIط` JQdmTKZŴfR"R+(EbJעm럕%)yp؂p;4^ h`?ad=Nڧl[0Al;3,(mRLLt tboϒ?AdtR (}VY"1vC$H-xjԞ)E-ywϯ:Ĵۋ΁C=ɾЕBb'8ql}e#h'6pPO鲙V˴e7)EJԛo)+{"Q'Yg;Q2`=G7h\8J;Xϼp[R43`;%S¥RH/G%H cjaJRg>E2RHڕBb Is@i;Wmf!.Y s&V~RbALm) (`+_C`J:aSa[eb6(RHLK:Z Pɜ4ԟAJQ͠G.&ӭ  /{pbvp ڌ (F` I%<΁V)\)Li'.}q&+*Ty:q=[!=pDqEz 4CJ1dG[D|Aay?4@L'Ƞh($ԟR[nE(Iq@LQahV983G[18h0zH4t@XVh$v,t[h$ 0qlMr"l]OLQy@L v FJQK@LAcaLiW{;l;L9^iHPԲ@LZ8z#h+MDZu(o֥(lqlp4&LGks88aǩ6NHсRÄK$|[(7hUY+ 0Z'`1 rR~r__=ˣ()[?R\Lf)(z#h+;M1쥘N4f[eaV؊YKƄ,M PM)p ߱!ۊ ŕb剔1rJ"Kyc>鳧)І5_)VppmLlp [dqcˁT}tX^NM &i"8H-'$|H)~6؍^0(EÊu^OH{bMв{S `gM{`2 Q'J\[@LiŲ 8-ހcs88qp:MA+M$;l2dB^9 or21:;2|~?MxIES3Ә{Die՟G}QR"a'"QA)O1X6֕:yqlpu(fCsڮMLGk38h983G[1H/=LcA UP%|"1q] gJ~!b<Ťt?RE(W중R$, =Vr+?M)-4p4, 11[Gk(Oʆ Hx /~>x"4@L_'|lJq1\-ROSj&~N"#C ᅥ JQN"^qaJaKJQZhKpb788yh+1MWܝkH1=:`AsM$[ń"l _EJ8 Wu) '|9!ܹR\Lq8^FVRT3(PN$k:WtVT)1|QEJG{pT6np!p )Es@Ly[˦WjieN WNx+/vo&_xJ_@L%  *I1EA| F}fJqbE "1Yxb˻DbZ^Y8JGQ8UQ(EJQp4Gu&hGf WNX$Ki\V='|XǛ / ^H)jh^R\L^'((EJ*>Z`-%(SyI]). 8&&a }r8b`ˀkɔs襘8z#h+Mu'$&|HO8f؛R\L vp믉Jq1IH1ʝH &J蔆JQ"1FBÆHLu'#ijvc Q8N區K 4\o]G#}>5$[x;VMljpΕ " x ϿLK(B]Fh"1@ (ҽ+Qb: IPEѷ$8zՅ FX~ji --"lpϤ]HLp44z#h팄H8_E$}pLT[`hA `S(vtF+%C;:LtD2T _:dY_C@{=v;m# ڌO _$'$a#m8# }FߣB[&#MF:bZ211@} -"JA㻡ԎmZ鈼`'dLtȷ ~PokN2Q,2XFGS'3<ōvYzd7vfuuX`-V8˼Uctl _0_$?[^0muV3A)Eg'  C ĔJ18 &nBJq1IsSn ᷧ1R\L~vMql9?8 t~y(+ؾy Qc6}F‰^&:ĴVye'$hbrz0jR qC( 3p]LdH{Gr3K2<uy 8l>3]A[p4WLxAiҢϬ#&|}D™}fHx‹ R }bFJJq1zQL܊; PͤLF# !p43# UoNXS}KD'BPٰ`?{3dGAj\`Nxv;|JԻdHCi! 1H$\Q29/JQkAݕ J\ b*tL4@Li]4] / 9b(zC dqr]!˱4rSLx\u$< JѱSK~ZQ@)FԐ@LzVOzr)Qɑbʽ.=H)jFі 8/[,ָ5$hei1]$! ɱEFB&|5N$҅% JQ?R\L4-zV#Ϡ0M} ꋰTDb*&J^$H1!oj8z P"O+ՙp [G΀ uh+1.ӐhmiÖS6 gNت]L0V{RTWc~q&&Wb 'J7 T))Dbg>eqC('PR/ JQ25(E5gZ(ESFBG[ /eqoEciH1u3q[GRKxpg}!~b3>~D? I ~U &S#3@L9>%^c9^+<֒#EP)J{+4+(E sR-&nG ѱ%c ( G1+Ӡ)@p48ڊ%iw) mV6Rny;&CG|iA 徠}mm( (?2!>ڊ)9 ĔH1^$hE'i!:wm 8-Gi h.&Wɕbp4򅀣987~5%8%(Ee#V4-'6}iM8L:R\L\)DV,S D}I1.FJQ*r )$SZ)nvW JQZ GjpC_qHC-,l-1p|@~T8G팄wL v[f')4PҨE opmz,I 8ރV)H)?>QR3(E*e}vJNJQ]ᘘ8pt݀bhRp&=!VDڊ[ sʗ$W)f4ׯI R;)B ar]LJq1o?J"W,7>rVj)uBJј󨗭SN"8JGQ8Ub( Xu󅔢983G[7"=ZG.p㔄&\bY$KSO4$Jx$p)Ec5 ʡJGT줐R*E9c?%S')}  `$~M+vapƾ|ieR.njh%CTO-SBd!Sj 5$ZB:ϲ)uj +Lܒ` [w"EJPS{>L!iM9'$<1 ?rd(|kҕ˶b—op0\IULzcͦHW->%eRTN)eRTHR~ E Hq|Cq|(7P4N t0億stnzR>PɗeX/JHњ'$7L"ȔV0INn(Q>)Hq| VVZ )S:WL4ښ.)/[ )D<;IY0E:~˄H#\!'\;YzN <)?_)c`:ջG<*ާT"E]$= `Af[L0&"Ek)j+GLqcytN 7Le2#OF  p΄SӋNޥ&]HQtZMC)&1DjKHWE"~D ^/Bh.@HqCQ\ })70 O.$H!>S`J`Kj&>'`ZFMHIU#9٩8cMRpÛt g)w'޶8LNbR{L*K}L^y0իXi~`BSNHv~d('˩SRlu#*%p`&hZj ſpY%ZE)R)j~1%;+e/x'SRu E1Q#+5]HQH;3R\'`ʇ9)S߸o$0ybyl|\i~O^) `#ym"SRroaѨ*S h@䂧O^0 '`GQWLk/u8N`DdΙ9d&ovoך@JnI L&aN=+7`Z&<GGT=& !HQ?;)SʿoI~(S0HQ4SSٌ# `uR\}c'`_sR}3,/`V $Z0zń?',i ?*֑@|M?@dmHQȔJ$߷IxHP>)tRZ!.s EtR&L7?3'`L% -fu E19)`6s>+z=Pl—Hq.Q+,I/(C9> "]䬎(f#4RhBS5 & ELE rDhM>X%Cr3Rks6' m?~O½)S$VQ`'V_R>J L&qRܐ"ƀ&ϊ[dNyKuRT&"EG)bb-5WIq.`tD0 &B |N86NwR;.]~O—?%).LD0e LūS|T"E)*,r\7mES|LH0)*5wk80AѼ)) 5 V`0aqbiQ8LNô g:) c)o>>"2%`Z}կRZ%R$RԪ[ܳ|^@h+?k8i6LHqϦd (2R5`#<3R &<3R3#`23ӟv8aߜv&/=sRܳM+ao 1`BSˇ ~7ZHQz$%dAT)&R HҷF)HymH1C)0MSk `j+<Iq$&#U|`ߘp L)8L% oՄluLܴƔR%6⯗*)hjO-GLFn1kg:)&H׭85*}*.)& `+}FMrUSpk'HvjjSLF^:>ODЃVk!zQ"` P""i)N  E}8<ſX0)zpT&"EGS%0q V0R`7 -a[)K`"I1x}bT"E>H@ !aL@ EvmmHm )DHQLE+#auR0)h8t9a鸟0+| ZĦo /g&|&).8L@ucW0([όHWRTH^vu#aLF Gq%R6 EҡSRu E1Q#ɿ8Lkr"8! NXhM'ahL9|>RIv'aj&hZk4SZ} }zT+ ODDP@/ lDX}%R!i*hލ K`I(TT.).T0VLrtS?"a8ImS0Q=u)ʥ`pDm4 qtR|]oRQ>.1)U億OH +|m&SP·"E}IL]߂q"VF5hd&mI0-il1$ 7Izd-|Yi'noDŽp83L^1'&|ń"LE p}Ii-g-gCgrp3RA1ς1A"?, czH lߟxrӥ>U0.׆'wT)K'w}n L| &M+u):Eg/^/E*)H)qx(i;)+^YnA0M"7 e-7i -kRz₿">RG./I8"a>V,شM+|:aO `j҉AaUӸKhށLSޡ`+ޜ6LJ 0]e"R03|T; OҚ@ڊ:^wcvi.@"z qN?iD9:HjI|;B0-!œvG @ۚo""ikIҎ?_W `BS0IR߀HmR ` ^g\0B0]Ʉ3Xs0tNJI)SsZi`50%%)},2N  0MLΝ~=yi20ïov;b0ܴu9Qy-ZS""ȸ %5y;:LtX:4o[>#a: HS);`0 8L>)`7 kTdݏ0W31XKV(_HQpa`;r}+쎊N@dLpuh8 ^Um@rR^9)S߼2'`-fB0M/0 0)2q=.k7;[OI!豈)%E0qN?yDR&w1HQ$"6 @ԁOFzR({@w*p~Dٵ t~gTtr'Iyj@K j^A 0ڊ8)SlJ20EO'B>L)LY,i %awTو=:d8Ln50oN 4 2`N (nHk^XG*x\=*/`w;'p.8)-t쟃 N Z}GJT_[-i IT"r{)H#ɻBQ@Dh`IGFJ'`ʇJaL}bn(}@0n$D1)*Hq7E`[oIQ\U"$jIQiLϜ!|%R)~> GI!.!i0Yۇ&nfϘfkuR$=a΢ىNLqB—R| n/h.)4웓zI ETlmN\ٜ0g~1`0)3JHONÆA0wFD /%'|yG㳒޿,a>æj`T|(ڑ"';"L됖g (G Ez3b &;mM>IpҺo`z·E헾K٩ 0)H#鄄$-c `2R )iuO(QR3Ra V0I8fLs2wP&*nLD>}yd0Yq57 &aYag vR)οj% ovx#LM"ȇ) 'HJT)K~K I֫Ks'"E|twa_+<8b_W._ppk{vӡ*a)8.z1wM `z $[ ̽(Sy=JG9:)hwԀKHq3V5]*Zl@WR&h8/J u ۃ΃.k }Q6^ &gcoq\j`EӘд~ze)%^@QE!)kߍg h vG@ G r ~p`9Ϭ_9$ W‚8LN8LJ`^S vRvnB ￐~.c͸g/*+96sZ%D0U8LRė]wB0)N57 EJ GkHl=ϭ?8Lv`RT&ZZrB?S H)"x7Iamo.8L@b#8L@jsR&)Fns0U) `*,o+IbESMyQe#Rt[ŅHqx&#ů{ #Hqay@0i#&NH@ݮ:L@nZ#'|9#a>ARTf0)XeRTMrZ?_N2(f'3)d+dXۂ}R#af9L@(&"Ez0)"MaWVp>0kńLS`&Mt[>pgH L}Iqb44- ENTnQ Lem)H9l G}oWL*)LSmL+r9}4 Eހ.ގq%Rt M$pΘ@J'R&"E`JlDJ@?m/ bLȤI*NRzig蝘Ç`\!*׏&XE0){p.D>V E~p~Jƒ&"Eqw+&SaWNxc`J߀LD oc "飍+"37LeSNN 4T2q$0DzBJ'KLєz;LI,"ȸE0EqKLe\4 `ZGD G#i) qW B0}4LI<ƕH >}[O.LEHႥa> 1 0YDVI$)]6 `Qq/TX &AԶ&)WL49zߑd09);)Z)Pp\;'<L,IQ g EHQ@JHZT L$Y%;)t `2RbG8I=Gq".! 7#m I_`VLy`)I1 %> O$,OIRT@kA I8:C) LF ET'?'!EH(Hq|ɼoI `YcSjUDo7iQ_C֧~N 4 `wz[zN LxلvRuK<& 19ޑB0qfk:)dJ [S£"] Jh av&"J9)*s<H11dW.Z\ 8L^%q HmHQL@ʗ%|L()WLx~z 1 0)xRpVie`QS[}NWqZe%;)c4 z[omL*Gq\D0MS|P'`z/5H5PH1mHᄥ&ܮP^KVT2W]#wY˔(Y79)4B0ej!"JU E%q%xnMMOG[s$b0yGR4 .&09 ;Lz=4NX5Rlc/$g;$p&?LK`E`Z} \R EJ arċcJ+0s)؁1: Ů{rť/!ᄉ0= n0LSsRhi5 ?0- a c`ۆ0ջ)fݤXtRȔȍQ Eр HQ@.f opR&?'4n 4%-7 ~;i*f]s `HH9=ᄄJa=)~/jrF 1f EHM+0n@R'Nu`(SY%G?(2,U(2(e80N80N _h+09E H1")H#+ ;Eyp…0|N$L0T0xr`(2(2HW&G$0ezR &?)HDٚػ/8L@֯{8"k)j+|@d8Lsp>e'Ř2RSXG9Mm| @h ajR&-„P%R|V)Iq9I2iH\*_ N t,`Ky4L[L`ک8)MK0M_]L;aS>K<6-%|(|Y$a>&o 1)ZHq|,@äE0U%BS$]$HILcP eLR(^(b !L AT@ 5]Z<` PK89a䔄^軑p1)J//LK 1:CBmzT U\R%RTEiML`ʔ!:4HqŅH:ZÁriMLmt _H!"iXᜄPsnp@t #`-'$|%pp(fLwN~aC`"S`w DL`ƉEqMQƽ`Q6RT^C 7vRkIqC{k#`2R &59@Q:@Iy#)^จ@XG E)%1\0w)5F IpW+# U*O-@b4RZ tC9Ltrxm h`"E 5}4 Et?%.HO~pr)aHsRԒnfDw[gKGEK*HQ#a_hL#3_fR=(W#a)*SG:)09)w_wLFd Ǿr1R &'aZntz)H1oە>>& E)갥db)j.$ZR͎_HW5TVWeX#9.D֑0AѼCQL&arRwHq Oo8<)'1W"Nޥ$| z80)ZHQ,DBOX HV*z$%+"]>H@JQm0Y=1%*] Gq1R& Ej8Lb"t$z& E *I2N/'<D` 1<@Lh E`(=MyjIx8{SB`JILj+HQ] ?+R =Lppj8ZHqhu)z/Mu+Z8L.i*U L4LɷJ2êFM VG$R\R3[}}bBtDjRwHQ_@ 8KB)Hq~` f8Lq)WXH1LVٻ`kB$|&q%Rt Ec8 /R9iդڔTaѳZ}{: T A1`BK%8vZPR4F G{R ZjZ\ibM!Vb0ٕ>IqĄWLr;! IoݏDcX[R>KHQ6Z\$1hqZ}-ԈBtS Io4И@J'RN &"E")5 H G[p^|4L``Fs2);)~ek\1K0uL1a'T(5 _1jd"zI0Ic`:՗'GEꤸ)NÔdM/!?7?+K@r0AёR>D0Mo h@8L"i0)%0 ;L'$'RLD)qF˗$~JzK#/ߴHQJ>;"ISZ2.(%"~%5.hM Em%RtF Ge"Rt܍Iqn&#$6R\`"Ey?! `?b8LCTNH*im?eT.-Ӵ9)SL}zT+ߧ)jdIQƐbE\0)V6d{D GkBQ0R&h8)Hѱ)S`\s})k^鄗rI0 '$3F&_Ѵ)&9W}~O8I`4Z*iSqII0+M)Z'J][cDvRT HQ^<3億] ]8#锄#QӌMd|V_N-h4$kXfIn}oEdq#i23J[?1N[L:mוعb7mѹ錄~p< &| 0p0) 'F[,7gVИ0jNQR#:4IV*(: )B8v"[Nt>QF'J׵q_)S %%i("Eq {H,mv]+R}L"EDR2iމ20T{RU?p Ӹ`D#Icx+щ AM`ZVIQ sZ=ģ)D6޵ZE00MzqR.i+rքALq50! T0 OL^p@t `hX*i *o? VL{. t (U`*O@D #D0U`QBdJI-[M0!)]hI=awTLD L()WIH3 2$vR4@ LS`rRu,x"C _y'Xn a:| K< `^0EJH[꣈ZE$IUpI% (:!`w4wGmHQL@dL䤨@/bm3)ZH1]Hr &# /Ixe0)T>`s>bHaޓ&)_|#L<"p *)-LY0.IqE'R46(M2):V@t EZ鉌y`aW`Iq`NX[~` XNCS @x 7>E0{7[Rh"SjģL"TQYIhIclDzQ1)/@h)#i}!I_FLWP&>\Xn'$\@Hѵњ 8! >jL`J_IIJbESBxDRWDKXN loHTmMAˢM0k[ Ek) &+@_"j H#L ?%B vvR\_~O9 `;7-opN RRSƙx )R~[ Q)LqRə GHym+LS0HI+I!MS>D0M}^ 1"EBK럞Gc1 0sRyUA9N !G-SJ`N0Iz3Iye 2@HZ>HS);b0Yw7)09l0'$ LpN0)6iwMR E$HQlU<7-*⦸Ec@:Hݢ6)0EeR"im4Ibr_}LC< vf`"ǧvR&oX7Hcx#R4մbt_sZ=DçHQ $)*IW 3vEb)vlZY '`J)Q0 KR&'/09ϵ)*ހHbw*@ @q;\HrpV/Iql^i*[LG# nvEKƂ(WK 1 0 iLڼ:)S 夐)`j8)HqtDaQLUdJJ$ 2vR֍HQ&bLD(WarR Qb"Lk `jl8Lp4RH8[@R7 o9܀@r-FLZ}$E%$)*i)(!Yb/)~V\S)H}mpO&#aZSӸ$&#`2Rn, ~p$lLFạk /H؜&#_MS ִz)HQZ:m/'@ )CS!(`'ՂpS[0QOˤ)0nb#GLxc)LF]n_ZqoIM)L_ E%^ EgŹ`2R,c#`2R &5p):4Ņ N'2)-p &"Ek+}0 `-x?#5mT&nk7R&'a]oa*@K+1JV_J%RT ݼ0egl8L@黣肶 GHqmHqC5o&k)ZG#aWL '%ܱv-el\ px⤸n[Ԫ"8L'$/8cH gUܰi%MҲ`գq6uG)+T4"uOHHQ@b R0Š6)LS;q%Rt V*~`JLD `:U$\&+")."֦)A§C`]u-|X )yqttU|素h"R3L Ee#RB0 ! ( ESߤ GӚҚ@Jp_-4*9H[#G)ӵ `҄ \D 7HcuL[u%)8Zb+ъS")OJjfH2Mq r vG@V?pQ>i h80. LS `I+2.* ?NCЧ/Lr~+E sD)0)?ӴȔ,ϯty8a~)) 62ފ LLN ԛzvFz)7-0eTBdJf8IN T;0Edϊ}u'˵{\j L/:ʨ3R:keFݏ[ٵ)~e5 U߀/Ż-0O&;'\2{`rR0|8)SiU8;''] I!2.pR{ `{7}`Vpcf]=H1}4 a`:v FhImffI-7lU<E!#D|)SbRw'`}+jlYeڇ'w:#At ^'DH$c`.ib?) sZ4+aES'[murb_cTRr@HA4+WuƮ@)*|) U%&N _{ʵ&⟒0~~b}z7cKFE~Yb(H1vL|r/I ` f~L})2)`H O>c)a>cHIk6ejU6"K:(wGwGxtCb0pr2Y_x{ o|0[ ^GLz{> skr38E0cIhZNV_Djv Ezo vG-ؒv 59_&:#NWLkj8L@L`Ҷ^n~(b0)*#tnZ}tJIx8zSBPrIL AFg&x¦0yg8F헟²SX HQSX(WaR4Ƌ|F“8a*Ӊ B x /H1izHQ`:G/O |J6?n|O"Eډ=vG3"M>Kh8H#]HQߠ#6qGl#\H1 ᳞a8L@R e|.kD`zÅ\.L_"]8܍&)eR^N,x`apR̉KI0QG;<]H>)0)QS0ߚW5 >~k·s>~b0 EGoLi'sNEQR}jUre"mgFDWHѵZ1NX9@UZ /QJb;$V A5 O&\8WػHӚ»<H]3O@)joaZx7GHQsPV_tU|(N8L- ى=HѸ)쎺'*GZ1 HQp8)0=&#`_* >3a1+X\0)Hk„0K1LWZwA9)S&i@>ֳZ}xLSMRRF a*)xx/d'R El8L;@b"RH Ej a0)H1LS`ꛠOC[n$< `tyH)OH0pyOhZ@bHQR4VώHWR$RԪYgŭ 2RvG ͚GL@4Hё@.0=RS3Rǵ `C)0 Rt[0Q[cRB3V ER)E JxaqD.{`RԛRLI `:GѧG-a$mђ&ɀF%-S 1`RHQwh8H.CLy`)ipV"EW3aZۧ~pR)h `Zun| `RHQ&7\H `j;W}~FmEdXp\ahM;m1Fd06J[CvdhBA[l?h?E=&h6%$hds>h5i[V9FE݂'VL[/y$mG+m1EwzD,Q~Mh@\tM:%㜄a|E_xML>NIcx{6N[{q~w#OhΙW'I,g /q 0I mk:m=+Q"mVqūVk ]+/S\Zъ]-7їTp>6.x-% 3K^XH~I§ýA[ _Ѵ$c8 WM*"2e L12BqEoESLD#ڠ(@JuAIԶ)]>y`₇u WNH`O:7"EsM+FH`폹pR $wNS:HQ D d)l47iHq;)h))}H1)p4L  >'I U߾K'Ù@ 4ڱiӵ`J)Q0S(DjIQو( `mIRT&"E ae.Aw\n@lL!i* &'`:RR@O%< &#`JxsaiL\Ӛ@ L?+Q$EI{"N;^h%L Ee#R R) HQ)j+=E 8LT.@IlP!.#4|6W\F<7'لHcu `:i9)j|8xD H!J{2vR ǤLm4k*f OLN t[0+>tB0ME0d 8H1] Bw+>um!sai+H)SCS}myJHoHZ^V 7LmALN t0իK::<@dLNb09)7LhTp>`q s LF+dj{C #t LY-B֪8Ic4 6`ڊLQLG `z+4.Fn.8)n.8)|( aц v|O8"FkIc`rRqpqRBxɪMdJJӒ{Rw 3f9)vG HQp &'E ar :N)8 E8LmqRBr;!S1I%30y^k~M/,pLHIUk^6 $E:)SS{d]Fj8~#&pUF#+(N ofLӟpr c? aԀFM,@jR&xNGxT|%Z%q^ɇE?}R0{0=znkK]y2):nG0]U5w P[= '!i*0'1V &,Đz%j'RHQ4V*HJH13$ HQ !ΊEV0-)lpAqŅHњpV:mLF_U%0)ՒWLxa8&kn ;7"EhV )Һ)T) Q(J!)FLF"i+d8x7mH{<18L𥆆'2)- &"Ek3 & EL$ FuG `~JS `2Rqp?0] eS*HQ6 Ee& Eqஈ0Ek`2R\][dxM>HS)2R)ZG EB.X0'\8%0= o\9p>M.!Hqݶ"UQ1iRѢZESm0/&#. :#`rRܢ `IE[8L/_& E}RT&"E|1,(ɂNa:3a'-rRܢktOH /ah\0)dEc`*ybwx{/RȔyOL/ & EK)ZHQ[]H1H):&d28L@r&#aJ#DyT0g$&#`2R$&5& E)z=gg)70lU<E)R/Z)*iۚAO֫HHQ.fL-j8ZӶ5Zhy/&lc⵭DhmןHQ߁ضu mk0)#n|_$<<a`rR׶>ݤF(msZIQ%RkdLF$:)Z5MVۋ `W, `Ueb0qLx6@HqAZ!#g&|7˲ QMKSZ 8[EjR4&HG$R+iw3`w8(MH֡(&j8z$4HULFDŅH:zq`Iq _04L[,1?(ap9ԫE0=V4\-%SZ=S£7>Q0**9MBhIXOIWvG 09)曨OV7R &'a6LM (.D֤IqOXyJ9G)jW,ZSx_Nxk%cxeܻ%E7*nU8.T"E 1).6 Ee+Fx1`US]oaR4 pT+&H'ymw`*F8Lnô1Tm%Rt.}wF&&vq:-a>CS[L L))isZ=ģT4,HQT&#ϊ&8L=0.a8Lpt?8Lpt[IqAWE_|4L`S LɄHQw't?L RHԴ,-#4- EJ"iެ_QTA$RaVLDh5 c EV0G)> `R4z(.v4)ӑHq@Tp0A$|DŽ5qtSv?! /?u 0)A())KV_IQS&i"Fd$RT;uL@"i8):B_#o@ʴuL0)ZJ&HrRHֱFn( /3vN8kE0aǰFAS"&ehQģ7-h,cK1, '4g_&|ǵa'|t *S|MwNcx1eoZ%)c$5*xL`*$YȔhA0({f)2un;4ߑ"L7j8`Zt]L #~pH\#;]תӵ~t^B4֗\AmH)Sfީi)hA.VO(= 'ES4Q)L͔ԫ2LC`jb Rm9(W ErULDyWbޕ"Lkjx)*"M$)龆<,`ھ0/8")S7-M)"`*RZD"Jh `*)AƽJýRށi+~eR߀i*6(ە R߁@e WHc8SHa&)IY)FIp/5HWE"R1.iA,i+)ImR(uOgMH0/Lk `W_ 5i*}4 <W!00*IQԴbR,roQESWJȐb_OsgX@h E)HQ[R!Lb09):RK;)Ӽ``k^^ C[R &'`:v/K ^8{!Zm$e)[[}R?k4`*USHLY`ZtR^_%E}R}\n""鶼{KR&_`x)* `ڻ &/֚ 7&z&qR&_b) 09)"-#5D2@'Z9# yT*O-bR&_.Fh@쎪H.hށ;}V ER) )>0Jhm@rł9$U%OHcxZkE0mM/Lc㷴zGEJLqRL…HqLx^ LNK@NHё@ 5VHQ߀Hq ^z#i};)0؄ӏa'`zI,@*8*yNgGxD ]#RvR E0=µѝsR(޺wA'`zL@]w&)0}R`?p ^9sIҗ㄂!a(̄L[$6͜{Ӵ)wViF WHQ aW{@:Hq\9&zɷW m:pt h8@ ZXGR_)vaL_oթ)/[x/HQy)ךVt )zVG V@@_@W 3vhHq);Pњ@J GS)b04W"EkR&@ДOI u `2h&_ctcطA;)SpnrRo~ģO%RT{&G(@g.)^Q.[)u>i 9)">ALlD#\ɉwJpoL`ں9)#OH5 /#9pYgx1Vw*OL `)&)etRL+0fR܀Hq\)0f}R&lH15Ӆv8a׌ &&> ǰqR &GkVHqgzG#a^^Pz67IQqQ(9% ٍ HѸ؜V0"Hњp> H2)3cdLv*i LH*ai\E0z6'& 3MQ04#`"V!"S*8[-Iz &"ER4F (&"E4R &k84 HQ0wLFd8"4Tc—V hopyOlZN6,@b4|[~m@*O"%V^t+;j#`aRuh8H#+5]Hyӑ0)f ^n&tvS Lduսc7Hcx}U7-= $ȔSZ}⨅*S ڀ)Mn?+RT6"E)Nhf E 4= 兄H>G `jWx#`2_k)j+ko`Z','őpC5OscXSL`zrZ7NԪOzk(S8IAFEq&#_&#_!)Wx07!LFp7Rũ`x0&#`@nk>_%< `2R &#/G 1 HQ HQr)!$NRNC JHQ0lx@`BS"Џ< '4ŅH: &#a2R &# ) 5C0=q~p@ Hy 8L$Lt1Lhz.~7Mku 7%C)>1*0m@"dH1H=vG Gj aR :bMT}z~Ί_0):=v8aZ]&NPÓi(J`uńۧ ("EC)jܴ)InόD)*餸ŰutJGC)jLU6"EGzq8LzNôuL1I#0k~8)Ą"5)*kot_VhL<7 >\M7-,s$<^U ާ)jHQIW'3,x+2& LptܭLF G}7R& E{Rj8ZHQ[#NäadW.p}p.Hq{$|1 lDsMv)Y0>7JEjHIU2&R+ Ͷ;:Lp4<Hm1R &j8Qu)H1?X HQ0R& E&NxɄ;'le0n ;L >R lDFMH)LꑔLSHQ4'aw㞐F7 E1)vG GHQ߀ G8LmdX;)-)z00)ʃ.ʇtR-)Z09 L[?!cxvRE0qrIqZ`*I)DJ)MSJ1L Ee#R4D0 ! )0)2Qњ*-7#aں$ի0$`R"EIc"HQRBNC@*O)B/lh딎D"E0)\'PQ/Ch;)]ӁODÇHb"EG?D0M~`NiɅ]8HQY!5S8v`Je "tIJVwbIڂlcG{}K__VƓ ֝'F[H7L[,;F[rBN EmcΈ%0 w#NIo`:M`\[W4e?:3aΕ о<=)[%t6*[%|1 )xrs:6DΈHAƨSdIJ'TAo2gDgZ&"<`ڀ?&^mH V`x CE}) '< 0.X) `§y q?ݴd)5F$`j?hdRH$qeLT D^U;7"~`aP\]+AL0)u܁+(@ILxqT%K5HQ{2K$Ѐ#,$[FL `Ax9$]HQ߀LCuB0]B3(`ںXPH!󌄏LM_Ip@qZ겡-I2KQc E%(*%iIYN ԫ "f__I2Ώ8)֧LEe09)жV[I oĄ v|5&턄O9{'`ʟmZ v$R>٥GۜԆ0%N*i[^Gwi}(:=B-& `zv}Ӿ~Α@䫫|4 ER) ~K 4t7'!IxeO<z),@j!)~Szg"L-0Wz\t!IqvGf EqR &o8hHqG qt LMuLV¾¼sEۉ<)'6^յYEI ZL/JBP E1!`Q[$La@BhMh8W\}e*@ &' a5*]$To]SL_V' ~2`7 t oD )1ȗ-DJ4Sq)u:7 E1)v `Q}*TdLs@hx)*8Lh\K) `*|1'|1 0EMGhIZ<e).=HQUۮ^#0y)vG4)wh8k5 Ů{~@ H1^@Sqʜi\1˟pLHO^X~{1\uRq䟃V'e0w F)L]>;)oϊk ON 4L[ͭ~; da `*mN mnY 0_0HI!6 t LNE>tO)*J?\B0Նť;@qLKcasRS:)whCG)j$rxƾg< b0*[R$쎊 5 pt h82)-@hmP@LHq2 {j+&|B0M4. ;]{ '^nZG"YKGSGmQ)J`!ȸv'Iqb "' 2b "#Htb81o!V_T̈1R'`W. `ՉqR;1N _CԸkR &#ar&|S4Pc؉q&Vt"EY/u?h@-ꄄe\Ьe3¯LIGuh8 `"BzY?C ^GbpR &"HxMNH1-iS> ? '%Oh/6kOyԟ;btRf)F22''e^nL>n:)%0ͻ`:,|)Ν0Ci}R"Ekv{\`N „mR VNIx9&u@;ݓ/)HqBKۤR%gwyDW E)8/!Hqm\o`ܐ0);2#`R#`Z\O$l!pLV0 `1T>0RÔ`jU`JILc$GI"E@"9 YޑMq ":dR'F8LIHqhޑm|"ix `2R^#`rC޵ `U⍹b7~@Dx[_K0@à)V8LAF."-$RA7OD)zq]3R zb0Yqh8/#lskﶹ{u'aqd8L U")&kB$d~fYfc._0aI.SSJ)t4#8ᷴ@j'R El8L@ b"RH#`2R&h8BU/HLFʫ#` &|lBRu,x2?H *q]&&䝏a `GLԫMSO)R)j'q<щ l8L;jzkX+dX:L@bXLiLD.hYaS ?0 _+<^(خ:q9#7&i h8gOq!R&q}xWr3R& E[CG)jb‡])9)&Mp\esRvIq摚VI\9nQS}ѪDZTx xAƭ-L@Fb0QGRh8*S^a0) 5aW&'aC`2W pprM#WLMFN5 NLF4iu xI雌tR"S(!YV0e `cc0Yqhޝiz S&#`Rt`MžZ+[e„H KM8N7 `2R} v oDvJR#,NÔ.#ZIHQ4IIJG)H vG O Ee(pt `R)zBh|)=)e‚s~`„?Z6W,@oypf "T;5-"EYjIZی< h(c"E}IQDA0+M)0mTθ))o+LiۀDu JzPy\?5  ^o 3% '$|1)ʥih;b%ƽƓo 'ϼ$sخF[mk1=h'gǿ'I[\.O+mqIa$ ~sB c$-Jx/X|npI;6&=hd uIhhm'։'Bk98?+l1/|ǰI0.ˮDWNq/1Gb <rpkb\u?:s'6vƹNrͷTr"L-{u^v"EӳLׄ{ܠܴ5x6p @dD.s)"1W2Tw" .;wCԧĄWLZg(x`zbm7ݠ[K'"S )wi}Q"%B0e("`zAƹH`ZRe+@z).j[7i Epx[T&"`ZGŕHu iO"i*S.'$ܨ`ME%%(x~B{R0enq1LhI EStLC(H!RjշPrI)Q<)'MTrRTLb5HU0*i AJYyR+P+vRV"ERGJ '߀EpE0 !mqyϔVK뻍,@jg)ZF ae{RT;z@t8u:8Lz% Hq|}Ca 0OO?vNx"R&>Jo"8 >kCzJv1\`zq&hUSI\';1|M'`GL%vLxCR09)R)/ӓ+b09);29ŅHњ`ƕHǒi)Ƃ'"E|w`VL)n2N?_"=H8)/B*? IT焧֟)ʅ]hI `Q)۫Znp &'EHQ\ HQH1).&)WLd#}>!50U50-7Lpv `:^l.\K `*ϟt=ae5ZЗT2hځV E -$*)nH4T0 ϗr@Hq|AK)w `rRwZP(&"Q[O%b  wŏI&c^E'R&="%R+,6F-^K3j'R `2R&fI E8Lzc09),/0 ˽`rR&)WITց8L&%L/hg^Kth2}"aI!nE2 `'61`D)Х J"E-<) 8Ll3?+5Fh^s#8LRw 嵵b09)S>D}z I `c'`wLU/'}\OHxk@ʏ.,s/fLܴ,')VMG ar[H1 (:ge(@GTLpt$4@i9)ӵ{:)&LuBU0*~` 8LDLvv9#0umpL`rR\.np#)(NK2xnb0`HQ@Wg!Q6ڇ0]<C0 OoNIlLf(6+I!.!'|$z%Rtm1'Hq$iHLo.>_<) 䦕BSI$Cvd)$R~4;ΐe@ b R{.ֵߓ''`""i0 >M4LN;[倂OIaH;b0Y'%| E2N?!_jZhI!S20EDRLFg:L;v0moF?uRv:Lfn/7ԧ>9)ӑ)^0C3)l1k'`b>a)W8If gR&f|?v.lܴFLv V 7)*RA/DwR(! ;lDH-nP_qRvԸtR &#`V#If)z$v5R8&葫LO E .#[/i|`ڊe BRLܴԃ# rmփYmHQIz!)S0 ii+]](30K+Y'"E>u )H@jǦHQmHJKV "d(!0YV0e `rRL!0i`|4L t1RL `rRL50)b,7(H1IyIbBLF PPVpM(-`BLÛ0w6e!Rj3H"E)*tv,&x\?u ]3?ߴȔ,jId;==)) qeAƙ?+MO,ث 3;)SHq4".70):HQ)6Zk3֦6b-Ly` %9*ם ڱ`"E72T1}! ^'&_~wLDb"RԱiE)J$%iXh7bM[ʓ,7<]h g 0X;mq} ?# E0MKHQDJI`#J)dJJ1R:qE0-!8iawT<m HQ}S `mkMi*@: `*+"]`iU&"ELL.i*Tֱ鄄ۇkULd(tN7ށLuHK`R)to0R2 x AƱ2T6C(&@ JpR `-L;by)SKZHѸ|RF|V{t [v[9 #ԦDsh@ M_:ɬd(2n)IfIe+@z)/5 %o@) `U%0]0p)i䄹T8)0q"=)SvL| kII̧4-"EY 7DjR &K26R,c#2$=  E: ~<1np&4 EH1|2)p#\IѼ)) D)SLNHH^ߑ0`ZF:SL7hhJS2()*9E0EqKQ]YV^W( az34_Ɨ)Ч?\E0]~7Ln(l`()WP1 &&Ln&|Zxd1Zƫ%"S1 7YhE%gRqdMqi8*m/9~L ]Sm]SoN LS``-/9 LN.h)tnN |XSS[0ǤLqp/9Ii*01lo9ܚ ark8EPK`]"byYm[6q%R6 E)yR &3ԯly qR&Y5q儷8L^à Ƅ9 *a=8)+9ar&j!)`JmOuZE0"Q$)j 8L-I+Hր=-Ǐ7u}zxyb0)B0}4L:n`"EkRm%Rt sRxoK8v?m"ۆ0e%)b0Aël1048)q]&m+;R>lu)z\%q8IzNL4@w"P05\ZGJ89 <7&'&줼:ݞ"nEo|4L^jZ(F E"Ѭe$Vize<)g &'EL~9BQBqo߁? (.DR &'ERܠ`O (&"EĄ? )J2pxǧL,aͣ^C9)*HqX7ERm"Ҧ>IQm6@`So3}gf E: b0m Rt$4U)&W1Z0!i*;0RLNdLk' LR &Ka48#a"E[R&?TpRe \)LsT#yLiU7%x% &9eR&Oa;2Lwpt܁j}vNaLֹn X0իeSPw cG:)P9V ;)0iHL| +5IqRS|$'sVtR,9)ST21)I]!3 pT&"Eh\L̼)҄o]j\=H!wAj |NDqqwOJ0w@_]õ L-i] Iو&*9%V3R#vGW ںr8|@Ѱ) }ך0)GRN(&#`BS0Qyؕ_HiL5F `:)aYWL>\nNmƔtJ"jR}ߑxͭVL'W"(9׻ld0U;wSO9LHQ>HS)g Eq0Wm,t [b5v m$:)N0U a'IyT~*a)v/AÙq@b&R4};]Dw4Էҋ%E!)fab/ZdθA/)CA5}4h82 E{7RHLC/LS4 {HqU _BS0P )bZ8Q013N%7'_"6Vgt Ҫ$N= "z460)Zf E HQzq\d뤘VieLNO Elj'ŧvR||pw^OvR(Ev'arR\zE`Jm"R07HKJU E)m>'ZR[1R~ S{`~7AI?To)gm H#`2R&ٽ0R` EHQNH^|&S|0gRTwI1<۽lHyߴ~Li![HQY0E$r-mɈ\& Ea2 & Ee[)W/Ɉ2 H֍(Ɉr(F*#4(N"` )*7"Eac˾r1R& EcQ@Jji(9۔)vcX~g8C`ͥG@pq ʅHњ@*z)0)zNK K iLD>;qNHx2C& Eq!Rf'G&Gyn?n"`M+)7[JPrZ1 қb"xaeQsR;څ4[@H. G{R"EkҚfþ `2R50FJi*P]$zZ%+"]HѼ)xs/~c9{ >\]HQV EeZHQDkwM;"-"ȸ^终UK.i HQ\NL먷0)*#6+(@40(IqdLFWGLHQD<| ׷$|VDQ*3 Ex:Ei8)nq,Z2ES6Le1zTwRG0 434Hѵ)S ]|܍ vdX6Rsƒ& EkړP^zb7{cX1 @M+*-z LjDL!HcFcfD(R܁vGRԶD0E4\`Rt?.i*N):50U\&*'$]55'$ Dܚ4I ,5E0)j#u"E-IbY`D6$yeMzǷ'iy *x$J7K).RJk׿*&E/W﯉x^wMMuIpX \?$}*]H;&;77ָgKDiI.A$6ԀĿdUG-;mrF mmqmhi6w&Ŏ;MbOǂHbOnr-IT6Ywڢƍ3`ጦ[Ē8I4"hddI2fآd͹ߺ3x$q0JBM.% /¹x+<+l7.xoD _\~>yV*~ع@ QQ8~>ǍԨlZ+6IZB8ƿըK.U(ʸ)tBƠ@J6 1QT&J@V`".DV,x\*8.Zn@Jj_pi>Z`iM}Eˬ)x3OO>E0-^%~wJ]ZUk(eR\R'R HUS |CHQtwip0)#B0]|H`꛴CR\IH5tJD ]i\<~‡!0S@j)Z`"SZ`*]:?tSBR$0*%"u `*!w.@ t$IS@ˋ 0!i*&.Iށ`rR>]+j02/zt$z) )w `O1)H0eT+0e+껍R-HQq)fQK`] mj~@1gq!Rg 8L|R`# D>D0Ma 9) 0ݖNH0*O/PrR)ԞcXSKzWMSa6!)բo7"UHt/y,D&> E|@܀/)).hd-7_!I &'`:R09)Zn@h@b‚ }|*F _B0:JL@{t+| `*6VE0-A+() wi}Q*䒖'Lі  y-Zz* Hyscb=Q2Zm+S4<09)"ńyF )S߹B2 ~$XL E!`ZVVEdJLqw%U)Y- )q/|ʹLO)o{L!w_I)h[Cӵ:)ThaLBxO8|C $ސ "M+o$ ?&`*r"G E)~$0Y-Zh@$S<aQ7e,DFk Lp~xݣ%Z)tM`R4߀IցD@d;LnR`pF 0I!K_IcXq{qRܥ=GYˆ`J 2I `xy h uuFm6^|PI0=L^0t &#`6/I%OJx1^imrիsR"_~4109) &_ctLjZJ"E-`EpD^0*ȸtL*)f)|;k8#{eoGoap)_1߻1b0#H9#>30Ր1Hqƈ!ur• ^wvF괟8LQ7hie6LYKێ 9L@j)O<; .~xH a x~84HQL@ `rR&x*@} 0IA8)zqG+| L}ޜ)u Ko1)ZHq|,@#Kz$b0K40K)J$pR!e0)]%.驘N 7~U0K =N T&Hy~ L}タ&='`:1) (f ab$R\. GWjuR\!x^ 8LL)Zf#ed 餘Bp6h8~ `rR`rRf )J9@4RzR)s *~ 2{) ycT:ĦUi`(_$.W[ W- [uIz{Vi>P5LkRkWLp#xdYL# v~dY0_ &1W9 ?HQN Tnp oHј@J'RشJRTt\R8#lpUe5 & npAiI34>kᨭDH1 NR`"EG)FaLy`₇A%"EhLA4F c)DMLܴFoZ-wi}T+ۯ l wm+\VK[@Q߁ƌuaLpt? H0X;Pw+`&+4|7N؊@bn8!m7[|GZ_:RT|RR2xvt w*ȸ"&#opi3b"RH`2&h8, H𤛺D-Jhmv"0qtSw8L4 _0O .icx R0cqR&`m'%.}QK14, &a & Ee_)%4j8LpukaGZ >m aRu 0T}RT&#a7 }㄄P?C XqNuǟSBS_<D0RƟ1ESܥ3!0 x b)40RII(7 `zL@4z sRT&"EG)(F"iX$LF>~5/V'& _icx3^l8LܴIDKХ%U@mQLj)ʸx<Gt.`‡R"E+>s9O:L@$4OLw#`>0QR^yӚPs_H (IqÏ'0)y~ EOD9)<>HyZ/{=])) n8L(lv+@HQv0Aށ4uOU%0;-'P"ib^8LC߁^a9a;%ua˅O'wGrZ} l9X%%SvFK Qm,J%)2 `!)/mp{A֡}+ HQF GHqhB ŕHڀ_HѼ[-8d8L&R4$~ ^&"EsRH:, z*Q]hD0gd\fL /1cRO͚}U7\p4ߜ_P>x7_}lWo E}RT&#aŵBhIqz,Z0RT"E=_-m7 rNR)j\kBqE0-!8vG-p{"i~ԝ. G DH'arRmL()W(I+[w0)ZHѸZ$09)xW^\Z|v9>M]ш%\K"j8L%E/!8Qs4loqO[.ܤ0)j+"]+4 ݻd0):NWLkӛ0)껓WT0]0Mô""i(@t+@ݯ4Mj1@ Դ8)S4%U:)S XDEqKL@E LrxO][hM EhxX0AmGhmFwA6R}h@bR[?k5Rɗ$ mwK N W+leWJbpmq- :nh~^?hF6H.oí#+l%=7(¦wh7I$&M) L8oMוcT6Lʻ D0ݲhi@h p &= &Rx7=e M~7A Tˏ.vN)#"iq4čļwNs:uĝ7WEvJ`'iGȣW9O9=i ggvN%m-@ qK䤨@9L^c'„&[ L[ Eu'|M 7LsVL%C[ڸEi%)HQjp 07}K?0JRw"N%w3V+L-{Q'OJg$>VdI!")LCL[m'TNx+@ʛN鸅1#'⼿ 't!Ia 'x"Nމ8YS*OTpgOp0:^I_=az &48L^/މmO<1H[/m׷0v)Jpp ?^8(jP)eZxv|I;8_Mn\ t0@ĕT:xx=6_•^f%.p`R$t0NP0W:9J{*dTu84bU_ =UU*Vu۽Wur^[5vU7'Ey RI rU7 7ycU EyI_խnV3W&R4f` sD(tRS^+7~pitUbA>sVwL[·s L8uR^): UٰdVuRN[x^Z$rux^`N{C'`W{C-#nm7u7|o #ei:&w$5 e]L*?7RR_0)- `{L@ {R4&'E'p &=\iR &ar>H0L^M:'& aNn&R'Li&S“T<$)'GI@oRH䯝HQ_W 09)>mkv@ H1|q8LV(8KIQ|W ;)S|Vx-|k^{#"Y!)|;tZ]Ӟ!8BN tLs6qRW 48B>L|1c)b933\Oؙ9Hyڙ*$BȘ1>)) ) @_3?E GiH0hm@.& Ea8L &ܖ0co&͒wl*;L@-,lT6a_ZZ+ṫcCp HcRTFfI.)kvM{*UdJHbFLSE'a:}-NHѸ),/"E0R &(& EqD0)@Ҁ)+7l- OZ"+%2T!zYJIcTCKژBSZ.&?[aK:IukNôOPIq,Hqƕʑ!Q `'PH1~ &J&:o0)1u0oߤ*Y5QbVLD)JGpH E%Lk4XxHTkzqD0͕jM,閩t[u) T; Hq|[a" 9 nr} HQ^_\x ?u,<' Ej`:z GG)*]- H -F8LsKͽ0&#`~ /Iq]W'a- (DZHkLz 7"E[&-"EH}"'M `R44`& ESXwtz$K5ReHѴÄ4(LCLFôOyHqu4Rqe4N[xLFôL!֐30USg&h:l9 v}!ځcx*D[*V+b QۂU"GDGz&Aē }tIX8.&͂x ) {w28 ]Ǎ>q+SNÐMR[>&}8׸~ FS^cDD"'E3YS)))ir0F`Zj @-{%溎DJ&<L]4J8SG<H t/jɔtݷ[x)oeWj D G+ 1Gh`꣔o|YJE}I+B0ϢLcV&6HLuOg2uqL1! `j }4HCR0My8T7a0u'-L Ls>Q L,iډ-HQY;_#"HD B%02fH!,R1 `*A+)Ӟ0MҐayzڥ<)j+b09)#V`ڊH/ H7,i<-L( D J8HB;廃k1HQ)o|keQHQ TkפL%D0$)tTٳh"UR<$InHѸh &'E[R|]me.H^lD[-LCp#$;פL<n@ ESQHr/Ȩm "E}&Hh@]qImi= 0C!D,M" L6LN V(8.ׁ8aLGL{-q Lt˛G)ґcXHQjD \)HoNRg"`^Sj^wL(;SBӐt9~`rR EeC)a5\P)ٔ0'HQ@ &R Hqx)>luKLcTN 3_)?r=+/E `: TQޠcUɻ)Mp)7"EsP:)J# Hc E2ppڷ(otѠR nj-|pѠgGE g\yA TB @Rj]ӟ=r=Pj_0p1ʫ`JբT8E0mժ9)Tl/u { l@5LN-liLf){j9@G6\x S"Z{qfgt-S *RX1k'`-)Lut.7,)Q!~[03s9)V&/_0)_ 8L.g#0sBq)Z*\L<#0yI?+\x)ҐHq}R4 -|Ä>ar\4lD H1xp9) ÍHQ a;DJjg4>yE ]ΔIQ_9)t؀5>x1K vqAi賐3RR&'-@@hp ;L:Ig#%[B'`5a9)OW3oU $%OR3yoH1Js &#Ǝ0uK\| 9?fXfX!VOId0AŽ!$VHary&na=;j4F &\6LGpQt@ăI1, HqxZ%[vMvO3ޥ"L7N Fzj `rR`tP)\uR'yTNx HQ,i2N~[f09)]gl!Q~D'\N a70~ )&5=7j )J/?}{ 򮧋`Z&\cxL@j)R3. `o5= T) (t Z1ڊv:Lk$@tTIs\ HD a+}vAh+N-,N&/S3*oH҈<05)fa$h<`T=-:R)0IS?[rN)fX fXu)/L!vB5 W}Y%#`zC ;)v0]4hE%L{pT Ei$R4eV $%잎D0^cTC .;:}0围Rw{dj Ee GCE:HVfXi@氧HqM9L?rQi\0)vpЭ`*3NăKi1<^ᖀERFV3_Ԯi"EK}`JM⺉FtR"E-쑩D G1Kp`n60g7+0)0M\0AKRq + gm|ƙ>kuR=):mpt% HQҘ*gvM6TE" pHq`J# کeLltg@Z33fL,tk3R &"EsM %< q gj UY# `jY(7u8cRTHq*Q Bg|60!HQM.}"VA+-7QD@h(r1 &R4LwLxk ds0CgLm q%R)+ cx"R4A]l$JQB1"bl=DAwD7Os ֭k nClK I:lߐ'?/n9ޑol s ox\٧1 sl8HI[Ri'RtLOl`#)ynă֐б-A":Ę:~{ G>`r/EHQ-:JA,פR :uxMR EuA~8x H=l3a;H#Liy)Ґu Kpq)*&|pѠxP Lkm,)a=G% E}""QR:bֹ♪eãR#R(R]HQ봓R:@ !їF"`:BSD0mUH!,uO E>& `ʛ&oᘁ:T 0uo1KR>?e߾{:~D)D.")",ԵIZE{]`*+}A^[ fL,iiy:t!&I$ t"${zN-@7rBm&hx6Xy )5)S"Bt5]3|EVDGsx{(KGE|[st5= 8v>- `j ÖwL.<09)Խ)ć -WPtn2Nt`|[<]z=0g /tAQ$ E%HJ`ZSGXi9.Do4<8s`%): `2R&O8 D.b0)09)j \4x8^95F#x )rE ES t$]D/>Q\R "Ec .>Z!imҞcl\_e #"9k{C}}Tg/L.*0`Ϋw{T l" LN /JF<:5%}1@RXZ0%"F"S" `*a`^N(٦)zV%WMk7v9EĵDul_0-&l_ӏu{ E:LFg#) 8$"LDDcHQ@L g>)2O(@?V)ER&?SR;#N nv\&[8L~^g EiR^09)SC9)S`ڊ',q *}&iԥ9@N &}1 ''`*!IeRD0q|vO;)&!LQ/0ESD˶`UZ W|Z̰wUe HҀ= 8L~O@ks&m0ymV$I!J޻N-'`B0gc1Lh@t09)*U}? H:{FjDF'RK%,'N}`ړ-p8nӞt[}uR@L.Q<i@BHq̲r@M3NGOz3n- HQ`۬-@8LNIބ1 a h0V@#arR\"a?utwMKRa4a'%!%L#LwvM$ EMꑀRfgGz_R?m9L뾔&)䐀=;u9d Em1 &2Th6LfD 3&<=]mEi`g33 7R|<{ƙ&gG~^HRW,wM""E |Hј $75 Nb@m& ]! `Zo~>f[yHQ[!)dp4$G<8)Ӵ`D]:&ͳ `*=7/p(Dlm0)3_֮i'_,H$&#_H6BpӞM1au@  &Iqt7WWOם}8N>$h@v;K$9)Jv.t ))8L/l0_OUtj "5D05 `2R(=# 8 `J;$Q<8! Nw"E[Vq)7"Esoe0)&<$J'@Hy4UC0GOQBT30vҠtb9vRqѲRIm`:);E0`8LFDh'arR&OK:LiH$k$ih'a uiĒGNL٨Mj- `2R|cLjPRUEgt)T `7iDd4Q~ fW{dCAE ґB7)( 48]6Ls&KU0O): `2C8u]5lɴSNA-tuGє)/j`*J)R@Ue5-nNä<`ZmG.@8LԖ+!z}%`UViG~h\hm@@h0TNh+@IqϢ[qRaHԥ\]R0է.b D8L3\%E\.>*"C(1"Ƥh HJu;ӑ$nFyaVmR0CӐ鳶[%"EpR8a.j,t^%Z`u%))]gF"EgQ0) 3_;?3 s9Emܒ "-i?3EϺ)Z8u{7iKRt@ols)g#aR4dO(<)zD7"E[WLCRLxK-Mc]0an1AoG)7F(5MYS=E&' ZzLDolձAS.ntorFN3b>(M'%<)ڌi0WHR"EG`"Rt_6΍HٚCXE0hD"&LDB ?bPD!) d } n ND ')@S+>u2=W~̑&{ HNLuOgN{)>o9D3k1qHgNo9=б!iH̙3+O9gN||K]z0ua5C13<]0Eoo'3:B/XGdVJ#) #a}iL^,@dLF P_R &8LNY'<Ry+֝Z~t w)Hhu$AѨ)c^`-kk`/J}(4[jjV)V,% ]eMڱKw=\)7៯%3|f aV2095cR$*Wif*2 {) hFh@bR&%h+7N:)ULF'L;d9Jh1L(*04+R\D 4~A $i /;Vcg0]KW 3fXV?#1W ar$nB#60v$RTg"E[w_T9xE Ei.]Oa@0m $0cf|T E @6e E)lRv#DEQtLi0_sR9'`eLC&)0ջ=Sטil0~{ ' E5 `?D$T)KF"E)KcG^SYE0Ӆq^GaA[v8)>0N 4y:fՅqRNx#$r\)Z ]CcxLB05HJ=SN d77Q0uR.aoAn~77e#Ru~.cH5)j++[\0lNQs)'6Tgs H1>H9}/0= !yR&_rERvM"2zNRS"pZDcg͂Q/`)pKi+[ |wGY-GkkG!DL'jKwA]:Yh[~c ?MIq`-%0Ogt )"EcTHdL3t$LNaPio(0R|.ݫ0S[ҍڀAp)w"E[H!B<L@aLk2BMR $%@잎D0^Nd8L@Hqp9ַ&# &= +mHQ<`UL'Fd ;LaHT #Lu K-H= ` z8L#a'!am`\0,H1) I0)~;;u)is.]p wG Ej HQvE{iJ"BO H'F5R`"ɺڲh@ґ7-M7DL &w̖k3R &"EsnՙHV(iwC&zR' H. .]ڵj< &@J)lV)DJ5H@rE&D E-g#k*g 7{y+p0b8)N)NtӼRwLx+x֐u4):N:l &"Ek.]i1<~g )rSe09^LjMQP%:()z Ei!R* U'aC)ґ73Hyͫj&hwT6<}tD\ `ZG(88L[FdL ;L$H13&|8N4$Ϟb-ESzp>5i Ң0AHDJlgt ))%Lq^`R\cwR&WV EZ hT+' HQ6ú0j EuPqώN#jOو+x\8 IڨKZN]D0Yŷ0ԈI3?Q Y^\uR|X0N0vґ0S0)+=W"EsV1_ΰ/CӐiAL'a:&Llݫ&# YS[u0QvT4D)JG.ݽq h22|pR&wX}/)5&"E]RTt&t^঺@5c閁fX fX-Hq8LG &J<0!J8LmױOpٌ銔sPbAХQ[0"t$0TL@JwR&EdxcH1쑩WHHeᦥڃf `^|E@F oC0nqR&4W s/jYKW.ԥg=cHQ _Zq|i7WRY0Q6LKTLel[ޥus)JuV8LTYr ~( n ުD g EmQ0 `YtpxB&R Ee."E0HQ9|DJ=)/'L#ICwTZ5 ^ G%i-`u[ v&a90)gwYv&L^ţl"%fJ.m.vpޱK/t[pgM9 $#)"}d )шE LDGNQ V"Eu%Ah] bDOYYۍ>]&&"E|Rom"n@bq#RԎ /kb7 F~zn`HJ^?HSϞb\bd눟u(a<:e8:bJlQO6f) Ln߯:Uso.) 5:o@c~">5NzGuxa>k[kKyb MNu%)͍>IvYAA /m{tF58uOJ8 3%|Y`xq;Ǔ.϶R[p.=hjb5iTLg"˥ ")P Z&wot'LC:"% DH)"y`RN#H3D]..74pK$F) H8~f '|EО) `ʓ4V ESE0I)^dkµT ` EӸ` [%ҧxD,R\m\CSK/YHQL_R%);\=<EƜ)$+LN 4rIQ[8a2TLNHQ̘-)]4LN"`ڳ)i1|nD^]LA_OħNP RBxuҀɋs(DZ)Y*)9u 0y7+Ō 2i Rt>v) R(#08)S4LG4p EjWSX-~Χ^D)mHqB3)G EH_0j̕jLe^ Ei `Z|I!,i-/'E0A!n𞭂0# `FL1=&<I 4$I`:)揯L[qpK;)S?. (w=ni|i`jIS-T?&O)X")Ň;L(HVӾWN -x|'`j! `ZGWI^S?b04"l;)04)fbN %|&W>ndj/T"EoIjM `Jkm `H:=1)]Z;ʎtdXSY/+;Lw4)~W6a &'E1)~V EiR &pcJo<Hq*D wi]l8{#0&O)J!?9)09E1)0)HQ^ ~2R5OH8)> `ʃONONO/''`CS'#vI&)0EӚ0Dc8-I0MYS/~rYn|uSrL9)5D05jrRɟe:Lt!'řS4LN T6LsS(߈=W ay8LVnPý1c)O0i$R;L )RKHQɻNiD[+Hx)Jӑ)."^0Yy-d=ɽW ini )ayq.0# `{p1\I4&LL `//L BB/F/@HqA)-.H!V#I }rAH9an iH B~X! QO0P^PK@jtRѡ?"HQTI V)7 JF 0]L6q-q!#`'Z-+"R4:ҖimriLua7FҰ7>.~c OF WH)EߏO,rЃERTH,x$L1 `2Rl}H1!iNu]LI$HqU"HA 84b09)l{vpART#`Z~b $)80j|*#C(1)^3 HZHњiS[ߙA6)%L(H1"'""L;"#RT&<O徸KqYS?6)J#],|*'v:("UKtARR:ڳL ƟZv"E h1;n! `80Mi)o"*)f}#ZaStaZZ™na0IuiJ *1鰂>l("^ 4)2;"E]( H8)J̦g}Զe8Ltb9L@fK>6O)HqЭItN kH@n):`w{5 &ݻ4t0[ޱK/HQn1lD^)Ә0*2%$Ցr=A()T@]T&'$1Rl{bcIJB'aLCd `w] :)ڊ6# Ei2Hѽ:)0U #ti'aZU_إ; Mc): \+F$ M?j)H<)4^'a.aRw"E])?TE[D΢UQ2 6Nv)J:ө/,a4=BN0 p%"'E%tRsإ8 `.[p9)A}홯fN|"(&1A%o%7e#Ckg=1>H܎ 1(in brDŽqv$/l<%n bzȦ 3w,IW tKw1B%'"A)'htb<*1Ve!b \s\'k@w+10D{ X^b7„=b'|1$I{]zqb^0w݂Oyd=Do2>G, Xd>vȦ89B^:JgJF0ϔ y)֑V󰎘zQ*"AT EST `jESYh@h"$ (. ES"nYi R &#EFhLs`2R404ڀH@Lh+i}ڹK{]:qnХ5THTCH  T*߀)HJgI{R H}NFO^W E1)0OES$H:)ao&<"$iH@P!IQvslVH.9L,iL@ $)Z޾q\(mFLSNN LHZa0y.Е]+ ȵHQb0)9 'Jxm^I1?H#̇zRӥwcx"N ˤS?HQ1)WY);*-D @H#DAh^0M\鼰e09)S[9$i2 .az LF 4$L-xLkTL3$Lie> `ʡϙ8)H6xO@ŤRw `2R &p0ys3LNU^ӏH1PIq<)9ՙHV֐=;*2"EkRT a~t O uR)̗CS$D|vOG"r/j1 `ZRԏ &'E |}.|Y ,)WQ Em,i R@H-O!aɓ7DH-Ob+@Hx{N \kէZkO2y ?0&3_շA?DLeK. P޺xP'`W.TR @ҐHq9n,agbځHq<)z|p6LN 4%?f0Mt1 Uϓ `0TL'JR? 8L^2耔LF $FB")(->Q:)/A WKNmH1 _09CU6 ax)iLN ԅ a*-8L^c"%D$*+߀-J V;*wD (/HZ`ʻB0 ISLF T6L*)f]9nH11a7OǔޠB0-I? &'`Z`aN u>9)q `*S"peRc[Rt@'u`xU!o`3h@wico8m-bibQ4|o'L/@rN  KRHњawbON̈́^UarRo T6LhځIҀv|_)  HqsNJ]zLSǰt:LQ0QdJd9L'`0OɕR"E](N kK4LT6L6V&-R4 Ey0Rb09)S[]'6lLkw'ҞKGa nli6)~f'jR0 N 4VL8 Rԅ0Es0uڜ)LCun0[>#{5ˈA>Hѽ)b0Ak0E'Hd4eoÝY;)SpgRD)S$M+ʝ0N" H40e h\Y,H1)Hp<$qw㤄f8LF 8ԥHQa.]]t/\OA'F YDIE)0uYS}ʢ.W sdk-ΰHqf_g'H[#d#`"R Eq)0I nˤ}RT6"Es[f8L@+pE C>`.|?Y{)m|LyGZrGh-Ƈ=%5>&#`]ydHQpT7L/> &ñ&'űr>+'űʛ `Yd@ŮǪI:^< p,E"RJ]ӟHA(ZHiPtG (FL]\'boh0t_h5PD8L!)zv@'|ݑLDHQ| RT LHQ[e* HـHQ *VV*s-)0#{( DRT6"E*#  wO]dL@ UXHN8L^7kŵ1\G9q L-WЏħuN7/|;(ɶs܉`MH{R HQ:^*՚w"E[#aRuv0QtR&'ay`:LiSPĴl".6u^=.LK1\/_9ɾc pwZPODR{jt]aVJIq;:L:8 `2R.0U0_j4$L):'arR: `2Rb0)=<'a:n)"|&HQtNtrS`RJ]ӟHA(_N'aҘ.;K%0UZt(NäudLF?rgL[Et09)ty>8Le^ H/fHQ)Ҟ= `=R,3ߏm%잎L00x%)S& E-HRe?ӀH.ml)ҐHQ|>Lq`H&Ϟzc^UL@Q10|(@HL]ӟHa"XCS7) LbzAŚ0nHѳ)H@6ĺ~)J)Zǭ`:Lp?w&2TN$DHQ^`: 0V F( @R"̭'ʙ(>A]-D&'Y 15C{-CCj{X{.M.Ck2DM ׿ qW 1`7is[{tqvZq l]O2%'eW'Lqwՙ8 [hw@C!׽@W/Ay<Ǖ:ʁIiJYq9b9NZd{xӐi-D3&|YiIQ&Ks Vq혃+ޛsEcZt,[_ZaKVق~ >QZgj O"Ɣ~0-)nSK» `1q[O@F`7L`Y-H:))LkS,i+]KÄ"w츛`)ԥ뢓t~N#"wgt")#!`Bj(D TVLkmR[L$HQوH!nY2F4$ `H@ eLFH4iiإ7Luc1H4eL9S;?f(m\CSg&`JO0UӞ0Ey1'`*`rRoD枾43:JW `jD 8L^bH@Z)ܥhґ4Հ)O"[ `/I6nUJRnHR4 E:v^B0  +2L*)f]o<`2R &'`Cb09)x 9UNx+)J5)Ӊ]:Wp1\iL>SNT8H "EQH҈'"4/LD0Jlx@b0 tf#oD0 5H?q/ ?HQ_L{z) &'`j!)oc"`t'/Yw ~IQ$h Hiʯ/I!*8)S::?^II#`e09)ӸB¯,H8]0YӖ0M#wijNܥ[@~)S_N \M E9")5)ӫMqA T0XyXKw)aR30yqR &'EkR= `'m8muR &'xl+}_I;$0KR&`InHy)u@)pUܥi| 8LYX|-ȽurRٽ3H5j`wV EiH@dLNR&wM;0#) a 88a-?E0I0c% j>ˣTL R T 4)(ƇB0luE/|{*fYM4Oo&BC0 &Bxd09)>St3ԓsR-+R|eˉO\)YKRHQށ1\BrRD0IS"SB8>"EQIʣoSi^Iو&EF=E'`:LCurWq0zR&y:p)|@w (ҾIcx@$>o@+H66RoBnKݿ0}H%ø793b09)w#%2 EKR&xYS[0vRLV^'`z Hh0> <)SjT3|2psHp_ HQ '`*YN b)4Z/Q\Ӆb0)|Hj!S)onm첑rS;NTL{t6Lyݺ]nO=HqoIL<#6R & &>)׻%iqS7b߳< (DbPV{wabO1V~J[\%/!zA)S܉` )iOQS:L-: `'0-M0!l"*ax@Om+za#F3޳1\N#a`xYߋObT E4Q0}sQTV8˖p#'1 `ʃ0=B0Omc<47,aFFcħM\鰞 7#a2R &1Kj"@i8L@JE^BߊOB o)໥F|1 d%ѡȇ5|EElj`[)`2R& E]( p`tp?+[/cT6LJ Lzn֋*E cr`Zt INM3Y|HQ jERTHq()SE4#`R'aW\?ai^NNwRcLN9L!iH@2 `rR̉;WxN0 ^_Lefݏ(/R(XH%H, >Rp٥ XviI@|s]YbU+ZZqHyz0;vRKE0mYٚ؀ /t]ʁ (8gLN?r )$l)*+?\aaC8-|S梑^o'rLf9)~>W􂢂F4VAż0oUMgL\k'LKTB\\JuLq\՚Ƅ oHJ;gRk.\(_~ֈȓ1 `fRD "EQo}Kr'fA'L(y`{L1 `rRqJx Rt)W'aR`ڊ8:~IqX6LsLt)z?WvWSy 8L*!)U+Eo'rEB 8t]DcKt HQ4 EHȋ ;):'aJF=rY&s`2R &#o C+[]wprRd{H0{lTMr)Ҙ:X5)̕D`Z0)CS'])SOI8 `2R.U]#šu{)Su2RieUl% HqJ8)Z0CӐ H4)S8V7NHQ& EbI'E L@Ԉ1&]FZuINh)0u&>LFh@RR0Ti0R& E4L@ JV7j "Ek2%d.wK.`?0&L@ |{>e,bj"@,&^)V!{x=4Wm 1on_{ 8}B60'&7} ) =c7KT/㜧b~=zZ!Q)@K(-39 "ѷ -8.9]G^ S؏GO%A&1A ChH b dS=A̜3 o$]d}mOQ`o1,OZ%p N|7gB*L;mn&{>@hLDkGyقVV1Zߐiժ-L|1Z[ ϸLwץa´LJVp2]?uD]z[Pԥ[,y*$ƘRlD|nvJQB "EQaVSj)7"EkR"FV4`)) JCR6LF`Yu&R EN kH@`CRT6q@).݅*p Lk6IcI H!"7}3 HQ ؃/X!\/y"E @ L8):)q `*{ LC(#';): vRu@HQYԥ&{np UOZ 5Cz:> ]"Ecֽ>at)zLu4$(~v^3I8LN LFHdLy4WL1`e:Vpj4)Zwt.}X1 0EcFLi˽ 8>Q :3^gi@(A%8)J;kLG.[:!nt*")LC=Ё09)g* DRT6"V<ip BwNiOQ70PG &->A?9$TH@d&HwL[yE 8L^`Ƅ]T=g+@H8X.a/z-)W anC""iR&TXַa Ej=OZLQlR0FFES#+R螨R4sd-HQ?)LFd\la)04uR(x0O;)S'_N Vm{cHQ "Bga.`JqaA=Tj@;es2C?$`j!) ;9t$`zvD9)SD0mESIjDnIPw#.%RD |5H>)W (Dry0[;L-b009CR쑋HW)* 5̠3R,a'`j_8)pm9rLkT7>|ff(3 ׃^LZER_l HQo"E]) E)FL'`rRGWh+@l$+[b_F^_2b0ً/rWYHqpy_7`Rr3D?_3Hw\ܙ E Rb03E '` 2R '`v7I'LGA蘀 Hqܙ!aoNYHqܙ77kg𤅤D[O"'`Ur}zoɅF a5@7؝"b9)Ӑ$s&"v!'%"E8L.\1H&?qR.ar!=0y1<::LsaR"EDke3R& E1 HQ 0 Z`ʻ0-ֈ oD|"I `2R|Z+& Eр# 0mkRtFDru4):ΗbI0)_ %<pLb0)0љjڿDo0Yw2ՠ#rēNKӻ? JEB_D)bLDZKR=H!$i0J8W:3f(I8LmNôP u&#qvLF0)W:6$pR.Lk}:)?HD~3 6G"%LSN eOR.`)][D0 0-L:1Ӑi-#'L0h!{N޿:Nxm-K1Hy=MZoCZ:ƥҮԼ*3S:?"izVpT@ q `*hںWL4d`ℵOv E")fH#It[>w"E4 `JǛpK蟚~,>~-@ ^Q.ID0,EL{MJM80m! L[H!, H1r[ S̒0aR݈i)o@NL]i /YIL5W(I)W l!LH ԍNV0!iHW(pR &#`*{h+@drҳRpWL(蔄r )ܥv^0pcxtI@ OZSD$z+nGћD3,8L^렬D|I BSya'Cu2809)ҀO؋<HQ) %|`%-( d)0wvwUtZ6Dg))4d|"H:j\uθ"`k9)S Lyn>5^ `HI`?)PHrO1^- H^0*!]Oy5Äx) %7F'9ՙ *hTg/ES^)z`e#Rl O Zk."c8~} _wLZ'-잎D0^&T%פLcN T#Y`e*Ƶ4LoLq9)0Ow .Hyڛ ar3U5m 0~Y`ړi| lD-~ l>S>+"RQjȣ_9)SmvW)NO80m2RLK `zB0mE0OzSAF- Lsńg}ha E:KLw@cؗ("K;a}uǨ3?#uW`V];L@HQP'Y0yN'0y00yD))M5K1%xJoLZ?=H:P$X s Eu0s4$ E0R &# 4$|6LF_!}'avLF0)ha#`2R&xߒң|I񫫥 `*돍)-ѤU~wҪ0i\|"{ Ėk$H ;8)gp4@`n69L@0R& Em`8L@&:{atB̆Lneݤa p0UӞi %}UȉIgCAd)&ujJYNtEE#`>ztZY`‘Di=;:% DRT6"Ej :HCvR&'vR&'muR|eE0mES"Dݤ{}i^*5X8IG~p G1l"RH|vO@" ` >X̟/<`Z &nt8L6#)=<%0)Ӹ `2R &#`PH14RL! `G&4wB]k`8LN/uBa_ U_Kߙ5@k~0U 7V"=FT)Ӛ0E'iHP,H`)cRT6[5rLbR^!(P/NFDe [x Eu&Re{ HoR =4JQuq fO R9p*4q?)Ғ&xv]*hѐ`PR"ET6LwHx@bR[n& EiHXb0)Buʴ`5'tgja"EkRT Es4&SZ$p4ҤE9D0EO#L:`$RԏOADi5g71b8NM=D4-"0qas`ߑ0p)'wA 8.'L(1܋`1ܦV|\OD-g:q!b } (6A b:Au o nŸu)}wL`7WN_%%IE:8Ab܃b={i,'NZqCAЛ&'Ă)I:°-O bë1Us)ڏگl og=vxÄ lj o0n.p&Jx]ocxD+8{ZL|(.VrISIi!R0EHE!)iӐU)i'RD0mPđLzV6 ާ8vO"ezrNt"iD tB "2*#񉜔H!" 8e]֒"LiHWHhi@HڍU"R `:Ht*{R!pvLkwL/vprLZ)'R][{LER4%- H*HQ@ TikHҴ)0=BG)yaHu4):N `pL0OR~V `Nӑ:)<ԠH*@ʛ&'JIڀ~zr؜`ړ`ZvR9d0)V0M! `zvҀ߲?0YKarRO@F8L|M 7Lv輘0$uH1^{8g>HQ7IEx|"e"SJTn5F ^L]#NӞ0M`7m[R쑋y0lu@q)*0y1aahL[4 NUݸ"EwU8W'^:g E 'x'rtN T0_J20P3h@}@HVLN{?$Lk~09)+[uiڭ䏓#naI!-/%0wN 0_JT:XΤU@OpRS`0d)jFd\"H.u<=d9LSj<SG-~b)0;)f7`z`~ޓ"I5CׁGr;&8c:)]/p0jEmۏ`Dṗe EmRdmb0)Iq^ `Zg C Lfd8L~[-ZW5Άx`)0/W=j஧I`J3{"E1q T$8wmt[`ʻ0-K3sl!=)٘yHQ[ ڌ9Htss`3gNwn)haXh0W9)'-t=>[6"S/]q}$-4 ; YBap|e븦0'TsR|e^0,ŸrD HQuz%=> kҾkRT.DUL@ תG\R^Dr:9`SHQdpI9ptLW8oMFI{L[%oׯtRx`&][0Y>R|2s/ [x\i2KÄonG;L@Ew HQ?QI RۤwOvM~HJ5=G*(G &#}̯jZڊ8u#)Iq}[rR0)b09)6L4ǷZ)Z*ViD^$MB'=kCR)"ҵV]xA\'a*2IqJ' 4h`2R& Emu|eKSb0)\*cdL-|09)el#)0uhHQn~z(Ox&!fLpǚFiʂN]gUHqґ0UtL8ʛ0! `:]Ÿ:)q*8L*H!& E8LNRT62[Xќ L٨ ݷ$cRЛӹ$ I1A+5)껼K0m`>M`&R4N0!l0=-0):|v w~tM{zL@t)j>K 8i)=NZE?!|Z"S" Jx'RgьChݛ&"E3~D0m4!HѲ."01 `ڈm[- )jn-\W"E)BnMߞMOZ&Y?Q2DLgh=3 y wZ;A<(XQS6_x0?eB3'[ s7ٺ bq-x6g&?'R.(|ÄoH}#^lu#^ / '] u#JxՎW 8nr{ovPWKG &|0'\#un b_NcG /o-|r"2?g|)JLS#dt$ %M#tq)A.i+"4 & %Ht'N8bQ[ާ06Xhv )LP1\HQIg[!RB輶k7,I}`J0@h Eum2" `J0<)zpw"`R8a{b s`Y8qWZ` {0Tv2JsR%@)^k &咔,H@ KyG`rC8I%*x)C[X "nNׂJSY%Ex 1<)*g'?-n%]B0$$=5L?7 )Op!)Z<)zb09) R&uUHq^B0q CӐ)`~Ub_30y% Q֟)*v H-)xق/"`Zxx' `7 `CB0mEteᄟ &'`:BSd09)K[R &N[IQY/I{E,8p#RNZ>>(StG,.ru`2ڼn0!""iE0-4`G\xY8}S9!ahn L_<{ ź3zl1"||"eQ.ғ0e)Xѹ}'HT{R afC8HQG.Kbof ҐH&na'EsR30q {i) ln1bt8)StkLcbe͞1o`&#z'R E!*<$ E]( DHQو뗤L!iHvRT6*{-#Au'vX7HQyNM!R&)|U{³*rTd>WQc'`Wm4kxn'`vLCvR"aOam7[9"Ry L%D0譓֟OQ$0%Lc6'`jɆ-<'`z]dsf[Hq) 8ɫ8wO Ei RO@ (f E20&l LNHE߈qx1F"E%ec $E%)Y!R.#jiUB`ڊ )>ODu)ڪ,ٯP_Lo_lջ&ha|\!a_0)^qԯ$~|(wNZ>>ፚ 0D))^\kpR crc`Zip|"CRu@sq)*b+[>:Lᐁ&R@d8L-fLaR `n-E0E;(L9ُ||$A,S@a8bgcL `2CK69)0l):& atim.`CSc[1`~v0a [HQ@#3Dx&3e'`|ګ 9ћBhB=`DsR# `zv$Hh.Gq)\j8)F&_LmvoHl4:+&-!Rp|& O/3ieoOt]R1c&#2R! ЏphLʃJFh@HCRH0!w!iHt/4Lz`.i+rM#t?6mGpI'iθfZE5Raqq)Z䏇,0R&43 &#`R`2R&J8LiH&H1l+y`vR|0-6樴S&R1R&d|",Zm2@ԈE߿8)ӳ `wyL;JRT6& E1_D,F ;)n0 9)n0 L&KX?v-l?6} "K0o%ѫ) `7qR&L/ 8QiLFK:)j05fLFjP2Rյ `2C90Y3p6`g')A ;)p3 "0HWɟO,j\e~&F `J#vYE0YUjHCpP}RoNx8L+CΕ09)U Lh+dw5iO@8Lp0TipE1$gģ$oR<)ԏt0O(QXssa8L@bog0s9g0{ E1) pR܀q7`ipΜ0=Ιwha;O{QpѱkccB%DQYbLYSDGL?3MK( &Hѽ)"2)Ju4 &"Es)gR4&hi&W0mZx2R^)jAm1<& E)gDMΓ1}_$ȑH;0囨NV0L l& EmRdLT&߈D0M0!P@F5ґK~x OBI@R7O )* HQθ1)8LDu[`ʻ0A5!CӐt+h\aR6#`z1x0#JuR90ՙok݄HqR7H (eר*IT>Ŭt)JHQIԌ))RKNPSIq*W,@`8L@ 7Ks8LNLCmױ4NH@Iq?rэvR&&Kla Eu1R.k r9cU+DrzϤ'% LI*RTIqUqDuy)4h6W8LNt.bI[%L@HqM;%!J((fLF4^v8OIMy I`:RNä(&FN=%"E9)gL5&#aJ.cIjo1Mju:]bG`wL[&5ES„)o"*)fL@.mUkU 9:ݷyiN)-[){:cI'U}rIhs$8 [@KAXNH<L&"E܉`7Yg Ey0 HQ݈mDy~cĄs`7p#K7Y:ALuܥkCSr) 2IO'rF }N[ S$ H/Qـi$#d%LN'0M>QeC:KH]'켅LNzN{] _,i@ \\F"R~ϤGI"^-1;ž+f5FӐlD(j -פLs`B0qKw-Lu:%!9L_i!),teH(&_=֟O{ o`*!K:JH1! `j &#H[GLN 4e9LNoDHQ@ #]V儃Z>ɋFyQiLVIG9`*&I8>Q?))*Bw6N6$eUhںeɝmLN 4rv6N#0ys*f `2R &'E&QNXZ)SY2$ q)pqY"2%'zT`DbK;:L^)jSiTZlZ*U IS:)b5f a:mX90~IE0 q7yyK-׋#=S=~Ԍ'EpqI'rIHQI{|Y 7cLщ4tDE>j9)핰dLH1^L8bN 4!L[6ĜiHݖaƒ#+}/R&quVIeM!R3OZwM)ʉ0Dׅ ShP?]t$V00 yH1H1IQ){ E1)y+' H񄽅RTg ŷxщ>]:.j] zB] ,!2$L)'y ”3I*wG)ON_ht_i<,2_",-r^L?8)N !F 0$x kh>Nj[&̥l?8)tqVr~ϤNS*&%ZwGiLNOv[l0]ik[KsR!RW(f ӵHQq\9{b7L;$-0My៎[X{RtRMj~{3pY4),\as嚔?HHQI@r ?I#ǠDrUUg-+rN 7e 0S0խsRu0R8eLNtpu1K|Axo,M0wT..6, &-{?(^II7S'N t@ ^يϰ`)>O;Uur˔:)6'7_Y2$ E~Ҹ`W &ӈUlB(oG)*.F"drEdZRt8v"E*Twp4d'`:NcH>:)SM]RCj&O|Q[ܥ? ('zJ)ҒOz HQ:$x<`>VHQb0)H} avRE0'7BSjPI||"glB('z)N2oP]t L8&#`&t`U=:LFlQ3E@HqjqRe3R&  iʡN4NC[PcR{("#当֟Ol%,g3.W ^&)L[H%,*ǹL.iHTxvR#³b09)S<³|0I j jT[XGRW `#V$!{,t\~v)f]0"2%r¿HQJHql-Ä|F8LW LT6L[`6R &#WuIv)unhF~Q7w"|&H@*Hy%Q HwaR|(iUHQ]lO0hDh Eu&R'aw])J&|=<2ݫ0M\0Ō 2wha7=*شeHQRpgmI'R) HQʸ[$" `CЩV |Z^E0 Ґ5)Z&HqU;)&HuSSD0+cȺpf Hњf]EUGLpYP YNZ>>SIBr)`j@@Ғ;HWMs205590~V0M& E0ADh-n" nM-T Ee j./ISةUbIl2c+X}uF8t>JJ=GEDvMIRP(p-)^|֏nPu\%Ca"0fB?~7(ZNSPyށmoE!p4kھa븜#U pý!&)CA0 60i=lSHQ+ s$ [_d#2$\_-H-Iψ|N >H7<<^M+B­@ MU>ȭ~Hʖ,Yk{X}@8 z J#Ӣ~{DqAip:*1 t$#sAh͸<_]tL8 t`[|+&c::opُte O3.#ۿЂA0 L 6Q~ zc)fS 0)F|Чe'Rt@ L0yÄS&E~#R<ǾM}cxp8$<܇$/%R3(&cwLEj0"7ɤL>eRbLyʤ(N EjF "kDΙ.]);&|@bo~i(T^躢3(ak R4^ #Qiv E|F0Yqu> bHQ8,G|E 4Y\KZ+ G~#R Eqݐ̀ / EDL3]!ZvLxWLu ZAWgzc<ito7.Hm)`~:K*<]BhQO&DpFi 7߲)+xJǧ9eֈ3"߉-rA L0yg`wL{ys3 hVC{a0,z d/O$l otu l&X5D0u[#x#IAo%.^xɤL=k"2MiV K&`r$rT %ÔP!1WM_6)Rs %l%F2LcR;aʌd2#&#d+37N8xk-3 |]9bGi)B0eF܁7-7N$EVpbt-GMde5<7X|G.26m*x)|W Eր SodHQ,@J)0m@%u E3 މE)Rren/52)MpӊȔba_"E4"E3aVS޵\"?M3l8F^S*٪)䓭l߀ Sj\Дp&%Ôjh8Ü3 S*3w<9ӘȤL .=D0M'7LFde,h9jv %ÔO`XjL$ضRt@bR2LY)f@J)+7"EK)&M'l_}}. gE`ڋ&a;K:RAM꿛2)x)*Ȝop̱X>& ,GٔI! Ӈp_ĹxCp`ۊ3oӽaa@bɤLeǭ}bݓeb]Ľ!a E @Ӏ4Lx3)zz+_]HL5D!%~nb7xW*v (ƌYB/JB0\mzeoMΠWetI\S_0?2)Z;"Bg$C3L 6Y#R@bI5& EӖk2L@ E(@|4qt0p4@{|`;E_&kt~p3 EkR|13;^LJq)RbRkDHQߺ n^; gTplyL>L d̦ R`"ErODfWl9N]c G~xMR4.Dnh>7 `zzeNVRR@J3l-aZ*-e24,%:.E0M.xQ`3֯O0D H-GE&i4 uA HߦM) $ H3( rM]R)qe7 > #"A ~b/OD(j8DpipE8Mub_ n4b 4doA ǿAG 4HL \&O\&I b9 Dl!!T?i~(:2w(+r a38u &h7 M(N"5'|DŽ4ȿ&I 3 < "b}AQ<ͤ&1O '|D4L^=0RbwylFN֜Ίe8a#ŭ ڗ[A'l>ZS]'lQgA&}! q ,Pp E'4Nat&?xXD05Ws E5Y#RtL@%HTI9`Lf H"RD0E{RRvbWT'l-@+yU EH9]0w*اa91 `zHHPH߈=N0m]ߠm%\_J ` &#Rb+P(FfNAh2AђImWR4_AkFmZ1gR;x`jNW}d ]8fL~TAĂަ5Rv Eވ=1zR2Lw_o5OlMͬ` (Dpߨ'&A|YCs'*q`j&ix6 HPyCRR-әR2LuRdHT&^vq@Y0M=^)IH3x&,%TO[A]dgh>xl;Rr?|Lql8:/(L0 &kX|ʞhﯵ)ހj|U6&Հ/y,)N=JE4w1bGsQ-GȺrK0>)H-pdH6)<_ױOna"X6`ۮIѐ9\S~ L`S0e!j)m{!RHP Eֈ8{RO,o=]”Լ)  UI fRwۧaU`IgVG)H-bRgR$#Y0LC|$3)R4 @IN^ |SS0?^LHL_WJ)((WO[ L uy`ZMGOMĐhK&`OL22@֞IȪ)IHp_5o9'Re$Uc>UC_~[= <g$%yJ >n Ȕ0)y7L=01߈-NfRoך0!" "!) 8 )^4ր4n,"n3HEjv?H[0)I1TMa"*ej2=!VHQ}t}M@Fh AX].2xT8-'L[OX.+;T gRry+D]y ѩGa0 VֺώOt1 1ں0M>TM(G]aÑ5Ru E~#Rz'R EAℇ*RT~.^0"Q3V(RkslN@ԍJc$R,I7UM&)-"(;-_||Ie_Eo_2LDޓ/& EIi$_rX#Rt@ʞ|0)%aF7HZ/&cp E%/&",Iq?ai )`Q$ipJ%0m}ne/!."vzCS3L ɚƟ3j/`Rt@ʎ3TLx2q^&kd]WP%Ĥ?j% |zzt)k &T;\I5$ɚ%0 `L0+e&!TwQj kt8aHL3] gRri_y+"R@HK S=]&@&o~RnVRo~ccts{MS3} 3@ى ;ēvzyW EptbΤId͔DP0oTO!Lܦ6@&n0EPE;EM܆Fh E-7B]'6iF]φH5$|/R)W E\|@#Tޫx pXyiR7HulZ5Z`j&S5wTO[Az+MQ0>|tԉ_ H3f) 8ó)^"l)Z*<Hn8}ˤdr=BeT_a3)HmX HX(HJufRUӟbD,G*)F*i_D}UWԱkH7"ERT(D H͉O kأt/Hɵ}e3IEje+'n%)H?jVkt-z.\bƗbh>f>/#RtF&%4mx+Z3L@Jsy3(zU")ZFJx\Ǭ& E.k_zIo8_UR*VM3)nz'T&0 Hd>`UK0$R2LKD-7H5"EÚ `ں&J8-! E}Rd-#ZHo1$0١"VHx R4MĤ?jŝa RDLu"sAIh'R8%4Jm1$ IY0a)R~ "0N.J ?4&"0A )Ev|XT:J( }`әV0ǜKA EuT fqPa܋xIGS1*g#IT|nlI e}Vܨp{wͅi#$J*or` 7خ6ة8ŐѣG, 7]ۋ*uDWo Jxc7Lxy,|Ä% s ^1K b۩p* A~%ze S"U& tayT7ZI3Hv,oԦ O}2%uq{J8x"/6z,ӜHn% oNT54߈=BQP"U"E08 `ZM H]n:V HԊhw"E) J8 `՝N@Ҕop oU)JlXaSS LA(TGk4T 6뼳T/a*iNR E#. LѤ4)Sݥ|#R& EрL-h0y{R&.J)g&0q oU [;:)+<ed)}$z@R:BΫ &'^LVI:GFQER6R `J?j)0vR8ahX3'`߇OJ$f09)Zc!<\#2oCT$Ity7gG[R4u9b09);4cLnZ) ':g24|>`NJr)-XtI}$z{pUR$8pj sbGcRDZR)E0") &'/[)*=)b³DWp )UzޥՂ.IQBxxуH:R\~v0MMmX6рIѲ))@J#Rt)a H{j%wo(viZ^<eHOG$E@ueER|.Aѻ|&8%cN t>uRGKSa4&OsN@ xžf Hg=RBxL!ѣPRj@e&# EѾ!`4gLu8LNV[M)#g arHq8% <.>໦l 2 FN#z%J?k#V m}ٶ0)iN6#)|Lf((nL3'^F7> 0dUڶT!8A~9?xn'k=W(Dw)FYo@?.pL/+9#`'bIPdxZ bw8;Wbǣ)b0+BK.~p'J#D礸S0= M-SuҌ%}W3$X{0sMPуGБ:j `"R6?t8.9LtV/&H1k|χ/{d5gFdܚ9 ` [9(a'Ŭ-nnMY0)nMLWaLh1 4J!AAb1$Mh΂g_&'aj!ij"nYIV0"a EuRf3aRX­)f8LtҲS5ɳ:$-_CHTs>=zQД:/pzu#]Z:Fmd8L@&RtNQ&xnҜ3}! LrQ"EmR u٭)JWb &:tɾ.#\1e)]®%Ӎe]X mU3t8ja8L>I'aZvLFl)K.-0)0=OQ?='L ;)r;TѨb"፮RFЍ|ltHJꨴZ#ۮi :@Iq|US%r:)CelaRTwߏK.N(XM^XML%\Cq&_7ϳ$rуSPuT i0)?&1t t8*ʳmK.Nô/MW0)`LX ˌ8Lg`^v#aRs5IuR4'*HB<7Fۍ?3c?e_IxOLE:exaXk@,uX{+;=X'hyiŲIMM&)qÔ@­$|<η O nkϫ%xm:=ztn@ʚG eӘ8ahI&L0ͩL;"X0G!q™`[qIf_փHf"%1`ʣ6H@*4e\PD 4HL#"X{gķH4ҌΧFL")^"欭)HXUD &' ߲HjKxaYGӘ5-@?N)R [R}B^5F1#a7(ւMMMg)GR/ Er .LF'0oLFtU36^R4Rm?a֮=")V-)D7ko^Rdk;"*4R &#ae:L@fx*ѾS*Q ESWnZ*qA50/0t=R) .i@Nuj8oXRZdXi8$L{3e#V:6@/FLg`2RG^)ʣGpS&޶&0));s=z H:Jo6@m-Ypw8L*)6 `&Hq"a EbLpMHq-_(?\pi?)_Or$iZ G;ѣo(\n|EHV.EB)b8L.]LthNoם9Ms2R &LDZ)mp)PM}ZD0UӖ~ІG<)UiuReHydEVMٜFW"k|)J3c5R&_<%'aA?m8L-0Kؖ\Yz` _%\ܜkNtƏJx 5m0)ٟ&i֮=")ʁ\E0YbgY7_)I)ӣ"l >8Lu%L9*߈HqijSš:)*))e\M> AIMj$)2wOG:2iUL "I*RU w"E(^8L@8Lu8Lm5L@8LˮX'a*0<)zC}a )Lu,6vMQV:L(WmIeĘ ',8E0Ix)-)4)ڗhR44'Jx3p L@L>JxSY G~%z"EQ RT4)7&Oȕf"E>`"RN Ep='.HQ;Mw"E1)zϫK 'm8B=z 1P2sc[!ƶR~!)nmq)lc? d>)wʦ0XJb;b_b\>І:?'Xg 8pS:4sqOǂc f|{yXg,&J:vb(q]cGpWG?yZLﴀQT:H(uY}ĺN)uDx]gdh) e)L`j%| Kz)"#m8R{&Ö-,HG{a{pK}9іqfi@ ҈oYS%HL`jrM{mqUi &(m R4^HFWNLsWƑבs4' Gu__z) 03OIQ}m H6 0rg{u&`ZMf:ys40O>@O;)SD0Y$ 0&'EHwLUDP??`ZI mϳ zs>i#RT#öjW;)t,{Lg`rR ;8LN ϗL.9LN|<7| KI*}= PeI.86fͿ[3JG0Y^!8/AS"$Ƿ`3mxshϜR''u mߥ])Sg0=\~|A _g~;|+0ny:I8~}% `[HqR~/)߯c+~;Cmmc]~p~#sꗦxIDjt:ijMIĜQJㄳ%Li|Xf9)SD;iY%60}>zHa1)*DI\2095C*rR4'LrR3d0)ӽجB0N) ٿ}Y@tb~wLyTv>=zD2)5%7~ )-)±~H!$iΘLTΘ tΘ' 6ƌcI1PcI1vYMnWɶks(bsm8>=z ) a_[ 0,n76Y*f)7 arHqHq|Bl!a|)ڣ)ڜ Hݳc~X,@d60}>zd) Lyta|)4<}^]'?$2ahGȄLg0NtRI¸D.9a.a'?~Id8)6 =̐<)#oR"ji 0T9LHq|sN% h|)ϺbN)jHQ4HaVw.a"E8)SGgwO聤(0("/S?GW 9i!Rn!ij"n;.Dp7E݋x;nr8a 7"EmR %n"JU# Li`= R PE[v"=Hq d)G Ei&Rtb0)z#`z"$i=a &#`z9L\@Z[BFժGwO聤d E)z8L7Fd #`2RaJF{d8L-0,;)z`Iq'%HbLf*̪R H!tR:&˭%l ,iZp2`P8*Fh|h$ 7WkWL^eלG Eia2R &"Eb}d9L@(DƂ6)f _4*brӑ|N-} 8= ّuXOSBeQ< H8Z/h0):N5,*-aG򀄝W~=0) W>͘4!a'_*:fKdv>=z ) (O HQp`Ϳ:i.w}FK09)F^0)BS> a[H1li0)jA'Y&&t8)JmߤE "S ?уHQ LrF#(m9f[PpI&H1Iľ`2R~zjn@lmUS6Rl ;)jӄPCpm0maH聤d E))IqE8ߤaz~:^x'ϺEi@8)S a#`rR個H1H1ҭ)3P LHG-n 0inGG=2zcz)\zaS}[fF0&#aj& l)?6[rqR)iNJISkApPE0؆3GG$ECR;إ5|]Ӓ=dLF?E0KE3R& Eu>^0YZ`* Fm`2R& Eo&ÝH>7Ж060}*z '-)Sti?O~!O'a:8L8V E2_r&#`>7H8)ګ0!RTDLZ[5 (a Eyk6RQ&4náDTHQ$cZr4YE0$qNbe8L@/LsT7A?MM@)S LSq~ (D^ULF􂄟gy` ]k $<)2} G9D!)ĸeLuy&L4܈=n1^cDh?WLDZpZDh=wAD`"R"CUnLXM*ytXD o m8PW8 EWv8koգASaҀy#0QB1FTP]Iǒ5`.> Kz/=Ww<D #ʍ# ,RNU)!uL}>.]E#X1>R^CX"EuIa6ad}2C=me z:m9l&YSoNM⚞[Hxk pG^܌;)0F鼅t|)W23fH$|N#%nK8WIla0MVH +8t85ai/p L/-'bI) %1I 7rL؊CxHBJz0Gt);E=PĄEIH>}WL30跐 Dyg60yXAR"!p܄AamxuH# )Q6!0 "}zkLD; )ɄIޞp ĄK #?PHny9\XAb0S8.!0#më1HRB aɦeRHL E5K8[Ąg )H9&pf!0V knIX!p;be vѶ|sV0)ORI>]&{IbD;`F _CH$fL|`R 9*Ea0yX }!)0&y]LpcKFxT`jHJ#DC e$ٓfOR&.<^]HW ܼ&L0ĊJJ,&RT—HѝDBJRHLd 6}) &HQD{_UdN̄v 7)9,YXV!0vL =mt")x ӳRH1 똰iOxĝLwp")p4*LRxf( az~/9z:v!0JBJdL@w 3='9AIVRH#n&| H4.I/ z0="0 ihxU+<%sp0yX;")SH2FaW)PɐHC$0d=$EtPz'˞ LNw *&)؍*|'cKpy0+ j E`J%HgLGrH=j q` <&@H a")<&@1#+LRy Lc'_Sa~Gp=&Ċ i/ ^3/*\DE· FB"qOWp 0 ^$)HRxyC}RpWx\8IŻRxZ@q;.;~j \XBI`;e.k e @h8h[ЙNXIJЃ0QiA \&")$CO'a$ء`2Rn~?]-9#. NJ~#%z? G?S&44Rb+ 0 la<#L0yI )&BhNa")|.k?+d}n|"L 3&i8Q6W[)D 0a'O >&|L0y8< &|L5R"M&Qc B@ a҃ pLpBJ`}d,BJ5 2CHaRb^BJ&BH a")0t pT%&+I!L4VgLgix'<|{Z< )Iۜ&;ՅI!L$01aq1vi>,HWL ( R!nu>8K."b-.y;&SASamkڑQiGm5I wH a")i+!0PRЧA `")+@ ` r{zHA ` )nTEl;s Ic BSH aM)xI &4-!%Z# #%̀ 0 )Q&H!L 0RHL 0lyA `)I!LG&) u`*pL0iX7K  - )7&i86!%b&↷A 4I!Lc &@ $&)|oML!0]3&I 0a!%R9 %L4>M*LR õ +VD!)=48+n؅3RbīJÉ) v$ĸ,HW Gy9&69j-&m>C`zdXL+V(){:Dj[z[]NCH{)8 0-&#%^!}ma0Rۊi#%vhA aR 'K*ܭ8wz%Ixp=bER1JA$qj8^Ibے;5nI$$eI$ʻ$$']+|kIn ߽›VXoz?2ucŊܫDUtm(MMb;ūTI\wv6$^5ho>'no$j&'&\/?M.mYt+V-SZ vCjVfM]:e뿣INK]Z-vck%|'>u7p{ |k[ QyN^a#s&%+VDjJH i&Ҍz+!0M`)FJLT#%,DAHLIn[Lgʇ mZaOxpMo#% 3g'\, Gi[~dX;)3EFJ*TjĞeL{y )N#0"0# #`)ɄIJlMH|)qTp$LR1˿~K?4szdbEjLքR<`:jL DCm-8&"0- 'GB 4I) &1o!%Lb(`kkR$+jEsDR!0aBxgB aoeo& OHJMHL 0h>S!&BXaOBJ'и濥gt{`_R +v<ۙ#`)!0+ 3 O}9$ @ a")քȇ„yY5SއEb &`(`BCs?^%VqN!7uyIj i&)MfA2T )әC`*3IL 0qO`ZUHL00}hFJ"D>L'\0 *<3SzcŊRRI sIab ) &Bx; );}IlEHL 01~I aB IRa5=*9ۢ9M!0lSHᛀ 0MHaRH!Lt1B `_Z3 )qM! l>-nrK?53oMXQu܊&"0]5|4rLQR|{@R(,#L40DREc$p cg 9#K+RVFR!0(=ytd!)L5I!L$}&H#abەh§VBJ'pG83oMXQ,٧ HB a3#T0f t IR 'aʅ}p+B `)8[HL44) aBX|j¯*&BX2k|^5X+R4/&^IH!L ܔ,L} )dR~[w>PdRHAv فdRHAv aa <[9\ykzŊJB a2yR7zGj>n!0RHL Ą$KR]b0K*\NW)|HL3e<8# L34FRkryd>&@TZY$0,KZv3 _ n+4a9܋lj0Lh8 ;}I_>L0> L 0D>L0RH!LX`u `d_63oMXQ-RHL0a@„GtW&BH a")+L0R%|a@&LRI!LRp YHyf[+VwҞ0#aG)IÉO_/PUH~#a׀Q  >&#%^p}(„IHxkH#a|4?_\a:bEv'RH!LR ) 0qq3vМA 3H@#@ `")\@62=FH W psh;^ؼ =NW V}p0RI02` qCRHLh8IN RS) &@@ `2R &BH a#d[BCs8G^%X"g&H!L 0>r*01o12FH) 0RH!L$0]A `) &0a魷k "<3>Y&z)) )QI#R6Ikөo-&@ awLg !0iG.$ O;~H a")p{l\v?pg΋Eϋas8G^%X"U]4$D-$0”0S/?cg 0a 0&kC) &B;eI+|7A aA ӷޤ$+M4} s>*9y7Rbd@ a*狖H3VTBJ`)CѧA  0~#%ʝ>d߿wWx 0a9:XQu|U@ a)D+X~ףTp!0RH!L 0RHLZ᱃dV@ a&eHa0nװX"g[4iĕiB^4dR 040Nsk2Hy )~ w?SL &H}R LԄ+\:HL iZ#ϼJbE^0PT6I6>`eO;χW[ h+CZ)q'IF50B[uRX8h(1*G))u##mlS'|ᓏ ;͗:BF:h!#Bl3frR"Ȱ&.!}_ aC! $?끅H!«d v#g^ ˀ$;cQ cԞe7@:KRW¦I+ ͺd³?GZ?)N`r}%pXP)sLi:{ϒ&xo|)SM&/y0U~/pS9#*u$0UD`ZZGz&"0)#S"0]r3QpքWx5_*o)J J=-IBb@&ׇtS|! HB.RoKyG R R%SsóZ&IJ& ވzZ 4$'_jם)22 E}һ!9Pz,i=_]a * ` VmJV?I%HLRTE'|x E+B.|=Yϲ)r)\i+BU7}>M-*ԎL)<"0E0iz "H EuR>˯sn_ }jVWM~^=ܿw>)r)JRHi:xV>z oBgsi*-Ҁ+m8o{N&.BHQ HCHQ*"0!E) 'H"e)nB&\i-t+X&)00=M\4 EB)r)J< +>jE9]`J2'GR4'H0 5T E^?t,8L="`"mm}䌭E`"28"0o_*ouRB.R䋐O)IHQkSy)\it.B V c7lmⱠbSRdca֕C ɇsLD`:JL`Tk2"0+߮41M@O[j!X[[jd J]:)%`G>{QM)0LG 4N@Ha`\P=5JsMBHј+MHQ0?61K&֖\/ȇf2!&au`1,K&>,Vu&>ԁ/ eb"e+B|(ɺ\nLDڪƫ..팔L="0E0M TOf H/BhHNB2tJ pXQ)\i8=xeӥjk)JM$4}Zlrn4~ ~Lk T`J C0P`k`RJ}7 ?yЄgJũ7U#%(uUӘL*"0;`)e T`J C0LGQ ut sMR0y~XRV`Y0H&֖YdJ v)ht<@nzNZ&"0D`:K\xCU'!Eo H|4H!E)Ϟg !\i6-mm J="S,`L)2O&IѬ@t҇2AHe@|Rt E@t EYWGĔHפT!p`+wxKPhMD` sD`:qezm/ ,> |RCLD`:J pD5QOM)T)pq$O)AD`b|~ w:"0%S>Q:)zk@|Rt˭p&iEw`bmm%(|XUL\PȬ)EHQM)YweRT'\Io Hl:߾8xZEX&JL<`*5a>K_V9:%(u2NMӁuRt"_HQ"HQ Eub$`kӑ^\\J;6Ra)plkk/xJnQ#dR1%ɗ>x֌LuHɗL`tLksIHQZRZ&O^\籚,Rd4"A F&٢L)Qb(.IHQ>U!EWR䋐Àu)Y%`!eX0) H/@t E@ā3h`^aLJw ĕV7nrH!hL*-L% EuRB.R䋐) ,#=+ H\NNcs^?tEȃe)R H1TgRHQ)ˀ"Ł3+T^߫)\i)TE?V@BW !g< EuRB.R rO`q + H& R1) ik"0yD``"SJ&/YLt"0]>0``}?p RT\ԤTiӘ+-S0Ve@BRyAR4.B.RLiLD`:0``qww~)u)\iReEibeKL)^݀ xҀLD`:JL8C0!(Xi/)TzR"Ȍ 2Sb$EYA&M>D`L7d"L22 \? ܄!_^a] ("iGm4a? U֖"i@*-wAo)@tRY0T`PŁ)&(H&1 +-ŀV "?)ubD"恊sL&Y0T`J TR)ʢ 6 E\0u҄C־ 6L4kK}iE:%SZ&"0]0Q0MD`+L`Z&gH!h))S)Lc2YtJLg OU!Eo H/BRԡ3uOmE&"0O T6+8 HY־ Mȟ1`֋)R)ʒւi"0AHa oV B+R4?_4 HQɂY_N[[-bJ$ӘL*])%e`CM@t|tXL L)C LMsCY`j@YL*u&u!!Eo}tP)N$3 LVN |Rv5R\ ͺp`a_j5դ+7zf(y`$l³w'zrrCM4I$IJ¬lJ[[-PkJEgzܤ ; Vr&mmuQm1"Ӵ"bJjL+6Y{!1p8pLYVxyL8p- \yik"B`Z"1 E&u,GjV}CgYa~%[ZRRe@ )%RL|-p H0yb< |OgZa~j5ڲX&Gt 4R4D`J #S$0UDnY"%0VBu鵵%(uYS"G<+ E6!hfC&~%1pu]k*J4bXG<+)&W/Ӭ0\vg]a5Z(^Y[[-A L&`{~02 ExV@:=S? ))2BGY L1%%M$S7K+́.z&/¼l!h HQ)0+}N8kD`Si0Hy0~jP[2RB;BdR)HqֈijzD`d )`l+?5GpY.KY/jQK !ERD8~pV/#:-S{x xV%L-.mWYR`2&Z0ɲL)uD`oO`nNR~Tq2LELL^N[[ /r֪ )j61ϊ̎ox-Y-]ג5-M?x- /<{&g;mRLMx^87|y~PiS _9 r毜^\[[.mqL0o*"(٤|u>ΓVM!Ϟ[㡘egDP)EO/\{?GY*]\KikGK[Le4Y<|x ~'&~}R7 ow+/aS`M(p7pᗝI|;̯{JںUf dM⋝}Njox;-/v^b ޑ1{ | C>FHJX,Ӣnd6rd3 ʃ_dL\؞(\p~IKzKaU^(=R0}D<>%oΥm\ "i2%_wA=Y˱d z?[ABz 1v-8dɰ)eAC)tQ1Mó̺ I-WpDC߱~-GᛕhT|EZCxA$,VX?V{xR);aEQY:324X^ &vԵrJ)R)15>7+b n8SSb:mp1eM֭RTl+:0³$F'AXJ8Ti]S~4!W#c.bab]JԉS*^+hdJCI1XRp)X XRDWbřuS#r\) JRϊI8BLD)G!b4"&I{dD)I8HǴ9Q )/ɭ:#"&@/Ixք| 1Z&,tn@JLJaZvD191UzP&2R ,-R%xKu9#"Y ASXR`)gX#,EJD{"^L'x1sP>0,–\Rs:zE`) 3Ry #/KeK.]iDLSe X6""Ĕ uX4my8G%>9ы J!b"?'5r'Q˜ngbCVf6$\+!Iɚp21"R|l;|G^p*@LP Pq2!,'ʣRQ\7@)DLĝp(Ѧ֗"[C)LLP YrRT8ں ʣJ86'\OT&}A^(Ҍ4I$HBL;EMe"y,ϒp~DuJr!qH<LXlP\Bb J+&*(Ei r11 "fC7KB01U`m2+&J4#R,#h8ap$bB8Z%[xNшR ҄+r1ai9#@fJA`\@Ʉ k¦ /p]B)DLT)V(!nefC3(,ʈ-"&(i "m4"&v̡R.#ʔbQ / pA%kKZ˅_Z.S:ܐ#X]T)k1C5abJxQۺV㿃p6(XC)TL]":V(Ž)r̉R,UP s`- ؅GF)N5 G HY;,nXK}kLLB)VP {ܬ.$*A 9%``5%7M\2ЄWCf4%>&&O'n.$^$bm01uɘ&]0!6g(lȫI)b1QX XP#֭DL4~%¢bڅRsOP jp\M %Z 'WbJ g& _%MpypJbJ)zx1,s9,ِ7Q(l;uI(.T2ؗ&ĤюG"[ XhT8ں ^%{[Pmp,-N)^LpAn$r‡}~1}.ba ~sRJ O ܭ5 |p be7EoTbM-*_Vg⓺B5T-ªROIA|'ѢhnTTojqlD}TY/EYugiTɏj3j5wpZ+$YUp?TTfQ-?>3K5\|OQjDi~P-.&T$yZMV-ڿօϹU*ؠb1WYZ-Fl^2%,R,T*EPO/ԗfK᨞PkF=!#W ^&%cLi;S!|!߂0B)&ĴǫaC= .(1 ٠"Ĕ vn Y^ 1܄ &i]r[I^D''&pB]%bmb$mFy*R!q^k? GIx6/z1B)^L LCm{%µ&|=}N)vA(("Tf|&D߬ B)VL)7SV'Rl}J&3!ʘ\ =mE84!ڔK?WvMJb| t5«OX6#W 1O_𐱝ZSǕ"T1muQ%/#Jab6#Jb[w,ŒmAn!$ѭ xX{p3)Ϊl6e>}$ANQƩm?N~ġ\L5[,s+ο MTLBĄ2RR`$J%uC RЏ*1ZnD8ZhX8BLD)|^&(5Ù&Z jdD)H+D)LLs[cIS_5jD?Q !&FC?F8!ab]Kń#mC+[mWcD)LL:FD)LLcTM#"C:.)()#SXIbk.&}.bC oL2&AXvR0CS(b"Jab*kHl ՐSPQ Mip%`WI(ŗk8m,Yo'RSXg & GM"F(#9m)BxH1I=徕F&,In2u v >SQ(lPJ8 x1yIɔbF(ŋ%Pgs%J+xĆ+ Nj( =|n.{Bi ڄI =|넧k־^)BL+ӏ'0–\ڔ71!UȈܗu/&hDKFĄpDpDF$/@#W  1)EǶy,)/"\~%/!AxP @A;$u} XR )HJz_TDT)ɗ̊H$B'I)#/'N(ŋ :#'&|A`bHAAx |%C)PP TO!@ XR &IH&u)+Uy`J$~5E5B)~V]L  GjnRzdJAI~} ”*RF &s0@bjF;:#Ġ(bK pKKEx W <8_uP%!&UsPOB2U"Je`!bX!eeL B2}K q4,B2,C,x Ϝ~#bp@p,,D-X[&k_No!n̉)UB,O 1V_%!#RKtWh{RR6#)\LJӋ GK%n+T?B|hוw0,b:{­" 1iˆ,1RF*nOE*P RPߛJu1mK=1!bR?o NbA "A@~JAT)6DT198A*(0_[:5CũL.FB*sb*5"LNޑY?0xA@)DL:Cet&H B)6E?(Xbhc!ъQέGaL)vs{1'W7N)T>BŴ5acbBQ4au|@]~a;]爘\"tM$ /͚g$5:wadd<B}9e;"vmG1B(1^L"-" wpwַ4qg#] _%XA&obR|a%//&| FtaKO8J{;Ηn#U"= bJ8 G;$!&6F6p$JA4'W :)4IP ~R+dJxwa/Έ,ALYʑx*U )#GuR9ʄ " Ǐ$L3s̍ J!bJ9Urq; qB)^LPR1A)>m vhiQJ(ѦG Jb1!oA$(Ĩ+Ϛ0TE!eM= e!&= e! ]ɌL˰| R,R &( JbRMlCK'ɎP,ALT)V= `pbvD)4X $Wm@u&maB/&IţƤIn="XI=9ۂ(gH c>;I8 \IxO{+a(-tɄRGN>@b>?P46"&((L b+‘0F8Yj#‘=ROU;7.B)1 EVP=j" ~N8$r ~^(|-c= =8K(|(R>RzR@)LLP lGEc#Ŵ) b:Q6@)DLL)HK0"&HD)H4-?҄cÖ(݀nIAΕfx30ӑnУy7a =EO |gL50bo_+^ O0?<@)DLP ΊC>6;]4d6PXYB*#b2+h^))ʼn bJ[(B)V*ؒ7NLhD"菊HdߓǛ_H =JM~Q#b99 ðWDLDӁ hU+X*&3'&ZK` b6duR,P SX&p,J)vX&bK#Q1!X8ZhRl݉ck @'g(&H8LL #p.$|0!&M/-Jab:;4>0|F( ߍN*()FŤS=*}d&l}HfC&v 1ʸR,X<".h7+;"-?*1h3B4F$ |3Fb^(> X(Y¤ N8kbʏp 34aӥם_bjRez .b P kn@i SX XRJB)vX<"613[1SJJTyx G%'&\;81p}.K8^LN)NL"XUq}Ap)o,4b§ڎp-sV7pЄ*bj{J db {xt Q[, /Aj`2%*SۙN67aZ:vJ)%Kզ0F]f-ѬQJ[["XٻpbjH8fsً 8sh*44n\4( MI^T±;dW7/o5hJLs2!bu׊p\N( ךVl$I]~KWp6!] s[%nZѢI|["XjqSM>뿢hZR-ɷӷJ5<-mYvt%Ck! Pͪ{Ta3~rB$?HX|VyAht2սEz YB :բUILR-u̪lTjqQGgZlA5բU|?ˬ׊Zo_&|˧{.Z[+8p>eu)tZJfZ]Y:JZ䕜v9R+To NY*ܶT9"ͪmU^YhL;I,S۫5 'Upڌ8'Mx]LÚ8l/&\]jܨBTraO8BM1A.JYaxQljKLKL\bRbq,R5"0a~RL! /Mi8{8 vJ)ʒJAT`ATJP)1S4`&?VA7fPRSLV$a&{`ǹuǕ"_ 1 [+9bjc &MOpay1H~T/M5qXXK&<yJ12K1c2!@v!c[AϲO;@Z$b* ڝ{LL+מ7n2FFpŸFRR@?^wo%v!in?kS ۋIxԄ~ 5+#N-bRgB,g*pLm 7E%ܠªU2cA0$bՙ$$J[% 1#C^S t&e/7|>, ꊄ$ [:SX ߏ׿SVsFNn\$gF–\\ 16#0"Jbu~ܒ3 mD)Q߹R,̤1Ǔ~\) q=B?NxG;k pz?aX'u "&؄ '{oa[ %6vPl>bu,)b*6Y<d(3R%bU hݣOD)dx +Q ^O*}\L+gMlbip)A;DBĄavCIBR=FfLpZYT32B 'XbJ1TLF+%Y*P nEBL^^6$&#:й>~ &ԁΚ0z{9$^z1i$^,-K8wDx7PeR(Ez!vYoȯY 9YKB):Rs]awnVjZRx+X_pb8ƍ߾Pi ^~R&JQ!eE8 _K2Sً32?ie IՋ',Z1)1E?ퟞRk+E]”b}@WӒ+QT Ho=~g3D &/&0$9[J&\h¦41*!?R &$d<1 ~luf$lɥ(L jbBZ Q Qi nѥd=|=\>4}lti];_{aԁ &Ex{˟ ک̄ÿW!\+kIZDLHݞ5G=P@L?vT)6pP 1Q@L^)1'Qp)\bWFJAm+Q$Qna/&[wW 2bib0*5uńՄ#Wz(1 dl2Px8E loS@u@!w.FVB)^S‘)>"p&% #4"|OT"BL_'!N~[2a'&.}N =|'|(R &)Ee\ZVSt&.wPUTV>)aQJA΅RHE81A)$#bB821e)+2B=Vo;pwsI$ A~0)wd:P1?Ex"! J)VN)j*%5H pQEu %VSaDLP벨2VwD)`b(E gG%%VU2a$1hg'JA5bTJ@ !#?pL1@jS Xf4%>]pUD 7pQ@LT)6% +eڜ=:ِSW k^p:1GREtHHC)NL"!&:uޡG:p 'x1@Li311A)~x)@)P 8JH1X-oW&;ƚ ?0+ȯGvJB[h-}R-yn1KIx}·FXi?7$/&+sKѿCꍑySNLP W*☄#(Y1Q JbR|8E$ pķuu4=_(%^>hP>DU'_ vSO`?+FT 1y` 31A)TLs0"&plnJ1Ą-+"pԄ,zkE8?&OBXP +}hmnA:"E$)-rRAS=$d6'u T!1/&ebB[RRD8"ϊY8Bh^)1{-jvFO.gXA~hKLF[/&Mx/ 5gUkMфogXhB)i2sGtX>}(&\.'\\)z~m.S(y,@6S &x1A?1 GI"&>XyD81ф4_PpljiRZhMâ G#CR{NX$ׄ4aG =ޔ|-&}^K"& Q)ź1sAcͱG([rr:@(UP = 8h8- GЧJu'=AD7;sI"ذ 93LӞ+u`BLa.#ć$092"$ g LIJ#L.%K߯$E%gFȑBT%ufyQf4|&s#c6'6lRRwB)fp/R|A~,Lɽ }pl]KG$>Ť?mX ¾M0!{gX1ںaҖ$\!saW2^]A phtȢq}K"kg_}oS)}CDH@"ӜwYD8"M$!I#y"֒QlhUD,3 DHxÅǓ`a\x0,|,$v,Jd›)~|Vh? '^4/sf$w ӛ3ZhC$,Yif։OL_KGqyS~tU1WF'FN.)Z qV^%R_=SOHI}DL?ǿV%ӫ3?^IpIzp~ub-;RJ >/ 9t u\"RLm\0EZws SHt-d dǗ+XɎa(8֑sG P& τk~9@B 5^X)/liOY n /G(?DHq)`j &zIL{Ɏ멈,3Qs0/m2uzcGHG@ i> )B`.?6IYE4~R@ ɭ"@֗#nPoxo(FHiHLI";(N+A$DH)L)r\vS`dG;5Y#QLKA68jLGMójR.G(> )V["m^Nr0mpߴhf)a'0%խO)o\j"9m?SԷGcֆ21:jy;J€ӌ{ E;D :m{xH# kI;)ǟTަp! @w!0^sG*\uhLӨ 0<+mZALWR"l?yN'TgU=xnI{-;bJ: _OTBs[H0>VtS7R m!EJ;1B o«PRL zCߌ6 VCK(ĭϺH\RKˬjرij}Ruj҈{A `)q}vP3*Ϸ+P{b+*<ߢ"n>;F.&x ?}:G I-"K^b 5 E"R)NEbtTx2Z9Gp$]˧$&r\`fӀ#~RE רZOvӕΪ +Xaz ^L2Fi?=`:!0/pʶ. 7zR Ed`rIL) 6j v#Qg;0)9MvЭp{(9Ln0/Ж1LH!0}?h#}ՙxulā%)l`>f++| ? &;&Hn7y`F{`5ٺT#چkT=Vg`2vFsMy 5RL3L/W N pQ )1R Rl}d˼I/}X0ىKIbkgm%0)RL)-vԬcM<ς)+ ‚ <&l! 0 ?-ΖHL"E0$RkH5 "lI[[(=G0pL pL"EÄ#߯jk@tՂI`ȣ0`  S؄S>0#b@Qw;++`**l)ɤ+LNMLTNOKz%H^D`yd!R= G$R5Ay R=Gd8J G:ly HL"E[OYᗏ(HD_+؅`Zn>b)ŢS ?= $\D W&"]чdHL0B E/-GD8rm{SH˅p4{pt\<|֐w} pG8}*))D(Tru􁔼w*dB0$R4D`2 `@D@ `fL 0!] R(>G} "D[Ի&hc @& 0 +`"Ui &ɂ4P0?\) ) b2azz2HL E0$R@&nH6)p Ie8;g- >HUVp"E0ijkmTX:Pxӊ~ f;L@` WƦ‹)(<.p7PRHL E0ٵqj9iA `b0ɒ}G˅pTcBN%k&BG8r)0v|)Ʉ52 ˧dm|!>}.QY%PY"}:H%I W~ z &"D 0^S r)X1LE#H$] H `D}!/RJi[& &Ra #CJ0 n*%TJH/Wr-=D`?DAa*˯J-.UJg!*Q$``y$t^?|8 N@ `) &O#H p䷃hh$Da4{R0A` GSV4RX00@ Ya&>@y$(|Q6Y*G0$RHLSvm@GU Ŗ HL E0ӧA8{D@{apÔ`$Kl0|>^vl0}!O-y„GTp€j&Ds"/!{> o^>Ma-"D "liޑPmHdRZ GcD 5#pđ)#-"D )Ʉk8/LN1L‚ PzDYcV #+ pomlæ3*[ق? ]))z~KDMg r)Ilh E`S &&Q߃A`Y6y#PR^ yLYa ^K0^- ެ &>K>$RrSeCq\CPg /I09ތl]J ^Ϭfg &@`) )$R,/ G|Q0D8R^p Y"h`3W`yiѐy$UcZ.(oaPâ4,HLCV8 vpu& 0RAfjږ tQ:hR &A81H )/ GD8R^R=@ `"{0!R=8C`"`<"myD i m &\'lZ4)UOPX~- 6H"R쮋A 4054߆ׂ[."EC)pL ߃H8 0p4A `"raV0hfE#t'YC7ᨰ G0-$^`) #LD (|(z(,‡m9HL 0<L1/*6'f RHL8}\G5)#`"&‘pA1mph+؜쒆} Ë[Jk`9'8b_WßܦmNJ(S9vEG7:??*SϫoEjl8w N|{dzeSggyD=<%7*CQx>'7IK.AGGj&qmN?iiJl?]PZr;k4^a6!eJӛ#zP %l&t3 6jaXw )L餟sI:=]IeP-n%W'0mnM ( )_DMlɬYh'B- ) L `q$“תڎB8V& Y^Z09 hΎά) ]A"n ˎSC 8·>܅=+ 0i< 0$R(/r_$Rhx/XZ7YKd0jg;NOTO)\Vp.7u)8[ Ss@ `<"g p4&pAmkCP| ٠6z~ CP^]}0!h&8[R=@JTË"zl)4 /(@jLWxaEJ5_VD -D`)Kz'OMvL"%i?P>09A}W<:ص#`)\p (/ɰ ߃syww>Hy R7& pSPւ`rYaT^a0gk!lAp۠#;),R(.i OH@g rQRT rit)k{\pSGFr. %5 NKd9 0_pp[1) F&2#&g}\5|`C k| $0N?Fq-6Vpu@ -"DH3yS,.}~,v\ibpL Hqt@ R<\@ڙ0O)p@0~Wa$!FT“0ͣ0A;Gg* ˮ))fe3}m&~`*>̴$RΞ>8X놣>)> Ǫ,``$胀ƤM80qX|nӧdA07HabJ) z* IP&=~g\`}« w bxIu@ `}`2i OCRHaxr#p$10 y‘pt$f"8 hqP03HeQaZ ծ~Rx)ӃT~o(,@ % `IR@ `) &> Q)&?doD`g, ‘Y%H8ER ߃&*<}LpL0@ `ÐL؇ EC>+l [ 撒$RL/WOUETZ KUڇt-RH&JӮH1'OMY`bm6@8AM8R|Z0|%!R=L8~y1@E"<%jp \Y r&N` mv UXz$*3)6 £[azK)ފ-"8w 0dN/7=p(D)ڕ}>ݭ Ga+k Gp0 GZ. 0|H8H$y# y>)`-Gɢ](-=tMEۃ5++n¸48 GĴl,D`)#{D t) &"tSSDP P||+) ߃p)ԛpJ#lw֐!RM8"EAa>ӧek!+le)`^/̫02vh{MX0 Q͕LX+))dkRHL 0Q ϜvQ:ԇEA`L` p{:@@ hȓp0Ahțp#}l"E0- rQPz<L8}YaKۃX#`ZTw" p /!A 奻C _AYpwhR(C3EΖ"~$HL N`B8{Z0p(/ G|҄#ޑ;߃6`"hȃ|G ǖ H@8}\:}6Ub0YuQ_U~FAazK)(9H`)QNRt{LY7 &>\W!m p[1 ߃pL ߃f<H8.&̀&摾 iYzp(=@A[.YaZ Q^`Raƀ$Re\uQU*=E'Yaڃ6p).VSTM Eg ra^QJyT):[ 0RN`z]=G}rs*RL[#H &lf)4-/I8DH0emYzX4hQaJ*<=ZzyAg'QvEť֊YI=H) &G9 "H) &1 &H$}*Q0p=GK&{8I8 E01!OW@0<'&G8} hZF_p¶\@2߲§,( R(.VST)*%9R&>T?#ΣK 9>=p c<@HL`) hp0]L E߃X8!&XzZLiT7)2Ynu k rq> 7}6{"mjDf]LE}lD7kqjZ|X41Kg]±Zph|'HL&[8 ,tR#MT~) C~>Ew/}Rǩ+LxEU>\WEPRx# [JXstS}gYx,=<j (']> V$\~UBuJ_絀“fT3ۄY/S_g3lW&8oygؖE '„ 'Ͱ9ϰqKHak3l '0`3lަ)Ŧ}Ap5`b`r,ar ea;aSa?XH)1öJ3lURuܯ3-_G0͐f:unB˻9<:˴_IT7wvsOIQVJ WIᗬ:"0_gl'=SJufRb4Y:_ݔL"0$RYl @ `24pGep%g O?mN5[E%ahO_1.簉$R=H8 P^sryZlyJl7\a|NLy%LYa>тa"E~[<\ɗ8sZ0y^G>sZT!gN鏜H|i&|7X &ϜĂ.0tؙ- (- (?3Gwe p3)%ϜrgNWӯs!ޯsH!,ܯ ^K GZ.$| N/ޯs `s[_g+ޯ#R¦-R*|+ u[@ :,a:>u_~~]]Œu>ypdמޖ%.H]"Š6D=D<[C[ƐWw޳0RBGJRaGtVX\> ca-.K\K(|h]]DE`A(ɉ>'>-R#?-R= GZrL)Ch8Rp4H郸5"E0y>H{7_.\V"%+*,0H)/0 S\mG)!"O]妧E`)I&GIuAtM@ `"=Ha$~'H)Ir LB6TaKIl ;DP/&\[+Lq)Rn@jHr &"4OO-@ `) 0Sxɮt݅p$Ub=-R| .itʻޢo5: jnqƮ) {ނ y[H[o1:|@ #A C(a`!p #0z-2DCM09eHN9ebۭdhVS@WX0ZzȐ۩^.c,\U oQGN[)p4{I px9Ðs {g7p#L&yR SzdT$˅ l" ^.p%0N%HLL"*RHCZ) &OݲvkLA@ɲ E0ص E0&}~ץRw)9[ɉ 0<L4h N`<LYۨ =\Lp!{) ·UaK"A`)0WNL";R&x@ AM L#%H HR Y0}{ƐyR0ie;# y| T@`Vؖ#ko_j(|,>*po=Ht%mV"h&FiKsQRHL E0'R ] RRHA YR)<#V]}! UX0Ehr)I &\JLxSRh EgK&h0HL E0f3(]7) VL"S GoG-!G{d00fGL.)) Ci W{ZLI` <˥`6]0sD} *,6A K]7Qa$RpL 0<L"E0<*#p`5`s&GpѮ HkQ߃C{pSC r)X8 hȃ|G)+L>`˵2 Z@akA{s \g =~D ?`BaK\&QabEIE~vp~ԴHL 0(&G 0h|X_A _j*Z.@p)$} E01]L$ y>¶\* h?]O=#˥)_^nV҃e 9`zW -BĠy$ri@`) &>D8bȧp>,xL8R^NA R=H8$[..aȃK 0< Yz<@i //W0ZyPX022RAܬ`z U%([fq90i -"@ `) bSimHL8p<ޡk±zYۄpt% 0 o$RmW dCLX%0&+B  /O 0H>+| ?S-=RG$nxW*)+ Wx>)i[G_ѳjCR?y |W?wGg?wq?§&7 >V D|Y_ m lҳ'gyq|N:Йt}Pz`=i#7*-ߦruocMfCa0IfC˴+"-xx [#P@-~Cˡ‹F{ybN"~]QETi~R@6NU\y3CaTO峆&MBJǖL>'p$w $0m9# qA mI.R[&DQ=pokaVL*< /pөf&3],0膁zQ@ËXHe{ۄ*\&6*aHR)XpclK]L  ATpCap͘arH8L arW> )m&)^r%sl `z>f+pw#"0GGʵCsYw(\`)Ɂ,ԄcVYo;G["A D`rF0 @ `)gE I &7ljc(,C)=@ `)aywb 7GVM2 01i}:}Q)w~a8[ 0RHL8}/H!@ `) ujVCPKHL&qoS8U0&@JRL>$RPð+HL@\v!Ri?E Z?=wz `T 0$RHL=n.ĒE `SupL"EkGېa1 GK8҄!Rhpycp8 0R+sR߬3fHA\/QS}ގ0`´\@ Ζ,m(|h#R{ ),`L:}HѮ5 t =H8ԽTpG&L8}Lg) qTx@Y#WnL,>WTk30YQld/c^`*\f+) 2P) GұHPQ0|0K% 'sD:0PVX0`b A0ymi X`=)46 0RHF>Ti& W&"Dw$= GFMpL&Zo|ByILv=@ `*JU\@ `ypHh@IaLjL\ $Oaˬ)MRHL 0SiͨQU &W)TG\P|Zñ/R(/ڔ00p=H8d*R0v0<"S3>`Ra[.*,T &k4$R /^fm0}La@X)l7#eS60xD HL"E0 m<3h&n!@ `)%z3<\1 l{pO-ui/&?UEI+l˅9<֭LzY5=xmSZBqyhz´\@ y @ `) J?˵L)I&"`<`(N8{.&oPX^Z.#&@ !w3AJYP0‡  `Z[ Szk>0jAa@ @ @OUxgYL(LdSB6)I&ԏ蛔~DΖHLҹ,R#|| Z`IhVÄ#D 7`nM8VYah-&|(DOAaJ=.*| Ki–"E0d[HLC#D `))p!NAc&Ux&+`fepDHoRUCd-=l- `§;n'N`DHNn<+<Ͼ Ya e,*It D'RHLv{Ӷ-x) M E`0m"EpLtH-&I8 !xRHL&|xBOa!_Na[ \zYa+6'E)4SR%*\L 0q`)f,*aV1^0 `y$D`&Lф޻MO =HۃC@!`Dī+)P\>וs>_PL1d &@ `Ai#RIHwz!t/߃pLCpL҃H)2D "&G%CZ0I~4@a0LYa~?# A "xr +<`A 0 Ȁk=H=V4SGV29`5`){%@ vmǪݓ=@`" [-D rN1*?O0}o6&>[.SRx Aa\J +HLIiO5 *loP\ ))8s gS`*[.LLLL` H-${ 0J߃WҐ<4yx!++-^әWYa[.«Bߥ0E` SfR\LY}RxJgWWWW#RQ&{ Rp/i{^v-5 G{p=tpI8 CXR==@&&jb ySV'&jbJpL 0 &8J qD (^CLqI&S`*?5Fǖ Had:< uB8Z|ZQ0|Q0 ߃:<`)i/l &"Vl-Qs/+lq {r)=r%R<6WQxPKiA> EΖod{nFZiRh( &GzGŇHѮ5$ᐶ@ % H8!RKL9 #  COJ(܇;^U^QX\2#٘)xE"E06ӴS E0RN` )>8}Ȇ#H8# 0Da' C;$}G EiQzYzZLLhc1@A:doi+QmL-ڃQ~ΖHL"E0M"YyRb Rb R#|⃳8 0 y%(*AL$ ya&bRK@ < &>JZ )+k%+L{0Q40^.{>-=´(S)R롵DЁRHLRCkR}#FA-) &VmyI8R|PhH)$D p R4]+!OC4/,=lO&~i@ )=rHL QR-*< O%9oeTs -Lt=}m LbSم&G)tص`)ص#`Ӑ'Y=@  G0p0])$N$&>SBAk<6\n7K œ/NA{БAJ" ER%+\ K)+$떹\RӤg{G4==E,t8Ә+HL45Ʊx/a 6шI%/ޠteB``>=fq@($rBK$&$DUFS;dQ@ ^I2c(ǟ쩰.,)" 3^q#+<՛sdA 0H"b>E&zYo=aY0FCH DbiQpK L"|v/DpB oDHC:pMn"rfrbXxCa OXp? /\x;0&#MW^wF%&yI]|M(`/L&\1N1~Ә:tLTj]<$(Tۜܨ T[wAbQbe6hr!og*ANLͻ)`~'IQz 0k0~o86f{N*a= L>i`^WOW)gAX}Q0};TbL\0c7A"GAI|HYbf[ R,l>bS:|>.L &`lGIӭ qy5N!+Z )[|U¡>MM4Mj1ٗM4 l)6nqLT5E&)x*2p8LՀ"pOE鿚ON[%P[ŕ *F zs§_%|"r$ʄ L() &֚ 2 :Nz~>'%L}g>1RL/U:89eG:8F =3c#_8~1RLPgf_8FNpnKX=noNfxvڶC/0H:kVԉ0]Ha0E NV)  dյc?:F =N p]"NB{*6pRX!e- Gȑ&|)au:aAH0, & I+F_p9R8L IS!u=%6[u B %aNEVb@ggpۚ ×(Ht LM4^01 ..CZkt1p'0 R,O9.kB mW'EvŚ>VEkU&`_wqXǖ\bRCpMg4 (GE .㏰NQ4: )&oeX8ռ[l8)& x~0 +n"΍ IbD>0 >}8 Fھ˥?*Y=`0mB-+aQ4}X%9 L 0R'-:$2pH]̌Fsxh~) b;)dN_TOi59P}ӧ`:0OnRvS³J8)&B>$^'c ZI<)Ib)JRG}ܴ'UbUCHL+=LVT ;qF`)@ ރ=)Z#2* OL bp3Ó)h8)(}´Xj' ?pe&P&V\q p)8-ӄ m?R^\H-zB `⤤]Ⱇ0@ xit|࠮ /%qMz6/58h$L4&`_Mfd#z ?NxDXr&+Oq2W%~ퟒ0ΨǦ Rh5d$SS R8Lu\ʓ`zyX#!YLq660tY]? RPWd۟1.#efb{l e5&ȄI0<{$v 2ᖵ|J.lLI$pY6)ɓ{E"Iw fH0( La?cum0Jޣ j/s6A =0p1LSR 6g1..H Ol*c\fK'\?y,N'705oLxL:6㭇ĬB `FA `&I `}UD7L4!01RH0)mg?#0aރbݖ)lض#=JR 3R,R &^ YLV0P\UQSjfj,ꁸ>NC[Jؓb,_pR%0Z 0]c&Hr"&J5Z]<{Lڲ6S]`8)98GdR8L8~B^b%3<#0M+pa^@ԅ"a:*Ҕ2^ *wPa rh[N/x@)5F`rEJk6[H`&aB7puh Ёcㅬe&6pPi8xf4}E< RL DHAL.`%^#A%,`B&"`zMvr{>= K^b}'HV`{&@ .LR`:ptC¤y.O\A+w?pX5 RlV?pLRqc1SRLIbaHPG +:pĞ)o ; {+#0n()/A T RPבu)L Yk*ך$?0)zS0 R4H0EBJY^lfLjf0UGc0tl5=&umdo8V?aaOIe'ɰعّ y4(O'Vֶ Rb0r_) IӪ4D#\SjS"˃h`)s-ᶄS W$ RXW:ۆֱ)+~8נ#0=t4-}()"H!0F1&ԵuI_p 8/0pL{SSOHAHδS?G B>_򄕡)&pL'>䲿Є[4FdeMgR:%0.:a  W%pޙ֣z1X6SLFa)S{PK`&GIb) RF/xiacV]H0cDž8&1plǪ _q%R0_/z07[<˭ oN9 O8>1fעq822Tz€ ^Yrcdk1ʓc2#$HPKRQЫIJ+zGIc)ʣZ5pygI ;]a&5pT RYcE/Hn)JVl RlYä^d"^Nxm &Km&f+0NC$# oY&,H. Fb6*R,LEF}WN}ɕA\wty */ q幺cli+ʿI\ͷclkuˏ出#gn ocQuPw,  cmw?^T80& 22 yx**+߫;bމ;MUרst)I|Q1eҿ[.֊_c6*Θ튢~HTX*{ni$Zp.* JX\*ffnKx'~:orj][7uGL\()-5*iĢr!;”T9u4e,6J٘agݰտKհ%տ4OjdtJBa?7PK6e%t±7Y5l4^UX ŗ$ꄣj؊<))ЪF6lr|49>Ք(dBTX S0`%wӃ.–\TRh#kIfwVQt%0&:8VV<`A>n%IaV'< JL]n?` jJIu3Y< @ [r *R'EӲ}'0%%pWW{L= ŚwB `",&1.#NV 0w:52G{EH0uPa:w^7e #x68"`3@'(LYĨ%QxzRL4$H0y(H0`R`Mr &r5~֓aGh ;& gDHa0_4[pz RN <8-~KpFHq5` T:]<)xH2R(Lэ횆QLIQ0EפRFD׮fKB AHAP;'GAU& ()G%`B y8)a @&P&0 P0'Ƽ`(^LyS(B= B )z|ӇE:>c`{AZsbKIGN:8GvgR'opx&hZ7[^S1):kA )ꂓ#ԵS3`"_[EN zk.!0R|! 01RyRLhtlT'|pRykݣ+'`VF H0+=L걻D)!L$!*c!_e~'3 x AG!|Pv3j cyRZOYRÔQoe|/o̔eURIQ0~" *VeAY̕ea,S>eٸv)AYV ҲH=銄 q]|`*e$=:a(L(3Gi4C ڲeE]P()Je^!X6 $Y S0)1p\R]`0[مjJwՔ?tA)|GA[&p wLO8;7K'6 ȄO) &SH0ŤLwae0#E{O:wJɑ`s@ )jن▂PR86)QL 0ntUѝn3H0? `zI' 0> = 9YD|{CDg f0ݔ0ZMm'0+d&F `n&!= E R.-.s>drp0PHhHzO WiQ H1FO$#cUf0YF\0HNHDL~;-F6{pz#aEi=QehD*3YX,x/؂L??, s6% /HQeI'~w:9DZd&h`Yr !#<.pIh0`,Hi^Te+Wr%>&Na&`R-H1SM ȟ,H1U 0 LLdHI|p0j HQYo7S0 ڳ"vwGx" RlږZUn?!³#pG"`*j9T5z w)RRk71ɑb"m )RsnP ǖqs$V\LCgbX Ib-"µf_x9Uޫ L#aAţ Ef"o, =2Š:zD*}p"DA zܒ җj\S7}Т#ƠYd Rx' W+̴$$f)xEbObXZO +BfVbf KUGy2 2:MQ 1ɽ)b(H$->rd&]rK(v{w׽>ZQs4y |&UA&)v<& j#A CƳ)vL5gBg~#EtA6eJ4!Hdxdi/ SL/) vG,D&$i $̂Z64XG8NjY#H!0#Y $GX ]ta& ?p/>",H!zėJ-/(#TįJUP2PQRWDK!b~- RlzSPcM8v-E±RLH?&*`*t-HayozPeC0]H )2Rpf+<+"6UL^.^g:R" ET}]Lˁ/]A$Hm6룺m4*sX:(){ zD±YW<= R< R8La20@%S.FXti>K&S >v[$`dCL $H(UkBF5GxrۚStA)TDS}}תi&kvI?) *G )XPGZi F`b ƒh±s+I8&;))pߋ+ȓ#H*}T3ْpQE8Cm0CmoZ^)r7Gxo "0ҊfM6#rH5]zDF`R$jx(HA2D#0qR] RL|냩!  &H8&J[$M8͂[ ){JVO H!0!< 00z&a'Dq$01R@t~i}>#H0ؠI0=!`֒#[b9\@ )#0qR,Q 坏I?H8QF` 6p%Gp()ΑlR8LCMwc!ymHV?a;upyO;$[< ]FH#,6 AT1KMy U2)aGRL AuD0- auE$ ´ҥ"NI$k_pULIC` ǖ5r`XsA&uGbyH0=yM-@x66zt =`F`"@Ou_Kau2'ᒤi&VZA) LEu0S^hn8]buP 2iU@灌Lyc&p,pL`3I[4X9c)б~HmRHc,RHAeޡ2E\D{\S7iYrQ)I''aEm O'j%H1 4W<ՃHCb9qYUaJ<)~!k;?*.QP 'yG';$A3O8ﰉcFbyR8) 0)RlI0[ Rl,E±0{'0Zᙌp9:RL{joؚ#"MFxg:ž_ gϣ[aX<)Pݙ a]͒'ÔlQbU. )ք6LioVbaHQ$GIb%_h^ôwx70EAMo*P L9oq-ewӼWUnzw=3oq[ Rقb/M!Nii* 0`k> R,oPgIKHXLKS=`GȲARpT:R)F}$HSv#ZI%)T±H8q8. z50cA Ŏl"H!IF8> kb֧K##fp9WV R,WuTI6~{"(U_7bWWjc䛌WMZ7yW=Q5?jM#<SF// œ5Ϫw'5|O_uw(\h_z&2iTO5:"R QamQb]Ǖ2tP-E%;0Em8ad9_L_6۝L!ıyTg㯙:y7/|~pmM烺 OӶ?3DYGxy3qldYZ'Ue(8=>>,)RN:Y̋E J8E ZQ%Ҳ)R<Lb+`ZSؙJ[ozPCRxV vA֪W(+5_V-9rLCUrzުj2LcqUIkJQhHJH)Zn$W' QfRg]Iٛ-S0]vbݠH1zaRdX̞5/ 3E{5]04&[HLLQ`G"` 6S?{Fx0JGx[*RLG{VY``ZZ҅u4N)d)\ ΧLl5>o[=Azv^byHJ"`L jo6'EԘI`r8cn~Dj)&Oojr}Ow_)N v%OAb$ow$)G5 R )f`ZOl[dAQQq1!3x<)vY[`LozD]7svmRw3%vV [O0ExWg#8<)T;7`z0Y'Eю"Hhw?B:{jEՉ"`ۅ{X+R )aY!ܐ)ּpRxZbFM8 &PuS=Чpc3bF+}BaZ-| K3poMGV`; 5#E=p ዎ0RLuIVeJ)զ`?}w]LCHXR8Laݶ1 R,\9)tTPT !u/Q{`3Cк! H^F 1C5#e>SޱC5 t04epa v0~86{'a< AB`Gm1PX"Yp8Llb&tg%Z'"H) r4S3RXe -!;"'.>#<0S`w&&B }qLaH#}$IWp *R3d&lA;eg&L !w/\΄޽Db$p [ [NLH!0{$l2,#0N2:VFxI2+00#-@g:DW=+IiZ r !V !T!oJҫ)h&`@tG&G |)> )R(L:#P6WL[VIQ0aHH]~DFu D)@]KB 1{_#FE)8)t@牐Ba:%"J &p;I”3ҐMl]\ =)I[.{o"Xؑ3{,+#0!кǶZ^'_O@ `J1LpO᳂inA?y`)~w#sp)&__$Aeo\*Ab"j9O9KRW9K=)t {'w>m5CbwRba[p|w6˴N~I1>hϾI0{C?;H@&_;sN о1~lǂU뱙0u/LkI ))$)9N.7 ѫIa0yqbDHL'= @{ 7gt" Ä#=) &O2R0@ZGGدuw# 䑀)oI"d-췊0z1]mu )&tLEOS}k4~DUCVX,)OY{Rw>Jz8^|A݃بca&ph#(9l.$& B `st 2aGu d9o`"F4»M`5arVcdCLTDS0AABm&CA9TAbaH{"o`*;wg'E ,oV(fF0&L2@mGHAA`r٬oIFLdF,8L5pdˬ:?R 3/ ? D+)ɼb6yҊBaZ0ϤBHӇ%IW;g Ā=Zbj)Qf0%BaS.ר)vJ"᠂aإ@*ƨ&B 5Oxh,`njA"\^Uj@'‡" R,"`V׭[K *&2wARrpB'aJrĔ !&^ `R "ሴ|J)&E $=()v٫ U ǖ `&BM"`*W#0Kь b&5"7&a}*£#EL&`I˚LA NAH/?AK&{"ZN SڛHaI[ pD4H~ Gjf & h&G  f&St[w_dzZzA -^Dx#FRLbO/Qד aLN}#T x ),d`"j| S. E\ &FlMX;N|0`+#'AFG{L ~.YGXR|)3z}A3)&4w0T/I ?IHquLaE R8LzbHE*Q3e@ )3eä=.ᐂ2[٧7~+;H0 Rl] R,`)VE8aȔ0xb.#\` eIGE8 )X`IZǴԑ2R,truZF,c x[ j=)xp^j)x1|5$ 8L8L^@&2j@0 ytgG7uY-utt7TA "09R<5ȋOHIHt:&&J!0!0aLC;d##u$ҽ&AgA6 {l;%@hz# c OC?'&k`:&`j`\n";'zM&`Ϧ`AEOөH2#tҲ6* tC-c!G)&{jEUMp7n! gH0`v(³IRDrL#"Ⱦh ô jZm0`'B`"n=[`\ T`Jv+ߓ%L=d:7F`)1v ,GG h±nPr xf[ORyoJ8v 7<Ղkr]`Zd_[0a.= Dx6#qԅ+t$J D1f0&⒕0h,mK8VT±()pXAm)6H8 ji$$h)1RP;#O`p{_WK|b >#ei[TL4NZH BYL";ZU=/*R,%2Dm4 Ѷ)q&` #6gpPmC` ^a-*СnO(L ䷝<#V`TNG8)L#ܾ ʫQ)V-I-~`RL_a4UAb "&L„*]Mfw]x`&A[-&ڢ\tLO"&I$ςmվ Fa*D±^$j[-M&`j.fHq0#?))Va>Jzug"®*yHʓa:$Dף~Ϧ#<'4ä-S;RkIVTe0TjBR}gw "b) rQDU+A'iV*{̂ D±őa 1>H0-8)va,LIb~z3p/"ew1SMt= )᥊.-L+NMX?"R kYҕjAH(OMtSoL=m`RrܘigG>Tpv;o֢E:LFh[l-LCg^Gx܊)N[o)w"#<8v:#"QE}TITؾ/2*a>z8) &ወ)le ὙX=oӛ{"rg5l`{V0-Lqzo&HT RL!>&?H S} )} )SMw*~: b u#wO8x6u6o+ ⳼UխLZJ[VdSsQ.-N'apMbaob0㏾ "A p , vKw,H1K򧧎B`OSB )7cV5oV"XRR!Ŧ%%0Ca̸r4SJMӲ 6G?]%0X2EGX^ 5P``;B x$~ &J ?%E=%H!K"#(LM-=#E"z:b3 gB =GR-4PR@ !4A He+>B /# 9x ֥3»#a!R(L7S `HIvWO'Fdu"p0I!1&0h Ly,HMbaKH05Oj3P ))qH(R(L0yRLCfu ZXa'`λ;")&&GX_(-Hie4BJZ&PVS)Iz]Lz/тXMa+R,&B XG0[ӑôIÄ8,к;GQ {' '#.\uA \u NH돏0 ]"\Ct 2;ο0·DEAH0͋"ŪLH">4?l|.Wx𲀩 PdРu%!m&̻,`r ?<)쿙0f~X 'ԓ`ZN0`_)tg,0 )e}3)ZB iwB I>rx HDHKQ<)dCQvK_3C-H0!&peH!Ѿ>>!m%J8i}2otg# Z=LV'5~zTr)N `b2S@~znTG"C׵H`/DI}`&O /#9K8K@`" ?~'E PGW^dX†elyRLu -`ZV< vWupݚB `rpL>aFE$:fITū)?pV fJc\q.n'HHA'S5 -h(HGߐ-h )~V0h~qkRCLͅ~;a~l!\X?؎ BC#&JJapt%O`_p,?HS0t>@ |, &pL =0%I8NI8Śl=! =Sa( ŒOnBꥈ( մokKܔ 1X `" :)2bt? .UA )s0qR,)ɓԙ~tS7\g^qi m&M$ ~W;GS0=D2;pPpz#heLKRvN: Iϻ"do"Q&OiLT?$vFB:ؗup6wYbq"&G qͥZt  !G%3pPY)}0(ȋ_&Q;E ` R(L~4Tуόčy8)~)I׎RQ](RBGR.j S]|dr"Dhk1Er#):1txG Ly% 0Wˊ:9DOodb5* RO5l\"[RM2iErX\0l_v${rpro^_H E&h[~:&Ba&7{͚ =H(6O.L =@gꇚU0) &u6傴p#J" *¾Q|Ϧ`ډ;k|ϣX 䩤4ݣ#<7#sY@HZ &}cLTū)/6 R,7쇿xY<<tL^ /tB>9 ɓ! %,b0^&S xYG}6gB `mM?-7HCx(V~^8$L>7Hbǚ@ U螜>фcU`^<`y iaHsvx`HWLykiarqӃ+O N2=a.eG3ѹ; `%6 `ϵMzWm 1H0*0,NGK RxFX9"n!T\'YOK#0XaRع%>ĄbD&P`O2 plLx:!2‡GxLt.sI6X\&t˗B` " R@ H0QR0*P&p@I{6ɰ$+N8J8) )@1@IJL`&B (P<z^GO "07(![o ?a} c5r[S0"0}~euH ڣ {l]()VU ӑD 3I8ӃRjʕ޿rr%2H0`+o6LIցP6;l#|.f˜gkIjID5]^ ە]AY6 OXCUD)Vn=)0RP|Lҩ^Nl_~$`JEA `  Z"q:()RT±e%HՒ$;kc0@ ^^v \&r:0 RX u kkNFafA/Dڬj$>FIH!0k0^b)X|]fI”oQ{Rt_TCbף:M4Q$>$Op>k&^gj[B-VI%HU,PT]O8bZ[Q.TqmT%y^cKVM3bkC՛Tǟ/GjkZ*2/>/w}7E9㥯\+uldZ2iR->SQuE=I/C6 r֒IRWېzYh&I^^ɪNRe&Zd=`Z.Tw=˞nވ8N|ԝXu¾-Tw:.ewz*4X? /:"\-iKS3y"<|Oy $S#k+ Ndfl#g4` (R$L!)Z)h}+RLXpRF[T>le^yU:c ]XEkS)jnP鐪|ٶ5up[*"r/`j^L4+"tW"Uk} [A {E %gS- bL](S}Βt'ӕ%LLU6`a/Ǿ0)5AbU"n0KAJ8vjqR:RvYbѯ7V^y:B]0S9 E-zj>wbbmm6Cok]c!H8 R0u\"<)JbI`XHqԅ $H0bynQ؆6.C[Nꖐ4'[J UafUԞߓ&o) OKqɑ`z~~@kaO E~ba~} +t)8)ꦟͬʂSMTr [3CHXRKF("I0ص ԪeEIq0yRL̦#`*Y`V=J[=$fݨy@ )ji &|$0yR,M `jSMTЄ8Lq}D*j{ HR6)4)F|6`b{4^/|n3!J8les;+0R,liH2»F#[VS0 a} w"ŚHQ0 !ô &` O^K#0*R8L qѩ%pJ[zmF'SvL%.{UrEHLRBVlȰÄ81*aE:³@HL 6CuR˅? 0U+dLlZ'XRLO"w`Ғh<)$GL  !rRL'H.VbDH0-'ۏs)ki &$pR_bz#@4=JB !%M{ZGMGTR 8D. =g-8)v)IZ%#B bIS@St,ORO)&O ֤V~c! Thܒh±Cb)4Hay޼D>GA:Ԅʫדpeg8|$ ]dJ\pP5FB &O1R~*m])&B R5YI+`/+R}3T:{'E8THS&< w( Hux3H8l岧=} h &$V:R)RȔXjY=6€|vīro106sTqN"<}M5[!0񤥪r HP9cLIA:O* {B'H1KIM E5!0M`I+"?HS"͐,9L I\ZJA 1N0He`X_b3vN &a0`j!I &"I[tϥLl`%Lu0SXU`[Md4H0HqIbegB bw&&p쥡 J a)vRL(R%I>)R()WBnqn1l>nKjz_uwR0*}jiW =T)6EL:R‹|JP;RxGb&|.S,9]2S$w| D,m8-lK=pLH8&$H0"92R0/~@|* 2J86.H0ExG"jqid"!.#|$\HmLt.S"R7 y)vJd5&"`ZF5 2 jIk&,HA'. W_$0Ext$FAploH& RH0~Ib RMHq$Y >}$IW$B )&L\@vQ54d )$H0cՒJ*} Ts*[GHL4 %%Hr$S |C-oH$H_ٝװ\ƊlRL $OO L&.I=9߁*qSo&A^=RiAv |Abصn@SR' S &L-):atIێ?aV^e?p1}4z+&F ~nVg;BX0{R}p{VO'$$0}R)b\ 0d@~:bKɖH8{HAb=GpÎ%6A!yEi=rH`ߗL ,w?G0M$0*}Rk8,BI@ )r$LYY5 kIW]:B-/Q1ML )VۍZ$_@ $$Y0T`uz 9*H wL ۫ؑ`dB`ZFa| dz4R=خCMkPm#pLK@ iQCU@C 8*j`ʄHa0D"H!0QR,&/X8/k j RHb H!upl}c 䐌$Hh±rt><˜b݀1lUKL0FHa0[c01R0*?%<|:XV:{B R) K) *aGtuAVX h)6EBbg0KH݃& D5Ow|x6ӻQ2R,A 鉑b>RZVFa)mgEG0S'|#x _]t`-ŽrqZ">R^@?IMJ+c^5V)T#gIW  Qb`G=)>qäNGkVV5Eުc(Mo*Pc\$ 3O/ȗ#mQ?#$H1A-.8̯h.wGxUvaZ}j 4YKW^>"—Dvw>qjhq@/CMMjTҲWIjQDYo#R=+@ܞ. SlTa2dتQ)lLTvaTT9~q٫޼QtJj=9|ۅR2)lQGj¥LMMYmYFxmC" 0U0UK;j]C:|aAp7ZFR&`jV*iR-p jZ)db! RL9Y *)op_!Ô`9<)vtS9c = 炔w]/ @I9IQ773M a߬oDݯ;DxM\_5~?kkxT"qR|)+E u>6hRHnq`^LRl`&XRR!-%0cHaO)uyM #`r&B %1ĽӗL.J`0&B aHF@kI X)jؽ;섚Ba}]b(b0DH0*R)&O `"԰QI=>+>#3RhcqAB F # V0!->GAMo>ꙛ:33RxҰ $+»'}  [`"XS1!]G FH05D&PtTXՊ02RD%6H[lH03zڤ ))}g!EK[H@HQ0#|-זrL8Ğ 홐DX]Cװ) 0R(L7SDňBaB.)& [5 9)&|"p!m 1&Q~2S`X.鰌=vAmr[`"P^dcF { 5i _=kD*}&|L-pR-ņ?w8x+}DX^Y_8)6yiE'-$8L $ B`X&zxkr9U VSR2`}2DH0aUBS +!V&B Z`"PНXa=iwO=\`ؼ'2'‡k8DI'z FI)Vk:9L!X"Pő=@\˵B `X ) D%&F ZyGBa 9RXAz =)`ɓ"`*zGX=D*}DkR(LsփBEB?)T_k^]p̓"`BHq0"HaMv7=!0QRm_<)Q?)vH"XZ.Omgkx -#0m&AIAEίG)"j6sWL) )(WH *B*39^+Y  S`0 Tq `"b3U-pNIFU~ᝎG'>BL䕸W2¹5Pa+ɽ`a W8L#~C!S+R+dbA1z2mKHA))x)6(dnkp)R9*d"=f 7ϐBZlT+OFv2#H|xG?Dx5Lɑaj[$w(VAey2#Ɂ(ʄ j1R,)1H!07W>8L\|`UeVRXn_/Lw L vx)SD8="AE&&>8L:y]5p(RdZ%r)W:ջNu0}HR,9RLX)"4c`Xl) * B^O`:$0/d4B D)5% %~NAӵ|@_[gb͓!^dk"k8E RL|@)R,ST_>$):H!#T#0MPKHL $Vǣ8 {;߱D±Ւa)V~ke>vLy@ RL篋%)&B #0T{Gx Z%H0- rA?$HPR38S}L¤D@ ū6+#0:*#0 1 P=ē){ xH }o / R,)(`ЇijT s4S:&`j矍pj` Lk4%p?GB `XSxZArRTJHS=H0TZ5 G)rEHL0JR|A x)VvK7r:{IH8{]yH& &`fO[ ŗ"% @ &¦\?u @K##<ROfd9re/*C"#{ZaJ#0R,Z`Xݿ,uQ&y0t0lZR8L]2H>b$NG r:&z:Z\@ {H84+ȓ;7 '!rv$tA ɓ:H0Mot¦\G]k)##猃t2}&H0]e$A~&:Sc&BajIX)B|D8(j*(>W&bt7\1*R05d6NH8c w"-!ɊowJT &=L+GIS4ѝPcS.:_a'#J&?=āpsGp3kxҊi,n:|+kPzAYTXMS<'2|ч$r%1LPH)4H0U)DK0,I8y6o( q^0u t)&`)>O R02^]t!_lⶾ R,l_aH)E2'źR8L",z7z>PIb9!MАTNb8(){Ba:FH84 hA)G54>5l}2y4!!ӍnLd2RJ|E.&S.wFT+WGmX=4IዩΥh&Bd&~]*R,&4>gS0%m>Bd k l0WLʩC׀t>)&J7F bzA;pX±l0 Lâ3(R0#7*}@b5:"¢1oA g!SaO#ŝֻ%`0jc09RLM77YZJI:S}L¤$|`&YЋ'*& x G2 H!0ݨ ^:RL4Xުc$H偑bՑ`)(Lw.{".A T;i<ϑrSښ)dw_ Hq˺4 S!HyjDH]6S&-#ä/re X)c05+1~ƙKKpyrx).E±tz/n (LggY<LԸ#?咹)~ʥZIFŚ#P[E)))&.k0yR|8 l,D?(H"*RV/OX$ T*Յz bXjdTXLSYIvhR%0 lwk{J&`j)vHRo)v[?5"3E4Kqt[Gid_MEXbcy9)L"$#M}y ץ#Hq0LE)V!C%(HjQ'"jE^ Z뤚EDXQg~&cKXҠ9=gq<+l1щvkZlvQ8dqR_8">^Tp.5iGx'Dx#{DAMGZ#֢+q~OIN'AZMű/$T-'?Fqp##PM3բUM<OV-?G pF5mUQ5cZ__kd)$_MT?3#kE^j^j2ʤUm^}:–˒HQ1)$c/h2kMVeXfVE|6dEՊN֊QՊA֊Ni3$kE͠j͏"|}@ϯ*;T*eJ}n5ԓYEx#|oU);{dqN1\+"T=z r)Z7)RL+;FU "LhL&`S0&gS0B2͏0j06v;GA"LɎՃ"L` d"k6A)ƠѪ,i,ʅcp  'Rzt0d Ί Z S}2#ETf0g NV#ς ʫH8&`zU~M"``/|-俰5`+LtJ_xjm|=m2S|~ߗGxwgᅥA1R, +BTBV& pOIX RLvxRLd\靓} EwNڳ E4qjS'NicH8wN%'EI|J8[J"dA0IQ0;NExy q Rd0'AoS'NiqXQ RӝNSҸ)""3RlSR!g ;m?'E =1Rl}SK [~WɓbI0 'do6`|VE8}/03QD)5L`BNi? ł֒o&rRtw0qKO$ʁ7)&EF] 7)dT "0,S pXfO8Ѽ B yR)"uaJA*},mY`0`V)Cٔ˼UN𙑢`AY̔Ff;3`VّJ `V6-;@DH>sDH0M Rx磍aB* }Hq09R(LEyJ rY/-cIH_s+HtR)qRؔ˵DSN/>L0h )b/y~ i&A ֣FFHa0AS}ڝt$Lz )R,%O)OEacX9aB|jX¡u)B+[˕ %R:qkKH!0FSm2aJFt$H$MF0:H&T]Jm "!Ä*Rĕ }v@~%Hńbq7Bc|zI<)\`"&{'Q/'=)ji()~X&`Z^ MhDHV>M4+ 0] VKw0H0M{#0ԑZʈL;XwDHa0LΑ) h&bG)O `"QqWɕN$_y9){#`.LCY#|yB ia9 zRL?pP4Znm)&oFk@ `XUҲHHab"X) &D#0u}O)&bF#0bKH!0mܶa#HuԡH8&_X\HϽ#8P=O )SbL-&F T&ka}x{v/}#|5 RLmtI%V'B )0NN$Y N-a YI.{RL𙎊o@ɑBGUc&r:rsE-+B F$! @TFaZ\">йYt_b0yGDdFwpwc4HLo҇'E+S0kb>+Ir-:^"0IFui*`&<)c4H!uW궤)ɤ9 _1LypEB $Hx)\xtOE6 ʙb(#/A `X*RIsD^@ܳz:)Ib)R`1[7!0R#h&.t/))viH]Ipٽ2G/L@rDt9L! R,>:\|V])"#a?aحH ؍pE-+R(L^%K>YuϒtǪiC9(#x9ߵ+#!0QR,l)ɓBGŷ@@ \)%, @.Rxmh+Abo0.1?pi&@azmMNK/$ Et.QL[Z/`|Z%{ ݋>3AL)TwSpRCpU8HiKbE 2KkaD2`홐`"Ʊ7;. LH8Q-n4Xةc( (y@鐌B +o3aR)d# 4aR8L_x;ǟᦺFKAQ-dϗ?M6STZ5t >A M)L0w>ДDOGA A% '8g4%01R R0Lp 4M|CFx끦s$0# `IEkN7#0)bLj_sL|HG;.(() Ѫ'`TLI~RL TsCVU5/z;L$6SJ;;eB *&T;. LaRL[: S/Lt=VM4s8oZzWQ]ZGRbD`)~]2QO#Irr&J䎫Az [5"1QbaTݒH8H8&p8G!0фcowM6V\DHA @I`_´dEDG#lh1ݵ? _ir F`XWLajV4iD?, SXI>}GȒL5)"Ha0Mٌw>(L&z:1t$0QRpL;"X; :dži =Gv,A εRLdN =sKs_c/Fa"\el;MWGx)5 RX粩HM<V+,8)aHΣt,Wg'2>U6Sor=)0L{0$"έH8N*)cúmhXj0se/H-`{0%AIFTCFx'#ڪ" 9ug2› \ &A٘)FV$Y%Sm " :bZQF~}-\HOjrռRM^8?[gbh\7R-b(Z=WoJ~jKhlTd7D= §?Tυjk=Sh?\×QhI5TSGndjpϩx:o$1Ex+/EwiUZ.jT/&_g_ Ǩ4T/yuP-? ο*8e_jquTM~=$¯2> ۿd"_q _U"/ b"31G"GxCԩ ]a TD"UAJOAdḶ:-ZuƠtxnUqLMؼ? AmU+YZ&kECjE*✒y,Nq["\ iT U%y&k^E8D!B:ijB,DsS}mXMb0堐,)*$!։o)`ؚ-)ڊc dlY\X),伸)l:(RLL`jR}0Dxm)j+["kxT>~e1TvVfRNIz%\!HQ0f)m" R,SۚM{A f#HQi6 k$HI0ZW$772\bCa0gA 0"km)VOn b(\S};mġH(&N)0KFy"qRLa40ӻptTW+'E Vo+GLFS;6Ooča'H0]7)Cbb2h:P# *vA7:³]c`ds09RLcxNb! RLSm)1(L!bdBGm> FY־҄`BJ+GJ h1z^6))`aKHw`a:%W8H`ZozQ|_?W\j=`"XZPRL@+ҧ``a lRL@뮙pSRВǞnbVA1B |S.1Ї:$J ^"#nLXNvSi!n%Z0!p9ZRRؔ;" n2,c"ǩS ܰ[ק |)U@>'pj|N9R8LoU-NqŴ'&B iq60Rx I0RLqnkA'=f2.h9)lA#Vڈ51(fa"I]H"yCHL ~a% rS=TPeS0%S#p)tTܕpc bKNGLӼ[3Z}R[34f?XR8L .J@` )\~)k)# aJ0[5!Tj/"q G@& Ȧ 0-) B )>6צIۚ`Fɓb f15pAH0p1]|L"OoH8&,Ly+0HsbN'B `X*H0y~ˑBa:ן+w)&3DX_ñ%PXim)*AkgS}L¤=WϙHIl nE$rm PL ´H sRP G辘OٞrG~)T2&=)s>Et2;-VKA1y8G^HHLk*Rg0M= LzdOwS<˞`& Rl?R%NHw?n_?@` ><(Xҧ3~j @ `X<RL#h &)ٖo[&aRZL:i&JE+ ϦL)Γ"` [-`@ Lk&A 9=}mb-Hu/{A BKYALy;$``ja?l}*6&o(zAEWG2³%p0nZDI0QR,u S$SЃyL}d h&`WFb Rh㖌RH#`-'B K8&O8tK'OU) RwAbHHLnj„N;`R؛F;8%B7`_}/G5LJ+bѤ)fus3>A>$)L2qpRm&l*NGŷL 0yRLUc&1dRL!탃2 e tih):+} cvcjVfNG8)t=@ @04Z3_&TS-H0կAe:R 3{MR=`$Œ'EȀCBSވ(|ԥ)Q$H{RLǨȰ3uEʹ`*瞒ʹ5)ۡC`#n|w%L&mHqa#\R87fCfW7DxDQ_äZ()DV ź~D!)D!O2$g1 †()nNG6**hsTO-I8OOKr&$"潪1&^c$ю>{IFqG#ύR(L YICL2#^7HɑBa%t$낚M nh`"`T Ȩ28)֬)\ RlZ()?c YHA~'7(LaFa 2)`"S   0,BE8ăX&H\68Pg"|{DJ_äZ  Pw0 R̢bx@OdI,kvIޝ:u &LQ L3Rp-O[4 -)ȣ{c͕&/^V~- <)`Lam Lxl!)&%0DlJpxD0ѹ\;hazKЖܠ Au""ڒ%]L(x"H0]yRb!SқզL邦D/0rQq<IbZb]HMqdőbG H±Cbit2%g#06(IېmiƑ%Ś'Gafᗻ"|nY- [S;t?GCw:pW,p lRLL;FG`XA _]1ARL|B@ )`"EI~RHhRLt 9bH8V&JgC[1yD*[1yD*[`&2qi &EEKB`ʌ{mA izPan[j=b~0i z#ŊRLק!I)&VAba ɓcLc,H>¨HE=__$H86 0aORX\aH0 R>LFaraeg7wHL3,$g:M^\0=H#,GZi &:7JZS&AdLa%:Շl&}fI%![L09)v yc&G)qRhaT# Wp,TFb`WN-+7+ LnOqx6NDx& [$rY*iu˕"b4&}9UzR-r_h{"m(jyj} j}y)q O{Y 6M0)R$0.ړ;0Յ d!*RLjK{-YliR1,Tv-B3e17dU0 έO'AJaWLR9V"NVNIkh_Me"kx,LFK{>Y)`R'HƩ.L!KƂӻeVkB`x(B-KnनF)Kp m~NW;UN,LObYWV0 fp? EԾǐ"pSEkDkX`I݇K3 E+S0%өD'JR)VDN 卺I0U9<)c4SةۧMy;%v<:Am60SؼUf'S 8$Aų %En0Z7ӗ0Hq09R,3 'E 0另V}N0AǤHmbl3,FbeGIq09R}E/HbHuN2$PPm$00`g'M=J`"P70zܛ=?(&0!3Cճy- "L]0H0u)6Y=_k#ERMHQ,IIݭn )b@^k&J |#koL B ^s 4X!utOIb+#„W^Y„l=&ab2-B lMo0!d B {, CG*}9Fmk(6A%1DVY-0uI &3B`G& sEH0x$!Ƅ ӭ6SWbgB R-#Bi,&/a@ P0Ԟ )&|r$0)o)ioN@4HϋDxOH|G6IQ0WFxqGz oj,bGUVAJ+B`ETI{8L$B )@a0a[S]he-X{r&Pj9b)"!M7A{B LT5F`Ȃ|j)HA G5c (A9Oi`!&kIJ#vjۿ8)~jJ`r!Tߌ" ) p0pUWO8t0r)G#0W=<L;Ld *`DxV7OH[mo?+RLdԕBͿ%9:k=C0%c0yh G@aCHA# RޟhIA =)h! S( L=!ŵkOHA{?y/dҵ4"&a? )Ʒ4'E nѕ'E}{ON &Y=LԊ%011^&`*F_nG &$t/}@4t2pL>3!0yRLLH &`Z]ّ{~j_T`bPZ.* ?D5L: w.AFQeB E5SOS}k4~DU7:re X0OCa*F4040@HL7p;!0L^3VfЁKCH !~4v2/?E0JJ `?LG0H:PƂy Y)PJ}tCu!HPRwhRwYRwVps0 Yc)XN;q3Ԃ2lÌ3r-W~jA_T 161p;# ct?q ߈1 j1 Я1 Te=^:KwEUp("G13t?Fu- 9!0w2@ +ȗW#0=)L&2E2 L L,>}#&`'LL{m'{`ZdPWnm!0zL 0[eyO#dIW'AH0MH1N7S0H`).t &,8'HxͻAbˊH0a)&=Hbf290`"0DIƍLGD5}Y?p<~#!0QRl\ #0!DPKr&XT$%=>z:g S2H+R,jDb6c)"έ I%fF :tz a/<؋ }J`+;'*iM\v=@ &`ʻ?ao`r H0 (Sj1gt0u !ILA nI@I65\Q4صt4E >( 8)Hr /H%G:#@& HemRKS0?a8Lx{"L6S P2izaJ)Ϧtתi. "bAHhc[haj&AH0{9:{xZf#H0S ǖYC Q0<0)Ba iR0T=*"T}96Y/_-0—ᣊp6չIZtV&" +ϧ5̑暑 JkR"۔q:@ )M$sA ~Fp)I9L )(VB0 R,l]&0Ȧ)&JM H!0QR8 L$">5F`! R,IPbFbya:F`)S25bc RL*H* &A=L0Ub&-Nܷ QITe0& 1XA Lt% "QRl~ h±C'LH8uIpP"-jsL TX\ⳌD8Y-3R䑌0@ow^z>1RfĔRLuboT_}GA 6 ,S &b<1Rl)K&J=Ἳ&W$M86A &fs4HB$;TLKB`b> )C3Gl>#,`*dzsy{gfsOFaXے Jia I%A )1t) :؝Gj SLVE7 R"n%0 #J$NGK pvAbIbOO8й\Aޑ [<)~q-I0|ϯgS]p#.L6d{8sA-փ[E-o/*8o*ذ|g☝ՙtLiE.`_U2»y/ZdA]/7AumVE3?Nbװ"VbI'qS'>WLh BLQ#jG&}uSYWXGբ\GqOjr*űUgb|^`m1{lTjG_s7YjK~AGXYF_Zo&AM~y5.+qK$ZZYr/ΦJYDuRvR f9`ZȾVQ=NYub08Nä>Y.l=W<Ôe3LÐDzίr"۩lQ ?ÔB'_7&rө7YU2IGUj3sJ"aBJjDUDEJPS2SUBώ1-)`Z$UOBtJ`߾v ]x0]\LRi^[5uLBƲ=˩ӹ>y"\&>+RL4#\#|~5BRNlr#H`*Ii1RLae+`bT^.EYb\IvL`Ly+-Wv FbaH+m]Z}gh,Hj+!a&͏/6K݃1RG)% QuMTe8D9)XL4M9<)L1ȝl,HZ"` ;l4@kqa' H0`ڼWԃ E4pP_Ld C2tWb)|$BHbT RS [U($TD)bH0ݒ9) M6;o'Et-MfVtZb`GZ_WNɓb啓"`I0Dxa=%l4 \}O;#kX4GV/oTX8)\VpF$arĔjmGjLGJ `b{o48T±eI!KPA`J'H-.*$HL+}ii*m Ħzj():€b#B @E !|5RNr%)HaUVH1K0!c &B  i$ 9)LtÄяl()`$9? R>qRI2)&fz\DxMtcz`/kr:2:!R%LRp(LHd-ҷ+#}+vHH*}+[X2RȮ5X:зG+Rl$o#"\vwx=))bUPX5->VL iLk! Rܑtd0]OrBFBHAsp:Q)igAɗnkR#!9bt)Db(D["a5 <1 pe QWR@#E4 ` Q *K7s8Lw1w(0`L pP=߰S9b 'd&tz%B >)y 1HLY+b3H&EjA `) t7An"&ۀqޒy<ڀBr013`)wh߉vjߡid&G`ðS-+"<ou+᪑1:L^* R,DIJ ?a4% 0)Skx)0U0oHQ0ULae"!0QR!Ib+7s(NG "nIT<&$ea%5)|Ѿwp8LL 'f$*}ONa$1A*ґJaPEx&5L4rH055d1R H0IR[fS.Q/O`f&FyCԃS IvKp!u${R0b䑇IbEJ4+)^R,) K## C=L:~CIlȇlm0س獀p=~,HiWgnK֡K`)~T RLia&a8-T±sK`)c홒;+5+(Rlۉc &@mx6rۮ!"`uuGE- rIb=K`u*ES0lkHT&ѦhcI` ^[pl5^Y(L)[% Ud[4H?a)Z.(3Q=".$¯2»OGz5 Legf&FE\\IRbVM{FL`)R|)Ld"` Sik9]AA $E±F)xwYA>۹h±e$Gr)5ՃAFmRvϟk()vH A iU0`";NKM zu &AO'}XIX5}!)%{]TXA b$NG%H0 ôF`;KH8g?G(H0 5[_4 dIL`bsT[LB`a3{H!0=4`g#e5 L"i-(wvN@ ?`t &B,uDb RȨx?)oRLtSR{B DH8 )&dZRsc\-e_msz$5C# ^#0mN=M`"`P7tEP^0l&B`bشG^&-ԑ)6eEJ?a4\PXM`+1@ L 4u=b%$nKZ4"WX0Al%kpLK*[N;r #<zji &Kk͋NF|_׈W^ù)&J=փ:HaPȱq[9L TD0Bʵd-()ֶ DOGE±H8H8As]XDQpHAME?)dѾ ʫ)y"\g3Lqi m ʊ&՝LZFxZǮ(LL2j;H!0qR~)jAQ^WeOolQSl$NG 3plދc&plqx%_["/ Rlz>?rɓalq8t:!0g]A6Zۿ“aU#caG_kLeg\ɝca:)O9 &E:D0)Rl&NLtwxhՑa:L&` ǺA% 'Ś#=)M|#Epӕbǹ#4?>')DI\//֙j"\CװN RaL[:IfbqLJIToVhFE-T)V.zwRoRϦ`i\՛"ŶKii 3"Sy]v&ӫ:Mדg7(Rl$Hr[DX~Ll_iTnV곆MůRUsqUII.(Z/QOjѼ7iTTcUjq! _g&Ju"?/bU5bԟuT-g-zR-2l~S-^~U=D #9Ḍ:..e"Xm8Z}PG I_x)M UvnWcktUsUVI7k|ȪcXq);ףC^TOj_zp' :_.U[b&5+VUV_ /e~)e*F}ִ5]6;Rܠ$`[yro_yKa敭Y-#Nb=Կva*~ǂv./N;_*RL6S&uGI!0ZbtT\aX,)f XbCp,o)vH~|)v[sRaL X)) *ۏ;VA*J "zJ "5#n5;LOR8LXڂH뒐Y()l%! ? bMI0=5byj YH]< CD)RdODDxH "d! RPRL)9l& D9~4`"p"BMHa0ab,()tT\99L2o/EkV+)da r\&O `XW*RlH0R$W @CO#IS.9ZJ'S0nMgR<)IjӒ<) d"OǥY>g!ᷭ~"p?-qL_v @ `V\e]WMU1(r/moh,HUIt/}pRL[$~:&ȋOXmHi&lH}xT2p !-_0q@ \GLLFD!]Qra^ؼg^S t.QLdAMO%$BaT篚RC@HLjOF`)2)-L400RѺG%M͆yPʖ]7,!ּu3R<)l%'s$>DISM*}Нf,€;{2yғb װ'8H0+I RDG E$/&sWR%Ŧ()Fr:&Jm*AuuJ H8bRXA~t2R&`:%0gOza, )'s+H EGx"LafRBE!&k. L^&<)1S٩G ^GQjB ["&J b RL58m9) _LNd@CHL,X0"s$`ʅrsr%Ů !Ttg/_ e3)^HbHt# RC~%N `) L8$HL$HiONGDIq7q_). [ʖ 5wLt0͏&`)WCS|:&`*fd2ʮ<|*[qXvI_öTdZI&) Rg?Ab`7@Ib$#`r#1R%kӕ,E{nk =݋Nj^)Z)v|LD{ؔ D"`gS0$€=n0J7qY}UN=6'} dH01RHn)c˾|z:"09 [rWAiItīA ߓU@ 䧥1 ^X$H҇W)x> RU2y1 4R.T#tUߍ  ҞWIGB ^处WMsIɈ4;5"H0ULER{W8)F#0󂛊쐈8L.IITτ&LCg&N'U4aJ'S0 3)pL,jT{"\_)%ǻyDIfXؐj=f H0E8IbA o5FO> R,o@ ɑ`&B T]̂4 mRhF`)&0H\^RL$sJU{m#H!0A2"</+0H7&`/XwӃ\\n ^w<A )e_Sg%G %ɑ`)lTKc0gejcAIK L52r ]K`Zߵeuf!1R0jc0Ǧ\f4®RpUr&Уx:s5 5)^ &``IUAEz#WMsc`)qO()LtH`Z1R, 4RL4s)&`_!:M)̴LB`) )bY˞!)>"|nB`=MV t.7A]Sc&B`XUҲH)RjI?~t$LQRm"a4-ɑ`RXSIb$H;"Xs Nb(L;vvIe4 V;)6)&Or&` 3a7y\L[/|ĎAF#<SZIbdU#aOy-L'-:QJڭG髧Ib!ʭ|MH0m85` 05=o)M%_)IX]&`*Drs҈`88L1 C'Ny2S!HfX7cY9)IDyi׋r>䭎:7S-z5|&`rx)V߲JZX$)~V`7Qb$)*:J"nkAE&)6.)~)RTl)vu*8o/&LE|R[4^MHfy5q}K/Z&CaE5 NHrCbUtdMGѧZ}T-Vǵь:>csQ-y8e8l-f&Z,l&__xZ\tpX>&5{@zq4oj2??^oT^盱Q-VG3W-R_Wa%Gw1)S\(ba! RN)&}(l"ƹ7k*q:Z*Rl)&`Zi .zVRlޫTH03%l)RX`azb0=) 3.9>" ?0)5\5f}椘^(H1d  0 RLSg&Ou E{1~h͊V ī&` slHHS[n I0=5.nx7P"jn 7WMV/K&o)\ RZ~]Aɓb뛺&l. l)VO䤨X gN|X7 RLduB4S'Z=IH0MҊVdUo3NO0SV7bKS\⟄R8L=In0-W&`0)fdϻ˻"N#In07pF H'7'#b&LHQ0x">SRDcz"\x((gqd]: 0y<;.T)zaB+[5F<ӥB`&!<7ZYSU<aN )`ּ`ƦMJ^WVZ1-`&aEI0<X,)&DJ |L!Kr:&JŒaŒHH¡O()lP#pULe>ޞTێTȦDpVlF8o)&Ga{ #+`* 5le*!0RLI'B ?k)&U'B qĺt05 gS $ ÔNR8LL7PM# . < R%ņ he C)vR" R9)dbӧ9)ZA٢ `S!!TL?) AcB |L1HNG<RxUJ@`Y u )!iy9 R,. )0WL:$`c?y)aƦ50e?Mš05APn)B )&0Ia0:~iKH0E!>pR'EhG6Nd=`X?' Ծq'Eq'B `Xs!{o:)&`Z8V]KAɳ>$‚7;i c}ILSɥHH0GJ#uj01R,SɃ6tc$U*NJ ))6<^EŌlLIInK&%Ū%)xvs"P_sS –B`ྲL+!yG#=iuzXJNǣe1:)v )>sE:V%E X"yKw>꣩ 1[ONG' +J$K'p|عS.-#J V9.7c)Hc`œ`ZV;g>2~hWW?;I0{0k+ ޽%!cWK0Eԏ>L @ |, 0U+0sghSCHAob)`fp(LaFmq&eO*}b(H鍒|V䑀)>AF:QR"L"IiJN7eظIUEHLHF HFHd)~Gu!0R<ѕtc!j4 Z <ϏHzasN5=l.5J?&B*[ 0qRl`rvٍ:Uw2ȓRܘKoGᙌpk6mgŴETt.=n)))n>58Lհ"&C 0 RػA.r2] P1SYS IbJjI<6/T UVwsl ]P$c+"m BEC"Y)Le6ѹL H褥Ai4)6ɤyHKWO?ӛj#0jk'0S}-(NG2q9QRTFǑI`s闽 $vDMK>e@ a)&H)D$vH0M GH)&TLawGBX`r36m\)&ENZA ׊BE1 )s4k RLHdKbQm2S|V{=.aA5=F=&BkxO@ZݑVrY) &WOKR [jvnA 0novX~!gϦٜz*%Y \$4K::8&Jm6p Ȕ ӬLlQ"XA%^} Ɖn6mHqKr|U PښɣX3BZ~:"*RhHa;1΍!)䛶8 L+#H0mVlKyB6T7o ; DHSl  z\A Zp ӼUdwC+ . Wokw1^F0%kVBa)5ܒ&Ba:jz) B`O\5MO`1@ 1%ŚRL 8-)V1g0u/E~Rز ǚ' Ӆjc RLKb[VC:#0%7y\@ i,YpF`:R)uܽYRؘI2(#|pT D2 /@ )ܛ BRLu2_5 FtUJ"0QR)B`ZUTNGt[vS0]:0cyPֆ/unk.  $l)֞1J!0 h)(L0t=ʫQ@ 9M\--S"‚^)vsz} ۂZSLT'`&% Ӓ˕'[ 0h<ƞ8scNn~.^:YVmTR Ǧ7+̆ڑaZ-MfKs1)6?#_[3ӳDςmp>87Kc~Ab˕ ŪՃ)⺖ZEk&_iynD+BbS`3bX2EWL?П7iE )֦_wKS0 R,)A|֛H8VT±!tϦ`qɑ`,LHyHq07S0 R,>s0eA-:ag&#o[C2D֊)YEy#5 ŦLH RrZ~ت qVJ5-TբVlԛ4.#_A,ZT}jѢkW>%cɯjq ίΒhfs_*qبdoC5/J&*8_awm敊E&nA2_X7YTT6S+tQꤠ(DyŔQTiS&Fjuޒ8KբZUW%jYxj=&{dS-njU-U˟+bEOբ&AzR-mP&˵jq{gq4WբȤu+eZdaE-BQ,tS'GT8V{I75ޢF8TsUR|* 9&Cu0UPű0[U8S^UT'm=tjռ?zU+MUVF Gu׍YFxx\<\_("o}VI+Z%->L*`?yxH1S,&UT0UN-Iȍ"D7l,0mV5LagqH1S:9bVR5kS&` 3S0=5&`k) AGf~L`Zl>$H1S5l &]oJRMDS0Zeig(Bb] V2$H1SHQ[% RL`l,E1kk)j)=I1# A 潉j{Ql)f4fVb$HQZm{" R,)kx)ZA R4Bޚ:0Ѭt~QfRunS-)'uGO+Rt߶zu붂Sjy9dF;+;/g'I LjN `/ nfז`:L_al8rRLZ_æ`u7Mw̗8) UP6&yOOJ&`*"Mn0Tl0u mu}a]:܉Hun0Py~Ah{ǟUKF r}YDIQ0vV` ;e(lI0=ab )=O_;!gikx)&pRmǂ`BjM s=3ܠX=a#O[8xVJ)vJ<кG?Vlp kkt2DH0g@Hrķ":)IEA )a_„3hޫO0Hj ᑒ=Q8ᾤ82 ʙGO )&̕ rCH!0A Sl *5=!Ä=F7Z@2q'M% #Vέ ҉^ɔ xjJ lWpKH!0!~IAz,we4"C^2!E7HH> SMXoEH0IgiGr*RL1`IHA )j)|f/H!gDI=m=6LIay<0Ƽ#ϓ0@*}`@& hR8L9)I- ~UA 6m0ᒚ N*B `"YabzZt!-N2~BCNG%TސQ%0MWS0$Z`u3lf?tZj0&HH(H-.`؞S<; Q }0 R,I-0aE]'B NoV6չl`/H?Ik5U&PY/(F<)*S[ owhb@t r5!pI8lI0s#0yRL:#T),Oy/H0m,ο.s _Vd='ůFj$-2%,#JaRRχaR7 *}ƒފ ٔL]t=W0yRQOIE"S6q[,?y 7 BHL E5aʕBj8RL`" RL%[h0R,Bc${ӵ2!ŖH8> ikL~p R'pRS)&gi]΀6r&`JD0P('` 3>%AUWG ik0{w#^b}VI+)PH?Hr*%AYcc `Jlw6zF :څt0Qvkl54  <$d0!O)!t' L ^Gl-LULJr9a 'G0OE|Rz0`" /T?b"&F)/᫧ňŌXBaҏ"()V_tkyX{CD]pALM&`KS0!x& 5!#+Nn'IAT0,֌| Ϧ\t#0M%\ 0;#]pi[K6} F`F* ~0LE˾~i0&v!>lxHSIVf|۴=YURПG ÎhdLh R8L5 Ldùn >ʷ#K$cWGi'yK+WEax20ߕެ.t.)F(H1[#h]23Y3WM F׉P"m_Rl wrْΎkX'B`>epyrxXA2gYO*ĞLn“?n()ԠD`زj)7t_>sk&v؂ SzWgp~b[ S"gcؼ7&r)8س R(Lg& QC)l8ub RXl3Et2a_ 01)ȋL,-F[L|`!ɣ) eEZlj#7ҼqH|fV7:QG(Ldz\&X) ;+)`-%G N7azYprp}$9I5eAɔKRL@MX1~_ 8/<;$Mo*P_/483B sy=rb- l}O/Fa*|OiN[TLk9&0  ;EH8־Ώ&`J+C2S<?p7)Z4&`)\4MKbD#o'0(H!q~nꯖ2} >&ܫD0= iUA$spca =g `bJB(6]"L}c *L$\PM]l\rQw/6 D&GCDRKHHpY;v"p-o8D/osګBvDOҀPΐ(\ :$j@B  fUo@hTM%-yKO GK2 *+:yv$qNBJN|~}7|}%|  rL q|xIdzzͨs\uʰ/Ï$3l, :֨w56B&SWXW&j[e%RpG8[5+RT\^Mgs{-HvH1S4!ٲX}0=fA{mv+׋^^&`z5A׺)v.j(jB&P ,f嚯HƢ,̸!Gr}/HQ06Gz0 RBhv)Vl)b؊S0 ٬> RLt8YӰS%~c cc +Y)1 R,+z#Ԃr:O`(20klDH'0DkSIK#K'߶0}l)N {ِۋ E~/oɑ`qmX\Ip5'N>}Mdپ:;u-HO~MLdu)Hl߃LUyJ0Lbw)'F'wmNIQ0gD#4jLsiF@k[R&05`>!DHbte%H>ϵhE5iF+~wq:N)4;/SqLSqZ0e/F`v?SHLtK{+H=_-`HIB>aR7ۖ 8| 7$`b-#EtHOCH0m 3t v]2}_?9GTsW' x+v/ ?mI)aVDHmT&8Ԧ8'L<7φ+7$ 8&ŖB3[`&ȸl)V )2K$ QtU />«fR`6;ڡZ~whaF N!M]Jt!߃ՠ1H~hS@L'E_2A5 Nk$K),ﱚnHR8L'Ŧ)qLIi;^C L$1O Z0q IB i]S dթnW! R,Ԋ=hYun%*@dl=,~ ֣>5[}mJ%@Wae|0ap-7k)~~>:8e@O 8%> an4E5a@bf0%ߍPR"5_I%mI0i)))d=%.K@`nF`C&>hfk: xN:'+6N:f*v?>LHG/'Ete-H>)l:ׇ݇@S+Z<)zB4Et8>Ej\Hkw&`Ea'!k$;êa] R|B$HLAH%cuiHi0R"׼}C +$HA֗7p;>)(+rه7=!|0R``9c@B RB`T!aJ׀?at3\G1()@1\3P3()@1sJt3Zy 1fVpG)*F=A8)z0MAp0)TQ5Rje)nGFz;"Ʀŝd Pi))c`c1W!0-22#B`)K$W!"&RP\?|H$?@ In-u-Rj]p|XZ Z Zz! 7>ZʪiDEjY$zEm &cCoGJy6k0UK:z`a |a֘\aY)NʇۆL9`(kʲa&|^LL`i}8j BHbmH%n ף(L 7H0FfA b vx_l[*`bK"Vpl`%9!0QRҨ]:*( 0"`s )&‚kW7x`RL<)~ZL?BH`1H0La$5Sd@&TŽR,;IjS-0&0^"06 B `z'rb0Sh SAPSA S_h7Z1!bR(LGX8A8%c)3 r ;vi&q! R,^H&?10H0] Ape_r'wA hhA@LsSH>G#0ù^b}!p1.S(e/S!HPS8@c vu/ ;05H=cb !OHAam c0ӵ YqÌ/Ȏ)4$-F3!˖0S - 8 {>">< Em :oi6`(D)tJ#0)RI 4У)V=)fӍDQ>ujrz^; tbLj&J u0:WRlࣤu-ȴ'|,wN_ iH6vo qֽ l)Ibe@y”)l3c0 R,@ )&cmnG+q;Z9SR,$KեbtP)aA%S0٢jr#Wra>GnG}6n\ɅR0#46qx%=;"W#03}V:rg"Ha0 1)E'X *XQ*F`)f&))n Li).3_dĀc  8&O^ iUIbґ`)e2?x g~K.h$ŽdP}r8Wq748iN_Їo(LH:`&`)IU[f&E#ŷq_Abݎ6crxGq/Āc}' 5VX׸2M$x)mhF1;Rӱ3MrޑaJåöM$A'-AY6S?*GSk6m_&`j{S0 RFEQ4qASgʏihTrLUS%TN0)ӽVbkU,%ALY4m|LjS0LTM9Uni6S[~S,+{ Ůwv ֯ LdyjHSX*HXp4d]ZX-0ڐ"Eȣ \yZbuX)R*SOV<9)0K ET0UG<$J1'A;i4SLT- y0}*)+vj*֖ X`-qz~gtR8)\) C 'E ?-\-^V fg;OX)& Eփkk@5)0_AYq՘Z ӭic!I05&CLRS# l=5pl8)jZVaZC06a;T'E҇[7TlMŽsHtOA"`j*0Zyb0 "Q?3t5+ S &85Bge3|B %6N,AՁ7{ËH[e!FX1l4q:`yÔN/CH )dx-4j32A w.xp0M` "31ӫI_QR_p)ui&emLt!”GS0tB @ w[ )|h ˖B-ИNyHS؉}vAJ.d&naBaB0/ 6tA#!E!a p`&|ZFJ"?5tjѢ"RR k*B u"7DHA!ؾ'S~SG-96P$*Dӊ()RLI3 B 0 p6@]*Rl5 Rl"-a|SRhڸIt.0X pLCFL?UT.JQ<,!e\)tV ^qV-!n &{6мc-(f0mu [##,?`~`XX(R;R4%B lL]$aZd@jVؽhjs}R8Lhl=dQR߲jz:U &Q`Mp}L{!о3㽮sǑt :L:&`j^GXS~3QxE)4w)[Nw-+Hp:`Rؽ&#v(3&Mq&ݹ<)P+R.xjŬՉ3EJist$0H0LENmn$#ӆ 8R&N (>D`Ut'LhI0+O0`#qRv%, VT۬͌ۻ!n:WYW}IH0t( ) \5-"}ltLa &G iMTPH0{#0=ȀpI6~X#k;)W.CHq%H`)>dH w&`)~i;&sא ؾ't%0yRL^uPR%K |:)Ƀ/8Ewf(%Ū\ɟ7 ,FlJ핔ɀ~)H|䁐"27)|QߍRXe~)lݳ$H ;7iqrR(ڵuGRRXe V0yRL1TI0L| a {_#`k3 0k0y!-pp5N ҁiuW~Z&`0 -)!EHLN^ n[nG0u0 ,S Pdu\}&oDj,yJfst0E2w Ha00"6 *B._!==&σ $ ,+8)x|H0ay[RlrR*)]-=%|Mv{+HLݹ9=!0Rka)|wE_dߴjz:*|c) ( PA5+)7.`V<)RHҨ!*`R`0K` E۵&k`IsKB 2[p^L_ny &J hA\n)V]=)&ݹ9~Ϡ B `rPb<׬~N- 6 0}@ 5ڒ6~0]")cT[VavRٓGmkn&BaRwxklCoC"FI[I~D=zC}xe4[ƿ&"f6}תiE*=!PR,E:G\k6D(@[>l-+UZGS0-2PŽ)>Vp kI0)[6D%iX gT&A 7 ]em`&7sO&D`[l ?9~.(ZB `+낑@ɍ&6jzCT)SRs`|7{}&K. E҂ QDH랕N@o){leKHry.;h)Ha{a&ݹ2u/@IE ETCajoYJ<:&v0)+z Sv$| ŲNbyt=$H_XBp@]KEaԓ@2[\PИo9VzJ&@H5 A ;Rd 8м){mPh0L!jXCIbII\ +ٓp恴0&F(r)&5c&ɏ-(o;!)&`HGoY5\=Z0M6)LmÔP2%ӽӦ8{4`Zjn&>Zw$>@%l҂/DO pB`N+#u ' &6=L{mԩKA4?f?fjR0 Gb ŊZbu߃$)E/]5\= kvp@ 0ivG$)L,5:dJ`رÀC8OՈna)L@ ! @|1 Ƚ! 9Rr!زr& G`Zqm軣)X @.sU!A ž=7-& _jyu&:VHa0MQžcXqF~Ա:fVg|ဒuO# +z &d[qx|-1.Qb [xY>v ZxaA|t"*A K'8y9p xT#R(L=y!JRbcYpTFXbE Rجx0e1&JmSIbpvF1c%K#\0F֨ K>\εQ@ lmv(˲0S}CHA~u~dk$5ʃ&J5H!0ΕCl=u[YS=WO+R,U xLp&7XL'=%Bg,Y 85~yK4A!FXNb5;$1Zl70yR|ɥI0&0Oi[ɧ6}%-N>a*5D#-8c?Fwz}xc,R&O?gTJaUmsȨZH05L55S1Fp8dF 8j)LotGAUK7z6q)&EĊbӛ^Vh0F^5Nq\yЯX0e 'Ŧ1s88LCt RH0MTYgTĔLPX,k|0C0Ŧ3Yb1N 8[rgռ )mfS0LUhnci C2S}1 &`w|]|MtYU+8K5mx]&ObiLk?AZbuG:&"N( +uh6MTW"Λ^We?lrbEQ]rک+6ڨKvꊗ?ro7uVӍx8![xUWX^{êW;9hRcz4TUܞ;0!MvRՍLNBf=י,M%-j-v8if"N[u'5Dx䯹?yYjT؞LV&o&b+?r-,ed< &+S)Dj"_QE|6RTZANlQTOu-:gYQ0=H W!ŋ۹VuVuN% RtPej%}HL]UJ*(Q%`چMH]P-kPHusPIMZ:S0E#@( GDk=#)R,Uu,;r LM]l̺ HGk6kG:3Dkm5ؙ.Qb00uG;vrўlQBjQ:)R)/wAe7_y)tfBaAzJSu4SamU2ivl(wNZ SSZn0+7pTN!4Nx4[iNjPaE5#„_ B6T[I!RR85=I3d: !G8YSH$8=|mK@0 0Rhf#qFH0!$a)5hlB i]`"ЛzH83٦y4˥?q [yhw4Sg_u Z:R8Lca4 )B`J.3)|: :+)r:Kt,}OG(>,HrMH0L$DrI~FRXJ#DI"0p&OO !Ty\^S"A BxVQmJba2}2F`k%)N P>[Rhf iBGoUL;=Z\r! ɣR0PR$[`r*S2S2-!e/)NF$2i&0 >pS|'7{{c)RLͫR0L@@EVryn,3l+R1 R6aFՅ ڞV%/RU*E@LSH|gy l]+ L^"_< D,FX9'𯾗GR<0@ @1|1H0QRlx L<ZB ~mRtu%wsR,ԌHaiMWM? e)V )I3fŎzS#ɦaa;06TsɑHa),S fʕ_,.0RGel\3R/V:T'Gn2vG i!Rlҿ )|_r9u8Ggd,#]>jIbSa5#ΊS4_>[s۔>-zq֩^'Hi&/:1R##d܈Jak !%>>f:-Lru=) eeceVo[Aw9iVe[,s:~Z/'d|jE Lt<#xSO BI08L qp6L{q/p1iXӹPH$G]o${hmr(l0()v;wzxMrڨ)c?p77gv&iz><AMё` I}L}`:aIbŲH&&0)H}X.[/`čL3B+HD$Hl @"7i&U+<&6Rh/Vn?I'`RL>N GV,ǹ&&*xU Rl襀45lu9g Bgs_tJbu q@VWWNL.7jhC0 !3HA:QeL nVR)n|[J.\@&\ LM=a;I}&[rl a/lx:/FX;vb/R@ BBa@"Hف_<5z;j@ l[zf5қ5+L!Ԁ0 R@ 0}dg$EMh5́@fڑ9̖  Lc;Cy|]0:(RP xX4B @XL2[ ŗ\lc0R+)aGI[w[SLoIAZ .EX_[:tr[jA@R+HaX0LmN`J0Z*!Z)icBaL y{r"40mJ#TަTDZ9re &(J.9Hq%B`b@(ajI(`)BGPb0hr䖆-sS0uQ*7݇)"H0 d4;j#Q)RJȷ%1 >Y1#6䝒b%S95 Gr|C d죤X`&0S<%k-HbKH#lɺG9Mm"1Z)t)en&W p4/yk&%K>V0+h<#L {Yq]zD SVrSy{K_Dfyċp" aAE>Nwpe`_x`b> Oox$EIs#(%%F1zRZ:{`Ix1|1yv~=/[S0Jcq)t8oxasuKw3%h-GK):cf}l-WرyIb1XX)?jEX-QGoANh&GhԽ Ybs;\q.yag<I\)Nh֍#ŕ\LhgǼ]Ib|fk+Z껩o L}y0D g_l&yKSֺgM2 xՅ B(H#)Ri &E)Q+RloLD#Z:E/xT˥)kqթ 678o[vb;o){ͯ#-)vPX|ٚ*[S-|-~LTAbuo &E)_TI] T58QfՋjuꮮ_؋^\D#WX-WnR]18b%?(?V]qx.rjٚo+yK-dD9KM6kupDR@<㯙?5uF&ɍ4vz?3iLݨI.nAu^F5L=_09$q/pf=1V-?{/nT+ofSkcg|E]N)`LN05)7˒:u8Z*:PMLaa$HͫJiY7EJu /uaVuL{ZPk+HYNrcbug038)n [5lϮaM.Xu\Ɖl`koSKo LQa6>5lQbAa;5lKmXkضZ6̪35lf0M71|_)5lְq[ӗM~nod4ARveMs6( %#(L`ѐ+#e}؀I1R`-L`"XSRLmb# uBb]kA[jHL)L(0Rhڸ}XT}ؚهDDR9ֱrMHQ0&B 7rFaV&` hĜ/xkcN@ E{Nhl5j=1&{NL_)&@H0QRlE$ Z)V])&+p+Hg%qK{y)\9Z!0_K 0_ . H0E" H!0A"S^a@HF/vp$@">c:$#a-k a!'00D"G^HD)y  x!]"#<D@IAژMID*'GF ~$EIJg3B0M-ira nGrSSY;<)&N157fHAۂ<)4H6 i%0q@ `m)6d )Β4UZ4O6lڧ05 )Ȧ$)23Aj!-Vxh!g`Od\ n4Ac|iGǑa2;D0 0RrwIqMp7R0swrwMpUd M )$Wmkهo[B `⹔8)0 | &"0gSr,x0եzYM-䄢yu㶀تDR{&`fo&R{{M]H98)DIL ECq[Hg?o0̏pKs,|C) x8%)1|lHZEA_8R8LS4HA5!0LsdaXuUYT1[U&^L ϤR8LLa*)TVrY&`S%HcL0y)-u}Cޓw@  gud_ψKqV|"6AbSVd/&$#L!׽74?&|Xu"8`Ÿ4,9G !H!0RKAbcNi/پ4d ()vJje\Ji/DB Ɣ;-]eޘ.ǐ.֥K4OLmsȨZHa05LaCtb6m A38RLzѸY|hȣ|ٛH#,4pGׁHf \&&2}4+L4aR6}M|kVdOo]= #y:}8R,5DvM"%߿jz:%pBm&B *=L4#}^\F`@>Z@2‚B`"[[WB `{!% &O |&&aB$l)x<>v&E͕Өn0>i }bupzN1J)s j=H)vȀVט:) x1.6aKmz&<ʦcNoiF t=}T.[,hR(LѾ0)Q%H!05Q)'/D^2#F`HJEĜ\ٕ\ZsArWNFa"%w\f 2ӹ6S*M|kX-07iVdVo AU R+r-w#ڗ3bPыIhpBm&E'4w l&A_5%Wk1ZPؽ#gO@\ Ld,^_<%E0œaz0K8ߍ'ô--?[ZbsX-=DCaRX8)?܈"Jsl3:k`UjS0h-)b#H{q&`*ƣ 7{4xbFXfGI^D;GiYؙ)S;n6O Zm-<S0Dl[Zb,o9[Lc&p*K""$ѹJIJ̾g3u`zZ1oP/ͣ/E#uy&~G${]<ϷLGW x)_:S0)Rl*HPXr4Vbe2qb";WAeJLD%qEqѝkV}"Ś^uMDb]w3B_KuE*6Q]kER}/WㇺG ^qqlD:u*x=W۬XPWبKKuGNjO|໺ [x(6G }E~U[^Q}x5nx+e]}G<#$WD1+.k ya<`<\ƣu&ˣJIjPdZaިL1c~2?;$q_G:I%y]nm)e*gݰ2r[J©s}KJq徭/C:W6q0QH)s[̄K`ʳf{kLǪ(R,oHPA.^Rg"LsRre'ӲUb Ej%#,))Z*RLؙiY; UdRՠ+Eu)GU:۬)KpuTudS)!"Y%lψfE$0Zh}k6'A L] R̺ H+MfK#H1S}A M`0A~`QĿfBbìe Ekޮݪ/ .r#r69Ly4SQ AZ3+`ʢ|,q1p L8`8) :e"jJ䘑 pR۽XVY3Ă]s҉1ߜRBbZ}vQդQSe죰rRLA ڻ"Ů)cb0]+0&qRLL!ʝ=cm؞pl:amQ ){6AFgJi1Sa!Ns%.S(HjII0wL {llAeBaZVr`rдfL 7kF\i}(HԾaHQ%;Q6:+Lq‰m#Y$Ⱦ=⻤\d8LyT8Kq FF@ mqa¨xHHlYl8)6$)wPwHPoiLwad-.L}>J)&[)tVܷw/4"&B OÚxwOANla/<)& aH- d0\Oi\B `΃:Lq.)J{r`t)6Ӑ X$ogp95HP=~Kr;2(L ijOџIbx|L#9XM ))n?3}PB3[LL 4? xD2[PyRLʱG&(K.^ ad ʿIae%D9LgR:ֱPRƌ?+פI`V`LTF`BL<|f 㻀i"H}ɴT_~ZFi,UlLH!0|iS\ RlzSu5q > d Rߓ`:$W!y dwɿ,`/+`&`jdwImisXb"0Q.I.6+)`a$[R8L]eՒ`9Lqe & )&Jg䑃)RH0ܥE}oi~:'t pQ+RjAY$O3")aXW O2;DrHK#JX @I6_X {t`)rL HLC  x̖ pҸ[NHL|B ,$ jP+tؓ  @ H0}2Qj}AF`)dV RLbю2)L3[2E-j+7"8) uo &a- R4? "P<"lAAnك5$HcGHLdVt{'[]OG>WX_)L렖5) H,Ղ%hӂϐHL {B `;"6mpڻtOgu&c-uԮRLDibIAf K$[!%N`)L!C~4榷i?>\lDpj) zHaiHg(RtTJF`X)$H"G-DH$.3zYw` B2[  3}ciA8(Q|#tGA -WF`PH0ynS ,/LrjH0ML xSk3)056)tV\uGL40uU֩,F`-d Fvvv)E'񓕗 3Jt"' R,)(DHA $F"0 68L҂ ;"3uK pZ!0આNlr6 RRLp& S$/OK"Ů:\t3ܠZ?S:sv5FX ӥ1H0:IyeQR@#%. 14:RL \V()q[LG>rKJ0UWS0Ly}!/R SL:1>ϠH:qhMaFEh)+w\EcQ^*"FX ;E]r0)RT+ؚeB}fb o᮷l&EMvMRR~ڷ*-sS0u&uKWWB0oi/9Rh'E3Ӻ4Ǝӑk`Fby&`nƣ){0eӜF*W`ZF>Nj |MHKsiD)vJd48Ls-:߲XU)Hj -o[9)"7FH0 kB7ϐ\2"ֵz3؊+ؼqEAR5:g"V?G\vݶxԫ\\~,2Q㔉x]&qEھf"N?nc&bC&ՖWT?VEafnkxKD_7i!Q?굉QBg4AE]M^(oI]2ID%]Do[N]~XeYt+%: r=3u@07)xk"z4{3NgQ*9fi6ǽJe|I$NU\JMc_G}g1(vx\a !H{6|Fj )DKR8LSČaK0!?R8L虷&M ~)41SRXL/`ƤxD'е3Rhf unQؒLiYOGBvMlh.\nB }8R8Lʂwi}*)*~+RoՉUHM.ؾwNDGlwdcd:H'LL ()v )ti1 ɻX )B2[:.d%N  GB =>ߛ)|'/a#p^t$BG`r[NG1RpL+l )( L fџ 0k0yOIbMGHdZI+0$Mi&<)&SM}xtv (bP~Q3( )PNhMX)貌tYDIǕK3[aG ('@+Awz##DI#]GMT- (vc0m Ȇ+avƌޗ\00/NB|A'2)p"%Y5TR'E䏭vL>J.se4H05_'Hk.Q R@MaȢ)a eEHLH!%D)]f2).(添 Zˆxx)Vͤ+&G r}ؽuI0=SK8qN|@9-=)i4H00eJadG, Aw2#kCbaKHL зHF`NE{̤^ ;9y-)6--HLO>Ї՗˼0(AjA@< [yu0d.%%3x lHQ|{0fLol=H->>R_5 ;?YSt2HBL>ILd'#DHA[9A0#' 6}yN DJz].pm|LjFEU~O&^#ы_ >|L+J `&BHR%B &8|?] 2Lp00ٗpwag/7H0krߧ6R0u`e&`j" i+RI{(IYo^5\=I;q@ %y!nGKnG%=H07#0QRPSJ.5;kj6(1F!~̍DIA1bLͅk'>p$#s!ݙԓ̛:b"0QRVӾ7%AbH0UGr %oZ5Hp%icGJ`H&`*r#03`q6$ yvXJHa%Ef&qNk+9LȎd H05p%l7#0 SדV1"0cLbu+ Ln9[+I}XjL4djKLѾ0ed0ejղģ;#yRLvu+]() 92=CfVrF`)&W'%rRLx.?݇'WӑvPS \= 6upxRc4H058Lˣ#H99R L}kַ67/^9 bS+ֵ[ Ni03AkF OiRlh8) sa)]oji kdT 0ĉ'*H0؟[EDJ e.[N `"-yT>g8)T\A"`*jS0`9v2 #i6&PsU9%ET)6)) .!ԅx(Y&l s09RL(LL1RSRLۖmrRLLaK?wjP p^t2l=`b;ĉvyFԶU27)v0U9 YS!lVrHp̢]>`)[ .`lJ7\lZ&`pMhfkv0 )|S)r J="ЖlH0,g؆&ϐZĥ4a4(%a&LLd (LCcmᯕ rdS>b03!1#\VcB `"WLB~n)6x`r+CN5!w?#M})lAm"YDK0OuCf-fTH6E0GB ?uq"Sѩ^R8L<խ`HgV~,B3[]o&K.Q灐a/B3[qY1Rhf 0z#`"0k#H4R8L#'R҈DH!0m[0! h )Q6k)fA9Mt_܇~S'XYL5 ²h? ) XYRLuXYHDHq0섹'0kXɥ:R=!-WG&(V|q^RRp#0 R|N' \)CrgyRL`af"G3[t&;rR %Cb`Mo.#`տ+QEHL.[/`~It<nH %@ cҌ.vT0x”u/P<0 :xI3[@(B2[0Vט󃒂XeLnJ.؍0Jb7&eA)H#4S{wL7PSRKU%)Lg_b(oBTeѓ"`G)3qRRR6g.#1h@1S:Ի_Ҩ=)xVrYM)I0i R,ͤ&J `0^`ǯʓ"`j B$,r0}:R2_R2  \bclhBJ.L ~K#]".J.cj /Q7B &w(e\<)ʜji $ >j&n/`·l&(aRX D!xFrXQ;R8L\L{G da+8Z`VuLÑlw)V$`!0!`& R`/,HJ8$A &1 R,t'Qݴj&&f>.?LޚJ"|f3$)*{)tV\d7qSF.V棯) Rq0S3Z|"㤠G~GHxC9L(y0@ )^L Y[?C3}dwcm"xBp2CR+bZQ0Le~d3.1H051Hp&%f&Ba:ɞ$3iLaw˲u&%O&3 \ &dH=dL \L@R8L eVq3ٚmSk`eް> F@ Ht H7xFQL@vd#<ab##{ւVxq-B 0)&`">`DIF$ Fg8)xh0">6= ɓ0!0^t~Ձ ɓ$/>ϐX jzQRƔ+K=ߨnG$l)6DNt,!";N=_"!$hf+lLh&B3[8LH 7>%Hx!&B olH{vbmx `0A #H 4r Z0zz1D#0} !HUX[W H0R(`j; |!p){4r`8N lq .0&Fa B a,,3ZNb`f R, ´&Ba'#(:RL TLu.vAK+~c.N㴰 ` 0QR ӥ!3 )Ʃ%9bRb0{v:MψTɑg}k6uH0MIbi |/Fq0S%Hh]X~sx7"&Gme5&`J'0 Se~-1#4&uKYH0j ().aX>ϐX LBPX`4qR/ROc&=׽))Min<1itM4-GZ\|\&`&).GAʅX. )V_Ox,)6S>{uVo3)R_ЇCH0 f&G]=QI]畈-KA]QwS1I|(E^]rY+Ց\nY/.%^K~?+ߏ㿻FoA~୺G.^ q՗"Vu%k1>ՕH$#_+9GMf<MD9#kQEƣL3]hRs&VLGPuD,v|_onQ+Exj3b68e"$hXff䊲k>KݤZE:WV;&"6&j 㑑xg&1LˆlJ*M6mn)1a鑏3*J6ɚZTXD^bNdz v/8)\)VlUz)vL 2S<V$)jI0[ RLLNbFb~lMԏCbΣ n> RLԀh &=hVBTH¬)fZ"`J<`F1qDHa0d>8(NB `"Mcd8LHǏum&V]#ԃ!E3v"pR2S6;(`"=L _)=8Lt%\0:/[u [}&&J9O i鐠)!RLM )s M )ДB`Z0AӷtFH}Kcq82ADHLmS`"Yq 0QR.i0HkS D6!”Gg&EւV -7DHQ1'q1śI1RH(C搐Baz1qe m׫1'&F ^waIHjDYJ)U"/,)lV\c0vAG@{Wص")0;z g<!d@CItx)!0R)'s'_<t!\n ) }b&T#%f߳je2D ~ ?+_&N?Ne6GJ.KHQ0͕K :LRLVǎ3gC3[/ g_ 6:e`r9O$ =fB `"NG" 0f ļK &h\=&L䝁A3 猛8gAg>!%bARr OOok'@]OHAa%|tB 5xDni8 OJAp0A3M҃$eU1(LCFn (La|?߆8 H6(L 0F J.vcNHL4S sj|L(L00QRP!$&G $+Hk)ol„`}x ҇A˷#)6i3\ؖ,R0E꒨= - <0#03X)XJ. 0 E x)`aJH3&`x)٪$>,Wu!<RlS}t|՘)fjKL>Љ(g"R828L ů|e Rl) ځx\S#ELLL!3?Hup.WW)Vl]C O=fP 0QRxI͏ֹ#E!V Lp!H}&H*jC$3b& YqQ G,l)6WDHLdB3LrI~6rW#0a Ӈ6%A)!mJdWWE `l4ӜfrKx3$HPD `X();z 6H 8(lTXI0Ry4ͦ26'*+aPEOl]X>R#-L2[`e8)(ecZE{Uص%k]]sS0>|$}bM"DIuP{VM?#Y1|!0gǠ9 ŲLd pv#$!C)Ui&HtDGH v%3)Ap`ZV&`:va@pζ%VG3"6 ;%3v.1 R,uUHIm26)d{ DIfr\ΣXHR0ށzETCVO +ۦY!ٚ0;3G2 D6ւ+L-AIݶl95wc 0 h҇))6EE bJ}]<TIl|Ǡ7Hn L?cZ ņd" 4_ O +9p{r$c*SnP<0yR ņ+!{B `(ڧzzn#0  >܇CbS+f)AK*-\=%L2H}$Hp&;W$K$?RxyB ^s+)v Z@ if'z"D3[V_ɉ 4DOdLyX##aqcSY9NEf+G[hz Fa>z$){WM?WO9L}aftV\E~;bZY>$Hs8L \PT3L;$iLgN ۶&``Zd`c+B $?$!oyɵ`-@q6 =09:/H)mUQb%0QRldVHlTZI`OId&B r|H4L !5lKc0Zۀi+W#0QRLƤbKG⑃b˖e0!;vF` _vf)&O_ Fc0'v}X L ku=1 J02+N(L=}1)2N&`f#1NxBuכbXףdB`/Fm)Al)u0S2}RLӛx \L!ŧLj5&%bs%HiD9B~m &Eńl=be)v/gDB`Q@ Ƣ߿ 7cQA8_rpxb;OwG# +4;@ ɑ2[V ֫$p<)Zg|eٚ>Ib[Eo&Ae}}.IsG{mLe!HH%G/pC-l)vK4FT9#M$Hly4eXFcPX)YT73b$LH`R=&" Ŭ)VzS2^b&`G)-΂KS0J0)Vj l1?ǘ R,.L E׫NpH=G+Rl/%IIUg&8 bKQDk/"?Ӭzyolr#1~SX+s\+"߮oQ]ت+o%jb)[,[X})oèO>sq(:"TOAD:MogA6&3EM~$DH6w&ql"0RDojDtxTx[WZxX lMGx3EP~m.-t>7)ԋ4zВҾ'EEJXAJL1S) eFS]G94WWS6aVd WՔjY(IUSK`څmy]%2:*.iUDUaJElwpe +jV1)KpT2^Z0urвogJ"a1U nJ6m Lf?'S0&*)juG0U'S!cbVb|r%?X} V͹ Nb[Xb`j:n&o.՟LYE}x0ՅŸ 5h}[<#JS%HQ0E'9%ec UKh .pfmSXڈ/-LtJ?L`v)&O"d)&̧e]4 t )vJ&im^u [zh:8 "E/É׶gI)U~{R8L})i@=u\=Cv[DY[t[LbIq09R㛇Vj)zR0x0R8LENĀGc&H&ҭi $KH S\a<@HL{:%05)-$Ǐ 8Ld)=.`R-LHENSdnt-괮V[5\= Iʂa:M{B 3!4W&`FS0m )V)4uO!tXB3[HE!0QRR@2!0Xs!>ӽkaɮW3!ÄN7P0͠٪gJ<)-CCvd0G|0 RA)0Oe`'\ ůyavFp5c@ `4+vGf 7"-ysxAN`$4R{B `"&R *b\$RZiZǖ-)&A5LċAHHLLDAR$H"&Fp-|.|FbՓ"`_ЇN>5٪gH0E9cJX;`v0dhG1^|LxMS\ βXߑ0QR R0c&fsn&EHZY`) A +lnaRتz%faI0HQ.#Ѱ  X_P%9KSJ} J|$0%k0ZbH5sIPR8LZt1Ս?eAAôTA~/Lx%5^ =L!m .z>'RLpMnt 3RM<hzTns#0t|j A믋g%!01RƘВ`Qm@2]șb&g : ΅e-BxGZZojzz8L $ 3#!0MnHHLrګ)NIO##`|'H&E)a)&`.EHAlS.lN7^) l`UP0yC"Ů!f@ )؇b)&EJXUa-a6n'r8` =y$ %#8 zD[w$0 HQ0JzT;-Z` !0LƓG& E3} 4@h4$B=k)X i:)>̌o/>G#0t|_s ()EI:Uψ EqLn✷!H1gD5B`" 9Ny p0u=9 p501R0ѓY#=70 R,L#%ņA +9i)6@;:? GqGHG%pփVPǂ)*W#H0W-δ+y&z9]iep!ktCjIϫpJ.LnL tpQEP™W Rhrv^5 tj Z%\=)Lu\bҷt)M Ҭs7p0iK>psAj-?"5`*vA l[ \)V)f @#`Gb\.]f! ;u XY M+އ`?5h?SaRS%y`fEo$#Ĝ@ iߛ)t\ 8v(3vLh$IbZF=fHeH{ RL(L \懑X(%D .Q&BHAb}/l'sV;UXv0SXԥl)֯KbLm28䲠"ź VɓK.+w ņg'`ϫL/8L/y摒bc.o/.(6<)`։>l$,&5he8L0h\= PIe)YqH0j6U 8 b͑`RXWKS0~ )>5̢q_r48Ry9U;vSXL"2AUKAi~^M<~-d &A8V=݇ʒTY|ՠUe3L=[L`r"` L89)lGİ5)b'&逧 )S-SPk^vto|ZȎ] u09RLaA74{jGa-1RrlNH0յ?[06fgBb)6j<ZykL 7 1gZH X;b3c%U t2 (R8LckD@t XGJ u=%&S0&L`ooJ[AfC}:F FU`oxFSÄm* 75!”HR8Lum?X1[[#ETM@ !fN잌^א< )CR;u #„ Rx皐v?$~P>zEZX6!>QRtfYCKHV-|<)SY XRG*3.pl5 ι4RL GZ̶-kq,X:pRXe~bV;';IvVKB v !;3)^R8LsU^o>~Kq|FF<[Rn 񌊜R8Lam &@8GjPnTXLM*4B 94ChcY`"ALKCGJVr 0 R,R8LPI0XHa%"^cie eRЀIa@KA J.0y+)ګnus`: ]B 젤Z Zbs4B`j'S0%CTG 7'E4$L0UI"rMR6ʕOyRL f`(F.N `#M2}rLtT _rA`ZޔHOc@H}Qw!"ŦcVQL3|4RRBD0k?c4=)& qx|nz@)3F;2}tQ(HjI2}'~60RPr>S9)֪L@Ixu w$=)4;=#3CPR"!]Q)Tmc$NIoM ZᏭn/^$b$H%R׬ qfڥ10n{h|Hi%&3`L1n&?n&1 LITԼӛ| ŦN  EaLu E2h~PX@AuD7h2ynaTM*HYbaքDI'9*DIä'[x2̂H!A B[H0^ϤxȴL0gJ@Q@H0ks1HȔf J}j)*]1[غ6=*uT Esj\fp0 R ) 8P<%@a gcΗׁ٥P ёBaj3@?"y7[S.QX)v"O.~k<";}S$!}A(NR ..w[ʨR(L` &BJ.Hyv/0" uJHAfy.wQ) j%)>YGLGq%0r(z6fd)i' H0%d i R IRL C;kH0 ٵ P<%F 9i v0L9i|mIRL:lն HRLNnpR H!094'Ih%MS׃WO?#\10h *sEAMej{hБ ]6;D/r]!Ha ҂k-Hb+7;[I0=qL-)tzR|V>]` lI euH}g=R"9npHFg4f(A gm[ V]3PIm<}bs2@ )9~2ps &0GJ.`äHx6U-l&0 R,܇[8 7go9{6Bxc c'(@ qw}8&wRXYA @!~iDA W@ mV&` 7&JJTȰE;Yؙ;5H0ŋIfm)Va)9@˪'gVrٶ&zJm.xbDI4Cwo|K.RL~iD@ JF,U]p"d)6դz%0bNǀ2[c@ +QP<"k4Ζ0q 3"Ŗ-!3R3Vrٲ^q&GĦeh##P19V 6f"Qo3 LrYHߚH"6eMl5%D"udآMH).tFa,dr] F%<+PA2[HH!0 _G2vG3)_@ -\OK̾{вdUd-@4TIX^|1 Ŋ/40eӣ18>Ltt"i1' ŊIZf/R341+N!չXz &`E#̖p6 ;}g&E[n3ˢZ}O')6?Ev/ItT9)v[xs+W&`RXgæ4!H1ke4Oc5EI7aXQfQCMDL5LlYb׫yYlH9WuHJbIb˥uw1w~aHK'HTT~m-G~~Q-WS0U)v{͆h= c]:( ">JsE(U!W?lrbEبKvoo__C]M~O?|QWtE7›?nU6Wq飈E4sk`(D$ޣ(*V/Ҕ"3׵Dtx<~+vլ+70D,^tANS&xTK9a:_ q|)s~1f݅Opyӷ8ˤG%K.M&j<#Y)(*ԊS0!uAL>t2a)QeLJe,-˜d|&v)(`wA%1,>Z*RLUVS|)l/UH)B<>I`j勄X RLT6nNJmy[XX/QbӛZ`ZWe‹ H1k>p:+*N5\~j)#.XaڔFcQl+ܰ&zmKBH$) Ae" ӐԔ`k hIGѪ##„{lHDžPR,w3,y)wDHa0Mo8)vi)N l^ )ba]O2} Za q0REgFWis`IAor!ET0!ÄdHo[nL0aPlZMTLd8LP!&B 5:-c-DHala)W$T R,t۔|vR6 !X()j: B 2ӱ-J;@š.R8?p^ R,)bǎ:Hd>#fNقBGJ2[He B ;`"s[XtHMWGEUSQ-'RhE bM"0R$q$"#e3z^Sv9UVr#0aZFaz&`>kIxL2}3[~J-6$`J m]?񿹥ʦm}.8)ȓ`J]oQy+#&Je aC8.b)~9W{B4ٺ.DHAɑ'ӛ.R'0 R+d $Zeyۻ-LHAŘ- |+nr*#A 0x%mIa0_)VFJ `u8`?@`4*$=)jm &dXeٚ>:;#0yR`3}4nVZXb ZFR gi #}X֯gR8L0 /M'Ca't'I =ZP0[05)dERhfݚ1)Oq>$CZL(Kؒ RLL(&j(ҥI}лt R R \ac(R6oÃ\\GEf53*R)d&F=%0a={rնv.xԀc/IS/L-8'`z`˱˽6 /BSV )lRPr) u՞ACt|9WT{GjNli]~E.Lk EZ_RLI$v6cHrQbA6$9R Ha0c0I N "%3[HXfO "0QRlI@( K4 HqJJpH$ X>$ 0)Ӄ֯g$l[LxF`}Iw$,krH0 Gg[z"2 gG&uL|!# ϔfCf \&u~P\b 83ll1)IRhd q `5;C S>\xAs6 $Eɑa8׆+:7t RLIbqRL_ 9 4$MAMo`d)&0H`Vm0HnJuG0Hzl{ Ś=/*=HNcSm Zx"BRLCRؙS4줩zMϔ) 1I 8 DIs-Hv\|ȗ\LHˎ"CBH1 bHa0Ha0gS09CLcZuԯVYH>LkHa0UZ* UBb)1"MEnYD{W+1B@ CJ.d S2SHrRHf+D})3##ZFQ<"0>%Ra]\|M|(7"=)zoUnh4Fc)V9h%ү_=$ )))Vԡ6S-ɴƣ ]0S0pmz&`mò]zL-qL{*A-7N]|1ӎb0Ip\[ I&M M][:tQFbɪZ~Y𲹘J}XsB%ҨYW:DV[;ȳ2njn~TQP]Ȣ,+~Y< uLM(hl0uSI<(Hi(7*U]\{0[S:,LFIe)!^)&` ;S0]bX(RLeNU+`:v^LaahtGUaV\[.$`Z6dS_.’"ŊZxFHfL$L?w'= 8)ZkE RHQ0-[0w4Lak&`Ds09R1sC+·'V0T0b -)RL]fc ykYH_.CDR"EW3B!MJSef!ց! Tu=)zO Vzf–9l]X~}8L/} ''+}Y"`墅J.`zmLNxĶlU c0b6@kS_.c05/g$JN47vd0꺠`WvvB)N۲0R쵡( W=(%1]@H09) em~"`s0lK6A[J%y5S`bHMc;6Ao9)e;4>7h%ʵ%0v0;fPRsLj')9[/|M ]Þ0CRr '̘0,>0%ε % Ĝu-'l]jt0ө b ZxgD;a'3}-&QRD#"FH0UEψ>ĩ)ThjU"iȰ #F2Q,B ) ,Ga/SK}b0ScL)gwB < F_+B 7;Ng H\Y[aho39XuۑÄcJ όP=&vZ{33 &aH'p0&f(lWLشfL RXLl-+}) 3)qު.4&A0)Rx#ȡHx3@H0B6% %)La#K<7 = ?R!d&0ŕ&B @ W/zRL׽C̓hfkZ>"_$#DH!0]LH905&o9lB%HZ>)n&:šn XYFuXĀ3[~/xI0 Г"`~vILyI!0ZdB/عnfWwh?fG6  ;ӍM My# UE֯gBZ)&X_(`*zO<,xRL3e` 1S?L~ ##Em/x\2,r0"H?cRqOί&0ϯ?!,/OZ>Q1R'ETK#Oo_8<@a ˴Ǻ^a\M|)7LHLL|"nv2٪N)2-0Aق"I&>L||$riwTF~7Or&_Se=e_Bnɗr^"_"+'EĘ@ B `"&9#C?}JJ  )(-^wZ R/&N <1gɯ\?Lȃ@)g=X R0BAojUX$U|(`6m,10N,hT-+>s)SE3[I@03@@ l]kH!-P@XO0!T2x$[ekidj,[CغvGcA z$7xR$ U WM?WOVd(f v2ߜw'tC{=č=kEH_.C)WV_=hxF(d$Y+JfQݎV&V"67x4d 8fjrZ o R<)YYŰ+HAf 0ytQr&kX0ppSu fmF/4-J+ĺ<p0"VrFaNJGxt/e+H҂Ibs6V;B J.RhfkU }7'; o4R\Z], `2s"HY(.7 Z>B`)DvV}:]i.ԛw>$Be)ri tJ&`&a_阫MB~݉Wb'w5y~A0< 3ywO@. |ˠHQ0%2cREb#2oMG i̷e$Ւ 8߽D09 #Sѓ+ln$NH!0n9YQp]RL(CXxD`.Dz4>Ƥn6G"0ѯXh:ZB`RW$a* #0 #LX51p|` ,!I$fF-HbZ1@ +()@N%c3 6!2E3CoiL LMEy㤠S8)Vw^5R8Lx,.مi odYps$/XbJ-:ت$d()l)VA # ?1I 0QRAt$ DR\7Ba:&zKc0V`WOpEnACD^$CMI7EI^ H0%;3B+['_-Er hf`Nd/R@ )ŀclK}6Ha0όu!ؘ&)a#HSBA\)L;׬xŧZ8 tc T"HaYAst8LrbERH`4YlD!Gc#TU6')Kŕ\P }ɑJ.Mg&B70z#/Hd$3!AGI{˃۰5oab66Ѭ/@ z$W֦o~}"R* uIV}0%ᒅTd":Kq(L "@L+*sbf1'K%#78d$F/DDw(* ,,#WDb@i`*_p !~$_84=iH H@j@\Jo4ȵj- IU-/ uy%2DY&S%盄)z#9HyDž'/…o!R -wMt :Aػ涑tL &)Jfw-_GxX<|#Z0U@+8<߰|G_}vC!L )=dOdd6߰Kٮ)| t(|t c:| tB W|a| 59=~cg~& 4 N]^ɿ~ZəJڻVR<שg@^! ̱]߁&t&YbmT?AJ_RW6نpeC'ÕրXc I(R-)@c^) Mʏݒy.g)Ժr`pk]S L}5Y&XJ1 00,6!mdf'Wa^ ǐX+`I;`'5G#RHY# h@ j&B yA= @jVRr VzZ-le "E0Y P-|K<8DVG NC ~Lo[D i3z$)M5ݾTs@ 5@ 5ؖc R@@ "%f43ϰ L lSSabi3za`u-!ec)>uci)M$0UY4)LD'{o-;R'L %iCMDJÞh0h#$R8ٲ>>R Ayd'<":V/_ Цt2YHBd1FJSٜsh HLL F,GH\8,ZF:Z 1"B Qd Q 2RHdK&k[?)(_I3 R()Rh¤<[aYvhRD`)xb^ZHt&"%ibBs#Z&KOcڭ"ԷcKtwr)R#Ő7"7&B+5΁{ce<߄>>v / &B`B4=iyP?<!պ&ߗ ;osKL2>LcF 8R1S[QοTjVo(9 Bk8 -7Q.,L 2 Bm$RHl#RVL l#<#PgN:{.*>O`z)t1(iDH `uc`]6)!Ow^{clnS@_ՂIt`)lIyrOBgDgH(ʣy2y GsGgx$miԦkr'g"WHI`ڔ` 伎6QR `B-0jy;\`bӀr) \@ `B-# )l HA SaMSgXUks@᱀"^,/ &')-|Cg,l*Tz 0Y"02$yfm^ WHFbD"FT.bn~"R '!%F|Q$F<R ' <g8 ZBM &?t%Hxi_e:Tq<ܱY"%6`"  1Lp)*r&r &rA5p @ &r)9^Rfǃ<LH1!d6)~\rΩ˩)e,QKKI$?$4 D^v [ۅ%% Bp ~p ~*%%!P. %%!P.lq A 0g= ጉ4H!9 R?-Li~ [+_ٮG&Gpr6W@ ~\.R \.`)Iy䧇HL$g:T=9-3&&_Pg= x.WQHmZ j{A\〭Hᄉ OL yi\T IyA \@E&ňEy|('#WWc86 pF!Rutl+8ZxVV}`:8ǩCH#)LL٦٦S 08 ! 5FJr!PP#RrA\G(y@ Z?(DEy$D`RZp3,RZ;M͢kME Cjnϼf,Qӎ&`2;Y82A` WݚWHV\kL B½&@ \@ `"<L#<L# 0)k<2 xgnp^uN-ht)iF?Sdb,HL匢QSQjG rtQeSIm!߅y@PKbe<LMG(`B&yrA &B}!`) yDȺF#_GybfZS > m662YS 1EImu)5>aR,GHL6!$FEJbQ..\@ `"A0!HQ *HRP.#RP. Euv%Z=w0]cקczLwo14a|q~=HwL?-?Nw<~ߥ[~;?;v?-Lw-n߻LJ1Oo51ڇ8; %PҨK7;xU(:=d|n!IPxğt˧t}]11ޔ4_ynyxſk{zm~ŵogӸck,10+orC [ $ 'Z@|(8 3}"DZ)qXaà>)q]Iu|] B\a."\ AӿN^w)(䲫>ߥ+atkM J`-ʷ4aJꕐ bh3j`-ZD`ms`B!O HTJ2t7"?d[p̄oyf=1b#@ƶbwV&"DT8'p5k#`),j|$0^Qoզ?L"XD\HwhR鶌#Cr)<l2ffa%`"HkSBJnK0H1r8H SMF'hd,cLGɑf֖ץHkW5rH!i&wCGdi@ /|b)[! Ie&B"H!jBBvyNP %@J\S0B춭އtqd%Ƨ-Aw#`),jV &[T>d|h$"2" H%L DyDU9O{I`)Q16񤏢&bB. ^_d@BO4T uMx[e4qL\S0nXncD Œ"0I`) LO}.wJb~Qk|E?&{r^V"0!xHtVɊж>#~ &ٻl3R7%eN)Gc+).jVKJH) JE RP. 0Q72 (9)(V) r?#)}ʣ1􃚝OL~C=w)9Ct HI.{, Q0ٟizVi[M4mYЛ1ۚV̖~I`\q~L +QG) 0Q< H=$R< < xǔ#͗S7u5Kڟ{ HAY(DJ)}U z1¢0IXjL' RP.p3HL "\*H H*9I&D;TeQHU3 R,\p-[pe=3BSĈH5v6JHaQ+8SC䕡 HA$RA(sG<ysQ#(C\E\r)>W&"4N6_LIouoM0%iFM/lũ]\LDԴ; oB#|l`)Hx#<L"E0)H 6#>ί ͳkyߙiBZd٘tIC\,FR,@V eVd6 l[!m(o> 0Un ݣR~~aT~b3ߪ\ ʹ٧S^ͥ)bCTtRd~ZVf[{VFVM GN Zh[ԣbUA ^ Iʍm[`:,) 0ݎRL5 4XW1|F= )nR4quÅᬷbTMt+V 0ufGYv[~SV)FբmMLD=޾#&1ԉ5/H d"_Hia6)_H w5;0?<}*0]#ֲHݢ|'pfi)b۝x-T/c0fZ H1h9[B*Vت;=El|P{)LXձ^`8ptePnp0}1? 0}q:4T3L}Y,Rl]n20Vo`1=)v MY}ʐBԳu&`A l]*o,oG 1 a !f)l OUHa^RQzoj4)Z_.cV ld9EE`JD݈X.<UJ:xäjnƁ! #nG܎ )L0 4DB4|1>efFp 3'5K[ o-f쵐\muWj,ꨖʼn2ьׅ I&Dw E lw` 0ޠlR8`mU7&X[sGk=r ֖z R8{J<}LzVbK8Y)v杆g#e7jbۉ,G f0qXii4X-o[ A SsFT"\Y :~ r̓ ɛ6Þx835)L L-):H!&9l `MZ!-I,)-M4M,@TÚ|^e'R5M_FA ī7{{iF.._-)L/ nT)8L )6DB#Es2NZy) |5 a.)R0Ӈ3,bڼlWywϚ&`RX7Pz90}WVIXEc L}0W2D4)iJ0v#dC~f8{9CMLX9R|%2'@d)v2&޿}Z%bY)%eL )?{"8y T)0 K0<}L;T]R&S{k$ |țnR0vh`r0U7bRK|2K#mC$"7j͚"_cM8t )uX؆DObްʑ 8܎JHa Rre >CpPH>%HccZ.x )L*V30MZ-~3.QP^.͞^̔cQ8! @O[|7}l9%)QR8DOZA8VY{vMIp7kH`Uړ3|PH>S6%P!$)Ly0VXV$ 0QX]p+0S7UTzţsaO ;A ǣF tnTdIf#G"[0 ,gF ByRyC$ni* }⋺ *zu4 |R)t>SҨ5;8yWHB ! n@7`N0eo4HaX/|bXaj ;ns:ЛJY2*\Okժ%b`AGQ<(860!x00L@ B'E~{J8{0 {)u7z9R&R8o۲^+g8gĐQT_Ȟ^J/$TBŻc#9۝.kiےr0)p ̳IH cV2-H̿)Y40wuv#6 TFD'<<`9Ϳvez)Oe*be-11j*mBgYXԈe@ ;PM0Q?p@ UlR)Llb&\C.X  .f`$Jb&\L}ȅ  \ͬ.0ipEHLBPD=~VVD/G`:Nր?gͭdEi@ 0:)"A!ŮEeaD"`jjQE \Vf= W"FUbL01X#VJba9E]FD#(UwλnVu.) xw>E #"!]&{SHLu?@ZuR<0"J: &79Rdm @׽P& C ­I':VHk.J29YG:1"|CD86 B[< "H!`"95p`TI4H`?HsQ` @  GTr|Nd Rp y;[Vyr|_V)X2XXڬaF6B]eLx{dHkL3&T= Ha#f(dH`jKEH&`3R,oRlcO.>}ILfEnj5 a^lhǴjUA &uYA]Q\>k@88~`RHx3 &Z]BvTHtv2%) g=Rxs<2>ǛGS}5H`J{>å1)ejLHklLhs랶jkܸNLJqw4g0dq<ӛZkCW1(Xލec׫~\Xӱ2&d\%ӫV%vRZE-*]vQ:K۟T?ThOm*4YR͐ 5RThg7I B?z?R ;rq't}D%*ݞQJKT .Ϊe1ԄM`cU)@6Mb\Y$k[z{+sJ/7 i4Mg\e7!W>M\}].jlq.j6dBm6d|q{oB$F:VIwn^$<3qn+p)Q-\lTDdꏭ!(YzSfW=RL4͝a?SkQR`{|pAiUp({3| ^`J;pR2Z(0^!D`*L)Μ3#>U l 0-@L?kV )*WS!&B 0]*N ) LCaL` Lܖ#f`* \a L֊J(0AaKLU|É[("S`3 L ź@ȋSyMӛj0 0ū)0* T #H9줐bo@ "t;E[6+RYaxq ۪Nr#`zԮ"q{ lB4L)6wD*G>9RhMf焲t給HQ3(Cb4VFAf80w4&HNXS7p|qUHLt9NG&kz7ͬi5B <#'X0tRHdHQ`:e&߁}nIG(bnzB=3Ut{8^U`ժ"HKRX}P؂g9Rg-yh8x Q03? RR RR xōr0=g8A ӥ3Ъ9CJfR>)RC{eVEcrS&Ύ)yHi'GR(*{jL@ ź|)LD&HA| a(EbB"4L0R@dڤ甩))9jH"z7kKNZB)&"K@$")pou,Qg iT6@`$HĵƘRcHa!1p05;eL0 A S7cLr9iC9 0Ub9r =A S5bf~me?5`H D<}E+6 \#A Đ U `HR<|KfI+Q!7o@d9V A kxvr^wC 5F[h.Gr 0 jKw04 r<)#*g>xE .>\% Ҁ@;54r)`*r{VIt`˱ )ydzt6$[E !U.)Lޫ֧ )L0^ {L#F.xD g,w&/Գu3썗Ӥn8 mg1¦u4pL^5jU )VQL[LT28p 8S| ppH`"HA}}+)RfG8o}N,C@-=Μ|Ax0[z+05{:D bx`̑bOqsn{X-HJs` jK&XHLsJ |Db٪R8 Hq3LAt(R&f!  89BO?)$l =ϯʚ^**ؔ/ )n'Fet!agEhp2z64w.B# jhˢ6֜V픩ū>=g24QmrhzvHa'Cw: C|MU>92& |K )M% M #`")o i2Ö2F!Ś2VaIa]a\aLf05]Z_XRS` &4UxVQ/GKz R\yLޔg<RxvT|/'è' 'VO)0m )a|`$ 9Ł?DbA Dg'e0)[]T'z+G5ZfOgSIx 8}] 0-IR8$)L )vTz][`bV m=;!rR0F_=һ0)ԭj t7p:CG _2{UUc Hr@ŝ: )'͎bTb`Ais`P B=[sgLa/żGc`RXȥ-Mܐ"L(0RyVbX '>˖!MwiP{X){Z*D/)bvd;KhA[;V幼J;70C=[@.C.O|HnC ~Q6v$f7S`:e9U.n$>g{8/^5jմHrLK%`)x_nZ;K7bg7 nGR?H`F#`B;K 0͐0sJY"=0ȟ_ceg,RH>OV@ _ΐ0Foeʐ8(2|da,Iag8(LZc"T FDb H!`} #BtjCRj1ma 1I$ف7p|UULDL&6Y A!iC!KEÐ`!PMl\4! 줭{RDfxNg؜?a$Z"~*TTYӫc')p[w0̃OqZH-տrY"A Dv)L5T9O8w}XߦmNH`5FP0-^&b}F$bY'S|c[|UURr,*u[E֛ηO#C!_s2&@\10 $b5뾰6D`>v a>UlkK #hGTd1ƺWQuZ5 \0-bjD3(X[02#8HeI!.zTF4)B<[ňH!`S`"# 0eo|$pG#J@Nlp/c^fMZYAI-2"C$ΘeM;@ן1ūx))R|LiۚP@ן[co}{6 0Oc)_Fc/0eߜ<L )V)_No0۪UYF&@mS`:*1M8F՜*[&װ1}HnvHq`RHGGހF5ksgLA{4>uH`j2ͷg 1ý[`*cxUUBeZWHgk4&':7fS`ژθКn!7|3&{t5&Ѹv&QHܘ\&'90fWԇo$F{V*R(RD#*5NhvJkI!ݧB,~HJT?S&Z7ܞ n>R'nx3.gMnvJâ蛨E{.5Z*S6萴uRiicB^$Iu .u&tFz΃u[/Ƶն0?k&rR7moq s1&UcT:?DVM0rm LcPq): R~ʸNg2SSݔ_9vb)0]b0p 0 SuQ 5}* )UvN )V@ֹ 7YH8w{*#{upV@/J>4T[$Vq0LɔS`:;RL)lM'?ɟ~R şQ7A nB]kdҞa}O_h-=LRZ)0D׉;&ƅn,[u{CPWE_ aI7 G)3&}@Rz-Eӣ2R8JLm.Mdt$UPTzyoWY*Gmk2S% nPEi[X5|bȑ"ԜMlQ )^QA0?R8. 0m[LA 8`șWn1oeDZV4zGn:lv)R"Ͳv )ԳuOр 6j 0WS`VSu᠐bך!Es}Ӗ C PrWFpufYm ) L}._SmZs:~^m R-::"639ݧx%Ha`£uR&£uHa8fgLekA lw V9ð]ޥkMRb'jNZũ}"buM*NunYcig4sc L`L˂!:%L) L C"H`Bٺ&# C" ,4wԵ 0%/jU݇ʎKoWD{j;e/QZ6,T5w)0)`%E X"T)+I`" %`J{"\.XI<}LCoLϰG ֊& `:f?_ԪU!zyjs}9R ߙV_ ^@ y_0 4L)L_ -00eo/=}p5C E0)Z!Ś10]f )@Α )_~tZU~% )&n x3Kο>kɹՊuD|C#AØ1 cA Qx0 LǘlǘGS|w#1>)>{mswI$l?k*2&/H`&_Aݼ%0x<%,2?M)<|H _`ClYR֜R_gK {A ҙ#&=v r }t$H`~trU1! $€D0D0DSyvI)#' L$OBk$HG A >C SS€D0)LJy  3! H"KZ*ˈp09)LiNiq0u 1}Aٛ≯S<(RZ3F=[}JiGMi[##s"# `"M7H7 ))Lu=mvVv )s"P֑r(| 'Rgk=7vkLpQL)]}%fq&p6/ m[$q܍)s=) v&_/h)j͚Bg}BH6uܹ*LIǚ'M'VY%CZ%޵,&\{aCGwo )v⮽C HA섺 A D0) L< sBBORïʞ^fMCS.b!nM@;mИSdVZ\*SX% syH#H#0`* % ꃩL@3 R&R,o=S g%639Md{x2i*"YERH` =~*9L2,z0BtD{ x0N0`R)K`ƯR9L$#)۷#3G:@~=B03rGaZH∑aEB z P^f@ʲs)LRh}MaM8 99RgKO XLxƑ03WӪ) iHNbah7 LOFȫ #A (0a3L.#:n)N.~m5k@¾Dރ@;UMmtOYl"=!Kw3& \n0Q>&b)L)@'G B.08=d%!XhKp]zԝ$mVc7S=L {WRBRxKڗxĘ*y'SF[ƑB7G=GL )醑5W]0Itq #Ha!>5&]m0L)03\6?l<# p0=_áѪU)mR,%Bt&K?ӹTM0(j$lX#i@ M0&& 1ԁnCi`# v( )PztHi4tr36SiѦ̕=}ZРPdX+سdEhmnLgȐbyG>2=C ؖFc-f7r, aT3˧)0݃Ù)0E1ZdSȩ%[F =Tο华uF4yw`HAP\>'#:MBB.<[Gu3|WijB.B AriK`"#6bx\ư3<ӱ%΢}0q{8Oiժ(-ؒoB}b̘vGnLɲژlL#Ńyց 8xx0ݎt}ACiilL{H$>5pV3<>;'ŝ1OKz4JV0auL`TE:bj L]bToYSU}LI >^M nR=g`J65ʨ!oRzqm.x=[v)R-bfX!Ҹ;UXR,-OgjU4Ba q4Ut 5qUBAUHS)̾_MQ7lzJ6z|TƵݷ&4|jr>j mRop~0{dBomppS|4&TujrB{8ڏhժZ0a.$6L hD:H r6=o3 >B!E*,9P 5$JbiBD; kZD`ġ|a!q^o>UPLҏuVP#s8vA(ѴDĔGNE^k7^gV/n&"80ϳ1*:Z A Հ;=M-Ju<[s8ď^WDҶ:DA)DS0 zƛXp|3k߶);=MHɓ"`+[Cen4l}`럂'xH RLc!A"`!6qf|A~_>c(ͬmGB )#0muhdujObnWJ  )6לN9<)I?$և$h7I0u\ g#!4C߮pH#·=}nZ<(QBbUDH!00V"֘Y65SuҶ:VhN!\]FV@gEMF' r>T!k e-|pI?MӝTԀj|3v z_-"`9\KY(Ɠ"`&R%eXp|÷+oe Ba'CIaG.*R)ezNH0T=k"PNTzF+/pf7usmJ3뫣A0 R,J`7c/ܦx5)~&Em3!`)嵐BR0'0yRLap2 |m՗fؓBG4s8˛Y(mCT0yRh`M4WC1jr@;{B l= L 4F`r5'9Zp-wxĎ\Ά ]wL:Ä0QRٻ+C@4xAA zpuˉ8U'^ ]OA bw0uP!a&: +ˋ()vH0HL:Â2=)ALtJߕ:`{Rf%JXyRL)7S~HE&E `򤠲T]Zs;9ZLt3wzwVܵQ5qpN+2|7 /q0]?EͬK^nuh wR%7cƙ.8BƘ僐U|!R חHKRcD`Yg8֤8Lh&`uG"(`$J)fuL Lkq'_== __ @ vD8Lkb)Gc&#ô=@vY8<"$>t\']k&BIt(QEGaA&T a/8 "БHj#H0 0  "0'xRL`xgt\;R8L8L ￐aB h9 ְs#ۅ"0Aw"HLhHLC=z8LD2 =LЃ]=WS0 *P]ta`ρ&%JLTXhU$Þ1 ?=MGZ#0R])fB `@ ;rIwr9:zG`RQ@Ib#0 k3G$pSR]Q'C)<2gu;"MCc)H16p[@50 М@D <)D Sxy"+2ӣ!)SHN U>9 $L:JIr}ՓQbGLie@ ;I7bRLi g& E5 R,E3hJ7u$D逦&aX~*R)( `#09#p0(Q¢,-SES0ՙ4 ,8"|8G-Fռ)v ԾLR`ԂcY+`:Ĉ)* Fb WkiՀLCPcgjv 1՚ՀQ 9/ɰ"EgV.Õ+4vA(Лͭ ŪIb,H1-9M5&`J7i H0\ R̆ H^`kՀ,HQ='] 0Ӈ@DW:.}dSDIbnσ Et&` ) ߖ >& ~|)y|)|1oA>z})y >&U&Ib W`%Xid)Δ};ZKR8Lq<֜z|^Mt68pu S{RpsVĭ*=:70-sȪVeQ$,`SH0ƑG`|cA 0(LxndG&Ϟԁ:ٖة;jVp)Bo+M2:Bʅs8f+QpR!oaYK$u Ȣ0="ӣA) )d`B ?)̄ӼDL,25m`eBsx]QD["B l<'@ 3g05#VcA"ΞaB Xt_#UkRLCPdR8LеoaZh:S09nrGㄦQDhԦ0yRLsk L#9Z0E߃u:}|ߢ@4 0 R)lk߽ۓm f5.3<A!wTv&\0E9qr(Q"Ms;R #0yRLG6S 1FB ]=L^$G%~2HaG.0ԪAbuxAZ;*|SqW>T"0Rb)RLf8;?I[S 0 O~*)`&߉%B#7͓:&"`pIaz-&` .;SM<D@ 0&E `)0H0ae"""*}ND~-2DGs%2ÄsIi&y5UF3Hq0)R)֜[3b no0a@6; R,<)e0\#{U}.!09d"J% 0X٘)LTMB[B aXX{`>L8ZXF+9Hq >LPU7 RNf8 #!9jDԦ )@ &`)JPAfق@ ;t&xA3)HP)v_vQ \:?35sQ(QS䂧&<0) OޑR?-k#<y<0?1Q&<0ᩀ O=LxJaJ S~8L{-o?Hg8섔 p.jDifi@I`RؑDZ~b!0g168)a1R%ޑ qG0q&l{LL 0啴z4LOnG#09ri[}MHhL RL La0 $<֋pAuF`RLB_EH0oC \0ݓ J:Ԁ dw&W0 Tm)_9PV_%B#/J)aI 3uOaa>YB`)d xGG`)&'s0#;rY8)6o[)HPk2< R8LK4zU%J DD Rln)VMإPj .:>H0m L ´HrVJ V߃|&`-=m?-bEe8?)v 4TF` L?.p3 nhaSs m%QrB` )Lh Gc6IAI,8 4`)&J() 6p%RL! N22Û)tǎRLdTV_%6M#kLԏ`;i[,8bHۄT() G'O9xRd9ZMPjR R,tK4Ӥ<Ăc6xR'kX H-Azk Rl:Lxp~?{5yy֤Hz~4b3Vtҩ)&k.Q"b\V(ڱb*lEqbgu>+yL5*HƃMuьyf-wkZ*v4>ՁQjRdÒR72ElӓIxd7{: :> Q#%cXs##`bw20닳FR'{{^8v+ mN7g]UNr YX2Z"{:Qn9)o$>0uax.Bgj=MUɐ-+-r}C ":i:deiZΛ1S?RmG˟4cY>C WEFz"5}|Oخ\w`!)lB SRO =BH`zS%cRh#[]|w~H8R 7+[ᖴ4R|i!z}'GwMp Xa?4Ru@ #E`at>I"0UbLy 0IZ=4R%^> 3^X:^x6%~\ u`0%L5Zb0'1'uBO B`!yc GBM!tuC xa?6¯D\Oĉ uT Ha!C6{|H'O>>6b>^|^JC` Z?%E`MW8`"#t'Sxauxaz|'\Y0KH4R%3R #1.F RHaB gC7 MW 0UHO!OHnLOy@&0KHB -O:Vc! iL J&y{Y!]aRXFXB F&s8+=t 12ց 0e!WHO?:Ӻc0=q=̾}Yo_K#B BFzSTz!hkW+31B )dRX%@GMөB ÷n~)F Y7أ\`CW8B4d)_G=;X_Ѓsց gq͓Q`~ߌ.0p_>0YYL:g:j>lT]'fhf> F )[#=GH#SX(L+WΤ< 10!0:x)T٧:օHB B <ip_B };*1 0ega-1FH>&Y, &[{Wks ۟XPgC` SMc C4;1 3*x)q"st$,xre_%HatG!>v*!XkH9̊sJ,?dga0%LZb0Y-@mf i lLGzaaY`w[ر}6U6&{)nMHa)9|> &ϕ+L(:6ԉt,L{ 0Ŏ 95t{`0}L0@`L<"\Wa0miu!0`J|Ll#120RǙx\AIa6MB 3:Zb]?Ÿ’MRHBB<KHoF [)MHĆuu`z, /Rhi0BGL m0y\PGbgY), Dξa0m4oL4S{t+0L@`:RW8A`e#^i#>F C5vi?c}VطF rчل$ȥ K6A!v-hLoB0!0`JO,ΰhL@`jݵ!F SH!6-m7?~tidO_fۼCH4Ri}6orEI@,l!0`J\ `!do7#-& )CH=R-|+t@`GX:һB:|j )4/ :#J?Rhi0BrوX< D`LhLcLF )^x}`0eC`ژRH!CHa^+\ L+W(5w,N1R8B$ >*gy)ŧ0)'lT7>IHaބx4!16&#[ )CW8Ӭ7t-oر2XM^+f)) lLGڱSHGRCH=>BlIHaVXHaZap+-F {)X/$p D`j9\rHP'B)R ga0=M~;iF Rh}`0iHa[B cF ccN+F {cF 5L^H!r! Ifo]RmuC_K?5%ϴcJ>%kJScJOe;$]1ё>|%ciṽB\R -Fv$L[!M;mH/+} HCRBu-|G!ߴ6H/2ҫVrLyL⏾ L1HiU)FJ٧VHY4x4~L0C M`m_^>f )#6{H^+Wf )F S$SSL~5هt_ܦ[)L.hᏉ?W #.#Mh7˕+ل)H#E`Zq L3Tnw#;3*!6#E`) ))Z- +\#*!#=b0%!H\"C” Zbg )3 )VB )l~ѺB Fz4ѭp^[ {:0BO{Y\3)3Lt_L}`nLPiO+Z ):Ӭ9g}wW\^”:5Z8#0?!J LYL`/a0moL=~m+lPSH:}6-c0er u5Zb0͎u) )L\6L?Oە{n2V8`1eWL{HL٬:r%j )4RC?b B UV=n%օ]Hg))H!?),0S02t?ZdG'HL٬* {ʕYB [SH΁u`0ޕw,:6 #0;3*1 0e:b0S08 :|%is1p-WdRX%@)R ԛvnwE#&0v!0RBlF .F R0ߛx,ΡnLy3)SK nL0ZL-r%v#lB R jF r+?>rda%O! Y~7RxC +l0B)R|02D\wOg3Vagq O ?j Dϒ)\[!xb0;Sc0=08oRh:cbMHaiQKH0W4R!B:ip,1ΤLӭbî[S$f )Hᾄ)[LJ ǎ#}$F&"1v/5h1x)!Oy[y"`:> +LaW4ң#0Eb0+WZ).i!ЇIw KuzLD` i&JQxfc3RD &]lB UB #)dRXԔ7WSa:S$B\p%@YB 4R/!)l!P7! @aV5B` iV86 iҘ#)d) #[\&P%Hatiلf#L[ 0 B uRфaH6=yb!>IK#?HpSgʕ>&P%C 趉[0@6ӣ!0 0&S|V )c0=in► n-HOiSKFʕ  l, Į8#8vl-hL'RHa0?0;L DZy> 0D`j4Oɕ+F B })dR%Z)!F R&c"&!،6-->%tF9֛o(0pi[F ZR^.+[Bʕ6űc0eC`ZSKgdtH }00cYLoZ?19dL OP`Nre21Ă )B })dR免jya# B wpM}|!&D\ lY< )A`-B {)0Rfgya#B -O>ي_n[).@Ya[+W&PG.[ S-~)F R{RRvH#VyхWX`j(+ȯ=Br%:6Ծ?|-,nm;S6*"12 #0M-L iXP+pٟʕ†rGb0 "2f )F / {)nxa!@)R !@6)|Yg!ŷϕ+=nl)t&,!HBK#Zx!!E SM/³qN>rEI!|r*BBK#CF لV )F 5pO~^Bk__annSV 0}\2ۧĜ!! )T ) {)nMHa6R0ѼgS_/_ee[b0Z8+B[ʕXPO1 ) *%P)Lg3fPSH0BZC3R8!ZaY s\)`:S$gDjʥ:S£#0Eb0 7nZa.S6Y_w{`y*W)P )D)d)aHBK#B ㅅqo.+L kp³߹WO|\лO# )ߋ\pX%@)Rh^xt! Cj>ȦZ3±N'7s劒BwgF R ل =12CJCHlB ->~ og3`bMsJPOyxLfa0}r)ÎuL SMLk`+F qb++W\=agdC` 0KILցZ%LLV/f_}W3r%6rWa0%LGG`luz$B R ~X M Kf}I#ZRXk0iVsJ ue%#12lwYLkOM/48W c>h>p1+ܻ~S|\CA*ݿrtH ۿ슓 0B`j4B LL`+eO)Wx-e+_t_\)ln]\`f^ˎ%Ӯ;S:0L-1FW8ޱ±)dRXec+*W()LriPKH]p臐iP `JjZL#0E~`Jژ*+<0Q\QRgfR%@Bv)H,R vN-< )A`ԬCW ){p;%bagq$х?"2fa0ya!SOsؽp 0C^LL>&X$Vr劑BdHHa s0c=b=0v$ip G^j>&9tY2=Nr*$=㘒6-5I=X"1%/RSZ_ o(co"OGcZ~E;YH }4i/k"Br?ym:sWZyqcGSG:~fBR?Ε+vfnPFJ)#1 _s0?S ^8j /{uo#=Ε+1?fnE7R Sc݃{^K`Eb0=lY%xa{BH‰4]#|kr !E`T 0`@cmHa!E` 0 H#ZB -/,0+JH^{G{ |\k&(}LiS,=l@"0%@ !En0P ޅyTx) bH )l:i6\6Vkm)f6c-#c )D)aMHaoz!_|ҞaBNlB, >ʕRz|4)&lLS3حWRhHa6!XF *n_F {)Į?&NuNtBM!Zxt/la9Բ- ^8v0S. G:Grp ut0z 04)vgC.[UL-A`) DSL^xS\ƹreB?4Ќ.@)Rhi0L S6YL^X/>;B ᅛ=IswFNN)ĥ+Wb)i@'REL;v3TB H#gR CjX~xa#|Z_948b0to8Vs势ePх(m@E7X\! *j )4R]H!H! q"Ɇ40^Xox߹kX0=o6o+ӱɿqw yB x×'5Z)DnL~+'!] ǐd4yBO>;.i!ЇB6+>ҬR p/+W|)SmC ;g 0Ŏ]!0t4:S>ްOSttxeصp #t{ʕ׷H,;H\a/ )_Y_هل~bvLL}xᦅS F pdc]zao+f֥0_F [ô=]H! {!{@C6!*!xG] 0R&0p)Rhc#M#ݕr S;&!B 1h\qj!0`)v >IHa&免R/,JH4R L/ttsrJt2R!Ц bag뱐+NK =b`ʆ &&E$ySL٤: 똬 ^F7t\2 )PF =ba0k)H*S6ULzQi&0+EjQ C H[Fɟ+WbN$H!BRǚ74REꄿȾk}ayaifxHʕ+2Fp-D/u[, AH0ߑ1Z##n,5#S=jї*K^{$Kj0 P(z: 琁UkG ΙfZp5S ƽ j@kT mG\5?Q>5`:W 5\Q>3@httM G@jY03@ji@lg< :0_ׯV$0 i]8 = 4]yqDGPUHOQ:=*@ 53۶v@m`?v@lKMx_}f IENDB`vagrant-1.4.3/website/docs/source/images/steps_background.png000066400000000000000000005376201226132634600244050ustar00rootroot00000000000000PNG  IHDR;#WIDATx$]%&뀍 bޚI $sk˽>\|$$4!:G2XHڽgEUuuW\sfdFfdUS 9|Yٝ]U9EDFG=W|WOwDk"?͑"Fs3˸Fc/^x:뢷/^x 8xŋw#/^x&^x6'po4wLӾh/^xŋNo/^xŋ/^x]xŋ/^њH&bDmƯ~?/YuR_xŋ/^xŋ/^xŋw6^Z5=u=@x"-'qKԝy_x{/^?{xxfŋ/^x.kǫ7j^x ЇM񯏛s醳/^;ic)xŋ/^xŋ/^xŋ^C3/%@8;6aMUk/^xm;xU֛/^x<|ƋwNo^Gŋ/^xŋ׫^9Zxxŋ/^xūS0["s{lxŋ/^x7/^x=5ŋ/^xūS~{D/ۛƋ2^xŋ/^xŋ/^xŻƋ/zj sqq/^&FQYxŋ/^xŋWc77j^{';? 37Wio /^xŋ/^xŋ/^xŋ!o/YhOg7YL_fWio/^x:ŋ/^xŋ/^x'z xŋ&}0i\áxŋ\޺O xŋ_Iŋ/^6}mŋ/^Yf\']|l[Ë/^{xr:2z/^x֝BB6^OXMl2Ż/^xuZxx[l‹WoIo/^_x^ŋ[&^_3ŋWw/^xǷ‹/Euxm6ŋ,$^xMŋ;zꎀLsIk[e{3KKo^ŋ/^xŋ/^{-gż xA%5@f7Nj/^xzNj/^xŋ/^x.Ë/^xgH8ן7uoV3/ŋ/^xŋ/^xŋ/^xŋ׾qMM+OzA;;yg}x?xŋ/^xٛ‹/^x8xŋ/^xٛٻ2?M/8k8+Mŋ׳ ީ[|7/^zxZxxj]}GM Qn@xŋ/e{xŋ/^xŋWi/^xw̵D*ηZ ċWeo/^xŋ/^xoFŋ/^xŋCo|z&bDAFe@xŋr}U|Ƌ/^x]s}Mxŋg+7=>gu/^/^xzċ/^xzk7/^xŻlo/^x0@"!}ŋWUoR/^xŋ^om7ŋ/^x]̛Qě‹ޕx9 V[$/G-22;L}5;laWao/^_z-x~U̻bE ތN`9xx]ī76;[io /^xŋ'Mx=F5f}ŋ׷^׫; NǷm7/^xu/^g^xjMŋ/^{uxŋwޤ;!vǷs^y3xz/^sIf^b>MŋO>2z=oVC/r!7G*zxŋ/^xŋwi^Wqxŋ/^xŋqW,q /^xŋ/^xŋ/^xŋ/^xu,@ǟu\ŋ/^xӛ7/^xŋ)xSxŋ/^~ 3`q/^xŋ/^xŋ/^xŋ/^xu5@fۛ|Ҹv^o/^xŋ/^x]]7/^xŋ/^xu5@宁4'7xNh-ŋ/3^3^Z>&5^;>/^Zxxe7W}xַ~ܸo4{O^ŋ/^/xxŋ/ޅIƋ/^xzxŋ/^Q0:n6}ŋד^SJWo\o :^x=mŋ/^xzċG^oTo~ o/^xU l#=퇽}=7/^+gxme>|6j[/^+xZxxeR-@6Waŋ/^yxŋ/^xŋ/^x]7/^%>ŋ/^xŋ/^xŋ/^xŋ/(ċ/^xuܫt ,j/^^LKdxŋקz9z|O:^xŋ/^xN6xFoެ*^xŋ/^xu.5@O7mL7n Nxŋ/^zxŋ/^xū/^xK>i:/^xZ/^>&zڛwﯨ/^+zw,^&*2ūn>>NNj/^91xuNj/^xŋ/^9xŋY2v\;«7/^xŋ/^xŋ/^xЛW}޾kW|ŋ/^x;7/^sixŋ/^x}X>l[Eu-xŋ/^xŋ/^xŋ/^xt.{mw;鏏/^x;Nj/^xŋ/^x:ŋ/^KW`'j0z,g7w^xx*2ŋ/^xŋ^ /Y1o/^]5?|v1K|x]/ޫ#/^x./^x&ŋw}-{xM$1|\8l-0쏏^xuZxrŋj>o /1:^{xe‹/^6]/~ N/~Wmo/Yxŋ<^\x]Zg/^Wxk>wOV6^\*yp_O ^xw/^xuۛċ^xŋ}ocJo/^x?/Jgـ'/^xuŋ/^xŋ/^xx@/^xuk-]-E [&^D6^3^xŋ/^xŋ/^x;/ׯkN]7͖H ċ9xxŋ/^xŋ/^x]7ċޱz{1/^ŋ^xŋ/^xŋ/^xu՛ƋoվxHƥ :zxŋ/^xmZx*ŋ/^.z3xŋŋ׶#iweV!Y@xŋ/^x;77" <^xŋ/^x]TMqwMŋŋ/^gzxū7WYo/^xY/^xM]$S>k7/')xŋ/^xŋ/^xŋwūܻbv՝]-Ao/^Q xfŋ/^xċWolNjWo /^~ ?'p̷Ewo!^mgxŋ/^xŋ/^xyo/^_zW$m菿 4&s/^xŋw#/^xŋ/^x5ŋ/^ͽK'!cŋw:^6zxŋ/$^6‹/^x~:^Ž xH>|~~nJ?j2kx&^xzWle‹/^gyhMAo^~S5fMon^/^gjxZxxޥzxŋ /^_:{xZ>ŋ0?M&~i:fZy xo/^żŋ/^xŋWy^xޥGo}X;}tŋ/^x^Ƌ/^xŋ/^ycxŋ/^ן6N[/7/^xŋ/^xŋ/^xŋ/^yM{=6aMŋ/^xŋwfo /^ŋ/^xŋw7#wD*/^xŋ/^ysxŋ/^xxŋ/^xz*@ooʄZ^x}ŋ/^xŋ/^ {sx*ŋnz9x\?Y|qU0xŋ׻^koo^e>~^g3^|yϛTԛūעxŋozu^xŋ/^'ixŋ/^x5ŋ/^x5.-@w7<ӸT˄[Nj/^xŋl^؈7coTo^xŋ/^xgOB~Imx͹xŋ/^x[//^x2y>xŋ/^x] Ї'@菏/^x/^xuxuܛ/^zyxŋ^Wy~3˸׽1aŋ׎ s^/^xŋׯަG xŋ/^~f7g)O{?>^yxŋ/^x*ŋNj/^xŋޅ+G׭8Sxŋ/^xŋ/^xŋ/^xŋWOJv{q/8oei?^xŋG/)<^xŋ/^})x5ŋ/^zW&%/h:xŋ/^xŋ/^xŋ/^xŋzd7/^xŋ/^xfo8^xŋ/^x;wɺ; O ^z xe|Ƌ/9$^ū7/^x=絨/^xY̻2g ś‹/^xu/^xŋ/)uxŋ/^gyxM$1GiW~ /^ZRkjtFqxz[‹/^xŋ/ ޚ&^&Fk<^eYy-77Q~~>x?6^||6ŋwᾅxUkQ_xe}w%Z17??2lwiŋ^$^xŋ/z>6ŋ/^xMܛK}L nq/i4xCo/^xŋ?~Ђ/^xū/^yWb B8KkExŋ/qh*m/^xUƋ/kŋ/^m+vAu{uJ/^xŋ/^[Mx:52nbFqYx‹/^x'ݐTÞpŋ/^^ŋ/^xŋ/^xmxŋF ǟwE/^Uޛ/^z3Kɻz׶N$~x;ke>ŋ׽}gwtEZŋnޘK^xNj/^xū7/^>]ŋEjeENjr^eYxŋ/^xŋ/^x⵱+ ^y xJ>6*ŋ/^&5ŋ/^x66ŋqGO &Y'/^%xŋ/^xŋ/^xūK>|\ܣɷD>0yxۚy[xŋ/^xŋ/^xŋ/^yx>yȻ={uWA>۸񫧙MN$^K67obțŋ/^xŋ/^x]?o 髾b㦦|eOx7/^xŋ/^xŋ/^eix};wA /^sړՀ!GV//^fŋ/^xjU[/ŋןgxŋ~f w99.u/^x]7K^[yx:ŋ/^xŋ}tZN菏O[ŋwMV.eؔĩD}Wfxe»do/^ {3xŋ/G/^x:K:1)"ŻvxSx MY S_xŋ/^xŋ/^xU^׋z-/^x6Md-ɹ^~oToBSo/^xŋWW o\o /^ŋf-"'Ϯoآ?>^4U|7[ʽh^6"y gxŋ/^xŋ/^xmS_jg-ޕI'qyviO6ŋDBCYJN}ū7/^xŋWk/^xŋޕIoziߐWN/^xz7d5`AG}gyx7/^xŻ|o/^yxUͻ2|$~_p֖^;xU[̫H}l'JZ}g~Mx/^_~Zxrŋ/ޛZ+m~>ǻ2zupnio/^kEZG6"9$)e>ŋ /^KŋWo/^_wy&^xuLjoo' ċ/^Fga7uŋ/^x.{6 xŋ/^z׼Ix[miD*,ŋ/^x 9Z/^x=5ŋ/^x:rxŋ/^xjY7Wŋ/^xs<)bŋ8^xŋ/^xcxŋ/^x]>gs>$Fūt'yxŋדt#wMٌHңxŋ/^xŋ/^xŋ/ޅ>Ans~;x77|ҒՀ!ŋ/^xMڛƋ/^xŋ^xW&%7A2 ':xZyxw?S`F g/^xŋ/^xŋ/^x>U[ѻ2i s>/^zsxl q7Y}ŋ/^xŋ/^37/^_zxջbĉvɺ=ŋ/^o{KY?6A(j.k9h6-WW/^IJo/^x;qSWP̸xmŋP)Cŋ/ixŋ2^/^xŋ3:^U+N@菿 ss:^xKHŋ&xzxcxm-כF*ŋ/^xUͻdkGzߛNj/9zWEKܥx[{|Fmgxme>U_/^xŋ/^/c;l\}^xU+Z0#[ŋw{^/^xū7^xŋO?p?ڽ=^x(WՀ!&]kŋ/^Fx/^xv}-'h)7yxZxt,)%^xme>u;mKio/^-xkQ_ꋗY𷍛mD<^ /^lDrr?VHX}Mzc#8^xmŋ/^GMx͛F!yx&ex}Oq6 ^x{3Y cTV_xŋO^/ixŋuxŋtiXÄ>ތFނރlUVYm-/^>4|ƫ7q7b^?u2ŋןG*yx%@hh|ѯٽ^xlAD9 :/^x=ŋ/^ŋ/^x.K~1|Zt]T*MxUZx*-4;7e+q"jŋ/^xŋ/^xŋ^a_U0xŋIKVfj/^xMŋ/^xMŋ/^xū3}돯„/^eY fpF}ŋ/^xŋ/^xŋ/^żKЯ8n\7‹/^x'yFO,وԡxŋ/^ۼqJxcxŋ/^xŋ׷ޥ1d}qߥ 37WYo/^^ YeAh/^xg&UY/^>&ŋ'_Q_x~*Oјoa|'ŋ#1Ֆ"j_ n7h5e5`Ⱦ٠xuXkŋ/^xu[7/^x7W=^ɷE)şf\']k{\Sl 7/M{77N$롬O/]R_[-xŋ/^x[Ëk&^y>F5&4fmZx.{>Siǵ?>^xT.^γ8P_y3܋^Ԓplz׿)/^xU;܇^˫޶f^3^x5_m>o;Yǵ?>^x5l:^ѫ[}䍕e-TMŋ/^ xċ/^G /^xfױ}pq폏/^5G3Y fd;]Y}50 ߤxŋ/^ Y/^xk/^r;鏏/^ 9ZRH՗ĩ*^*խy[xŋ/^xŻdo^_/^x]=O: Zg/^j_6wi^֑܏$RQ_ռMx%obŻGYxg76ŋ׽-xŋ2>vjLN/^zCŎ<$hH֣x捜e5`n7Y}ŋ/^xŋ/^ӏkŋ/^L Γ/i'w.59d>u{Z0#GV[ oBU/6o^ ~fU/#I}b^ūweRq/8wb5N/^lY cK/^ٓxAs^x}ŋ/^xŋח<^xĻ2/q_eyx;iTzr?6/K彺OdR8HĸY/^xŋ/^xŋ[ ^exЇ~NHŋoM$T]ДՀ!B3o /^xŋ/^s{xŋ/^tqe@Qx.A~^eZxu\YvIϩ/^xŋ/^xŋ/^x:pycxHٽVrŋ豕*Z0#swi^ѕ1K6"9 Wo/ŋ/^xٛxxŋ/^ctjd}7 -So7w#\F$/岽f4ω򅬇,ͼ^xŋ/^xŋmބ^qqr_O ^M wOo//^|CV/^Kŋ/^yyxŋ/^۽izzxhyt:d_|3x[x+o//^7V_vR%Ye%xzA}zk{[ŋח<^\*=ŋ/^x6LaMaT|'N4?ƋW-w#R˃d]רud#{Qѩ^xŋ/^xŋ]o5xm {xLj8 T|O$ŋw(5{64fhLւN/yxŋWEo Ix/^Mx}ܻbv82M{DQLv1*\x\y͚ 9ZZxu/^xŋ/^xŋ[^/^b;!Ӵ@pj󵘟ƫ9%k7VT֏M ;/^xŋ/^xŋ/^xƼmۻĎ;jmŋwtH/KoTMW;r7D*=Y/^xŋ/^x5/UUԸ+N.~;'0xxk7T^R_nx#mY MiV_xŋ/^xŋs4^xŻ$YƵx{hŻT~"kYm彖Fŋ/^xŋ/^Wx^e>ku%@>Z?KiƵsOycxwǨz(#Ż4f w99.u/^>ŋ޼C^x7/^xū׵}ֻ';|,Yy[W1oB#oٓxA6"dR֏My,JڟWZgx^]7 ^ex^e>ŋ׶#WyJGcRb4y_xޛӛ*^z({cxI} 0d/[xŋ/^xŋ/^xukj4^=Iw\7~uixŻ,A~^ WoS n$kNεV_0ξfŋ/^޸^y[xŋz6ؿz/Óo۷9^xi;C}R{u_zWEɋ}ŋ/^xŋ/^xmxz}xo=z}zċo&.we#2/.Ż4o|!Ѭ%Z}/^{3xŋ/^׼^xŋ/^xz*@٦Hbpckaj5|Ne-T]7Zhj\SmoKŋ/^xŋ/^Mx͛ë7}tWeūw/[^7v(롬{5Ϧf/^zxŋ^波^7[za>}mWo]/^zڃG_ 77Wke7R˃ kޖ^OcׯQF$'D*=gxŻTo/^{kx}oR_ꫦ|ƋWobK>H_j>h|?Ox8.un8'c'KoJSox&k_z /^yx/^xM7/^x W?4g;עz g0 /x=4k0jQ_zxZ/^x|ko/^x}K}U @;sX͹xen*C6Ż4o՗Iءx:ŋ/^xË/^xŋ^W wL3)lӛ/^xF*=ٌ~ܾǛ،,?:ŋ/^xŋ/^xŋ/^^ӿ~q쏏/^iy3Wޫ[} d5`nF}ŋ/^M)ixŋ/^x:xu9@_yƵcf;vCku» (Q_xŋ/^xŋ/^xU؛QHnOnxzћËwqeY fn //^'f :{uﲼ1xŋ/^xzNj-^x}Uk{iO.p/^xTzr/j //^7ʥ Yj?{ sm9[7w)3^\^uқċW1V}y/^x׶=u qǬ7xz!롬l%K/ޥyVSV /^-ixŋw/^x댷ޕIw LzxmpǁNYW+oiS“KiN$롬NΩB^޸^zx.‹w6oTo,^xŻޕIozʷ߇E^yxF}JŻ4oޕ{ѼlD.w/^xjm4o/Z5ŋ/^xk]Ȼ/~ XWNכċ/^{lDƇX\ւJ/^{iMxŋ/^xxӍ/^x{둲ѻ2)}y7f ċ:$ޙAx.S_K0 ߤ7/^K6ŋ/^6ћ/^x}M޻2Q֒G' ī|ı}^R_K,n4+K/^^jR;/^xŋ^ /ޕ~cxQrŋH/SY?6%TY}5:rɃxA"UNj/^xŋG/^xŋZU{3_ ċ/^ǥ ~lrxI}:d5`ȎQxŋK4^^xŋ/^x]ȻrSm>qce@x8ڲ0d7S7Y}und0jkթ^+__{x7_o^e}Ƌ7,^x> G{n\8KoLUւ%lADs*v/^{cxŋ/^xqxŋg^x+qOAŋ/Gғ ԡxMU;rؔD}/^xŋ/^xŋ/^xL~$p?'p/^x?BKY?6e3q*ju;ܷ4|ҺbWo/^x*Mŋ/^6xSxŋWy'ygc/^x?MY +ԩ/ޥz`ŋ/^xŋ/^xŋ/^zWL/8xŋ#swi^ѓ1K6"o1@}ŋw98^xË/^xŋҽ+vCiiO֝WGo/^گWrW6"yو%\>3M/d=diѩ7IxZxx.27BGxxsZq hypy\x.>n KIol1o6B^;^xŋW;o/^ixŋwěċ^uNxM$1l!׻^^96y}!CrMw9ޖH՗TICY ^(>n/^euxŋ^xg6=_ꛧx71jVߨneop}-8?r|ŋwj_E>D\_KE2܋Nׯϼ3_xŋ/^'^ۼ}Gu9ͮ_--xYr>S_?{Mxsǿ>n9s/^xT? /;ҼF#w9+HSM3^Q MYx5x*kU‹/^xH><v`oxŻo8/7'_}/^O⥬ Y^R_ny MY kMiV_xŋ/^xMŋ/^x%@_a`6|Tܸw(qUۛFUě] ?>Tի[}3ʲHŋ/^xŋ/^xëoZ׎8pϖCŋ.oBVXxI}.Ûw^4/]/^xŋ/^x7/^>b:^&}kQ߹p+Axi6gK&Vd/^xŋ/^xŋ^'  v\𺷁/^7sٍKw@!CrMꫢw^xū7^xŋwY޴&^yeXMlŋ}s˛oe'UPVWEom^ <^ggxZy/^xūw <H|/ƍT YPVBKwi^֑HNE-Tz!oٟ.z-x5~#uxŋ"!/^oz h3ȝ;}[ xmu +^R_nx3Y fd;]6u(| ^{SxŋVo/^' xŋWCo/^5艫o36lnGy wuZxhv=*jW{hd5`ȡբ|Uq$[[3ŋ/^xŋ/^oz KZbބ^[XmNZ L*O-NjWn&kACŻ4o՗Y?6%TPl 90gxŋ/^xŋ/^xcxN:{eRI/k%Ql |?r&^xMxy6x͞l%Nn8wkxWɛvn8'[ Pkwu/d/TI">/^xŋ/^xUkjkūwe/>ݘN/^{Cőr0]9mjLu&oJ&k= ,يf>4~ŋ/^xɛƋ/^xM⯿IWO ^}ŋW{պ /kWxYj™iΕA8#sWŋ/^xŻ7"^xYn>\Dz κ7O ^썀/^{w3Kw^ٓxAswnoؒ!ٚ^߼yx*ŋח<^xŋ#wfʸ_1~4`<עxڛ;Tzr/jFdVdRvY94b7U~ܔĉ3k7sGxf5zE}ŋG^N]{b»Y 5ŋ^Gx)롬!W'ŋO^8^xmŋ/^></'/^x[F}Jd=^7Sʽh^6"y &Kg1d/Sxŋ/^x{_/^t1~~~>x6ŋw#\Fd|xI}鍗e=dɗd#l' /^_{3xŋ^xŋ/^{ :zW&&0M5uux;zNe-T]7oj|D/٢][Nj/^xUÛNjަf^bިބlӦ}CnmŋWEo 7a;r7XxI}d5`ȎQY7YQ2/[]kh66ŋ/^xgߦ /4^x&ŋ9^xzkQ__x33\x5Y rh\gKJ\&5I>tŋXE}*MŋO>2w)Ȣ7VnQxŋwqw3UY rd/ޥyfOOn8'bqo)aCr 8^xŋ7/^xŋ/^wOWfB6Ń/^xN3nғKMo:b`3~"jqKًee'U/^xŋ/^xŋ/^xu<@ǟv\;ŋ/^Mk ;~lfTR>Ż4o%Cv35[MzLd+Rŋw#/^7n /^xŋw/^cƝ/^xMZ l^F}.ջZ0#G|E :7V)t"ۼ2ŋ/^xŋ/^xߛY}IowѽuʛËWotuk6zr?fFɧo&-َo ~&YmNOWŋ/^xŋW#o/^x}{Ȼ}&ƍ;|z=Yw=ŋ/^}rWELJ*zu_`PV$_>nd#l'N$RT}YWn:>>\&|^ŋ׽9f^b>e«7/^xq}GM]h{/ޅ xGBCYJ2A)Cc&} 'IءcڅU-o/^x5ŋ/^x:-'qw ["C{kŋן:^㏃ܣ]l[}I Dl 9ZJz7V)2W?ݘK1~/^y[x67obțŋ/^zkxūY= Nŋ/^v{#վl%KJ³^KM5DWmȳSY=KvZ"=瞛/^x7L}ŋ/^{=?=?J/^x ɽ51\D>M}a~_$YvkLo!#_ml^/^P_xmUɛ/^xqWaۛċɹ3.S_љ4E'TMkLYD]z!|U/m}ŋ/^x:m<>n/^xŋ\Gʧޥ~k;#x2̻j^R_v~ڗ,HxG}냧}qec>;͵eŋ6^\x}2Mxŋ^ /^_ls0Y=3nܛN_&<^ /^xT8cijҔD*=ל)w7_Zc#޸/^x}mċW-oToBSo/^xū7sRmwcq? /^ x;BŎ ~ Jӳv|u{}˲cTo!숧?/Yixŋw/^x.[NjA0͸NNj/^x=ڲ0$hT߂&^%rg]9Lȑu}5H_mtNz<[/+E}=Mŋ׷ŋ/^ G{l^x}7jX(zS KOS粓(H]}s>?4ܞ1ͮ_xŋ/^]qxgxŻ,o~^yZu\/ŋ/^6y#܏n8'ǥ+iΖ,JM}Oܻ`ѯvQ{*_xŋ/^xŋ/^xu,@_O=2.2.5nㆊ~lftbxI}K,^lueo~e\e\Ɲw^xūqqq1:w>ŋ/^xMY S_٦ȅEW9*'{i/^xŋ/^xŋ/^xm /^xuǻ*Z0#s꫰7Yi\zxq&ۉ-TD]vV/f/^xŋW]/{7ŋ/^xkKiOGyi3x^Q_rWv%vBXMt>쏞>Nt^iw=Wg](z/^xZWyo/^]2n}W&oh؇uiŋ ^m Yee+Y?:>sI(S\WͷE:|64~6ŋ/^uxMŋwi$^x֮@__Ѵq}NkU2m70Ɔ*zU_ϑӶ T}uAnXŋ/^xŋ/^U^x )qqcCy'O ^ / x'G)|}_5\oBdp>.@>UY rd~x;nx~27/^_{~c%،XMdw>9/^]x1*pw\빟M4ڗQ`חyz΍M|6=ٌn8'ǥ/ߜf^uyxjŋח<^xŋ#waʤ&Ao f^xTzr?VHNmߴ{ė>^EdЗ|R~}>'+~lfT"վ߬f_ŋ/^x%o^xc] ?]'p־xŋPRb'V4Wxŋ/^xŋ^K3og>k|۸˘0yMxū7Q]/iF{t/y{}of{:vQF$'D*ߚ_"<^gx~eo]ŋ/^x}Q_=1MM}fQ xDN WW Ν ЯhLւN~oMŋ/^x}M/^x'oޥNqWgξ&73«Wk}-ٽ_[su48w:@>͚ 9Z|ŋ/^xŋ/^%^7SZٿgmͼ-xU֛n:HX[yO :szBh z xŋϣ%zCzfKzfK~TϻJɵKcw";=|]vׯ᝭ 9*)^e5/^xŋ/^x{3xŋW}Ҟ}śd x;?̖#[y_NO䫉 [7p߷D[<dOsl qzh}ŋ/^xKo/^xK>z bLown9?O^3^>&4^Ї}jO֌/ >{^z㹎[KWA߾"uڗ_xi|j~SdR֏My,Jgko/^ho/^{x*>S_x&*U3@O5'h6n0zӚySx7}xĨ]"Lεoמ ?<}n_'mf_joДՀ!{:^xŋ/^xUkŋw4=~u9I:/^x;)@UC0_H3k^mѽt*ܴMd-ɹߦfUsqM›ŋ7jkū/^xZ>Dۏ^t\'ŋ/^} NYiْoQ3r'\'o{ bM轴?z'r'ݓݙ>{w^4/]+xŋ/^xo2y>xŋdūY/@󭥆Ɂr_~囷f͞Mͽ?aiWݭ7^:`FR%+xud;/^xk/^oq菏:^7^x_jDF LM~#{_腶ȿ7}Yo ߐՀ!UrU/^xūs^xo]oF.HnGy h|>Go[-?Y7qYW/B=yzOv{d7ӝ~6[8Sn|^me;YcSBKW_ՑՖH/^xZwVxxU~1^ o=@6'/C퇉wi^ Fq 3^nO=%oy6\}𞈼ng>;Rŋ/^^ŋwAoyo^ Ĉ7Wwk['p޶Ex7/^׽vHǥK볇_g߷oH/Atox&Cv-z{ 7Zx^ŋ/^KֽMŋ/^Ixֻ2 <I""=n\?m9t{5@zuhd5`ȡ՚kܤV/钬L j+ gxŋSlْOlZr1=/E{|_u1=/F]ׯɃ+ѕ{܋Z.wY(/%xcxŋ/^xweqǽ]~}?/^xu/@jv~}ٰ m`+^mܵ]7UcS6' WyJe-t/^x^Q ŋ/^x;=}Y{ŋ/^~]_A/ Ӎ-![t$Yz}k]^?iEIKVfjK~suY ro*M{h}Ƌ/^x'ySx8U{:.ŋ%o /^xwe/ϷnO|a/^']W[RmO w?v~uz"s?u?^Z~3ʲHLyo՗L .Yŋ/^~=՗_/^xŋ{ޕ~c_|b{ O ^*{w_=FR/gПzfK>bն/vv\";n~#zW>ݺGjo')KErr\(=ivd?xA"6sL#-xƫ7>|k>}^/^z-gg73>X ċ/&gَ?%PQ_y3y K.+@J"" }%= V;ou-w@o)yׯ5r&ʃd*ϱb[ւTYŋ/^aIOz;GC _YxUD^h<q'/^xo Ї~@>_n{x]V/m n?zW}=wVn׺0dlh=Ve5`ȑf}Ƌף޴^/^Jy#U_T//^xxūwz!|uZx+@ay)٦N{x{Ͽ-u/r'WWƿ0@Foʛoe'UPV{s͞<nع6񶮶Te>ŋWM{Sx_7^{\/71c}ӛ;[Ë/^^޶ʤ?42(`o|N ^*z-zo ПzfK-7ϧea>U?lv=W.DQ.@:hy=@Իɽ%rWyoz)wMLs޶f\kx*|kUzYܟ{[//^xZxr*]mdݹNiNjo$^۽?+r JUmu$y^Noӑ|+o{n_φfot.klJZx'MY {۴/^x6"9۟WWmo/^x5UܻbĉvYYIͼ.i߇k2^ <G}"w2@݉{LNk{^}cj|ӓ^fQ`F'Zx>ŋ/^y=tK}xŋ)K}_'psz-J{x~9*g~ 񥎴: {}Q"@ŗ;ߜ5 е~[}J)\Klte3fF$P{xuZz[xmsx/խxŋ^]b7Įlspq$Ύkx]>;O3[ݖ?x9#{]_yu/^ o?}se/r'y#z}y*?ZCWYWg6@Apw]7rږՀ!;FE oj 皎d3~"Ri3^xl~ /^x.[Nj?|߹%ɿ߅~ П~#>Pѽȝ ծ%{Ɲ7@_;>n\٪ 9{V_vREYe%xza *|~ޏxzûTK ~-.k ꫁7Ot=c{;KEy'P5=ٌn8'bGyoޑ{܋ZO3QLւsϮ1>ŋ/ejk8^xsxo^Wyʛfy.7/^Wpw|v)r/;H\ݽ?ב|+ocv_jG8qpҙ3.5q#' o<‹/^xŋ/^xT><۸Nj/^ޗO&5x!tx~Xۺ^>ܸv_'-Y i j0jM5nڗɢg%[/%zSszxpN}ŋWo /^xOsqW/^x1$wMs^?{#7;JW~eu"@3ʲH\yo՗ĉ>^.AC&GxŋEK}ŋ/^xz?@n?:<{zqŋ^7ْ7<%lR6K3^?诛}O?}\g^Nz#|ͽd;*%]F#lO$R=7pr.롌%gxoo%<__nUnŋ/^x%@swA=|tLsɺ;/^y-nMߑ?^Kg>]w=:"ot[[;Hjv&> Ye%-IW[hDO۲4,F$/;]e:gxŋw:_w{rPkQ_yx*2U9@g5/^nO6 ҽ_:,ɏ}nߓw:ztڋCwwܻCwM1j^6Krw7(qϴn/^x]$@zO}Mŋ/^xk;hI8^wިUØ!bP?́txitދ^/2ëz:WV_vR%Ye%xz ה!񓆔Er͞<n8'ǥGxz[[Ë~+_K/^xCoR_'XMl=GpqU0x*K>e}ۚgF3k%y%Ֆ7~d0/ AywT.E߬y+@wlԻɽ%rM} S|7]Ǧay/^w[l>kMպrVQ6_?~87c;,/@ۘ>:ϼSa[ ڵ9Fze-tٗp+lrkt&zÅ ֵFŋ/^/yO?ۑ?nyoJ6ŋ/ xػtܸ㾑ӄwBuÛ8['[<*t?}y_k]xxNO^x"@]y{qP94k0jƻ.qF=$kNεn/^x5< ?ڕ;^/^x56/w)4oz?Lxz*%T;PߜfُIS†sG>$}{VO9ޛ9P;~K-oK*VT֏M ;GB^ѕѼlD򶶱f/^xJ/JWGO}m?ċ^ls>Q_l\ŋW}ZB~sG@7YXVvR[7T˛??7<%?|v ڥwf[x#^w#@mz vyWt#w9+H7Tc~>|o7QJoV/^x9LxwNE}vo/^ŋz=wa/xMw-{0׌~w;]ߴfkd+%[TO`^KMߑBБw:nx|o~p(׻:{ݍ^o-Cv3եz#܏A4'f7oj|#xŋ/^xŋ%זqx S`qꏏ׻^*M ПzfK/rP]MtU7+˃l{_N?Qyޟ;P^$ov=/?2t+g 9ڮ{w3U2$]nd/;UK-e>g7/^xŋ6o/^{ G{\*i楾x Ї~rO>y"x wD%Cͺ-nɷt{y/ԑƥw~#uoUn ^ٓxAsr\8>Ֆ!|u)L#"c}uxŋyxŋ/^EqMv<ȝNj׉wz{S><)^MU;7e;yz>/goȶ<ԡٔ>_] ܠ][,d)@jor)Ǧl&_Gf7T쿾,H[7^:`Fvm>ŋ/^xf4ŋUkkn~vޟ?>^xN Зu|~kd'`Pw;]ɿ=wN׺d!@l 0d/[eH/Sٌb;5Y rhn/^xŋׯ4^xŋw)^[,;q#b&oS3/ūwOoT&4FUy6vӹšg}O?}A/ܑ_x "Oyw7]`F'szАdioٓ-|Ƌ/^xŋwË-+\kϿ' N/EXwǻ֛(e'b^7Rɿ=ݟؽ5@O[]Os BG~5Bn/Wf]F$/rwq gH,k:rؔ6|ŋ/^xŋ/^1K}m]/4M:uGɛī;׫-ܯwljG9N$RpcyƏl?[~u$~˝>ol=wo|!롬l%K=.ud#݄%fϷiKVfjZW/^xŋ"^/^Zy-꫼z-o'@]^x~ 646޾21%T\׿_x'*X㞧Jߑ v]/d{%@W^ Y rk>6nڗɢ g%[Pƻ)Z0#G3=>4xݨ7/^x&^xi_돨_?|i ċwx=~ V_B]†曞۱e>s {{{/od厼we_5}֫|/ɢbT/dlzАiSIoٓ͘%d{|*^Yx~Y o/^xޢ6i+~q4Lڳm:5miqū׏:ӛ,n4#5_V-۾L϶0^ȿ^[N/t䳡ׯz?m+;ѵ}0yS ًe(JW|ivū7Won<^xŋUoNGد٦HbњG~ cŋ׿^s^S5] s$\z{Tz߶ޗz.}qŋWO_@שq l 95|TEŐҸ-5<Ϸd =/uW^N/@g ߔ!BVo՗Y?6%T(__ѕѼlDv a>ŋ/^xOo /^x0@^u=uy/tR_ ;^o˱Hۖo趫^7o/kF|؟9H~A缳>`I*UJ*RXAb95:s{'-Y iQ`.CM3^xŋVo/^xŋ+ޕIwL88x-ͼx+}ofz?&X#t+\Y 7!u)oſ K~xx˳ɿͮ%oE丿ޛt/g7+?xw .Td5`-k2me;yz/^xŋ" ^ex^xW8߮8*xsyߌOњq/PĮ-,PxŖlG ֔Zޕg lմ.kAϿ8K{'9z5Ւ!f71=ٌd#2JͷD2lsr?VX:]x7/^_zx{uūwN8çMugŋ^%߆z>w#iyG gEjGl'N\%oܗ?,1zRw&o{nо;ًq>w нyP| ;~l~D1MV.eؔ뢓GN{cŶ 1*u;]x_/y.}W]o/^EIxZ/^zW8h3x7^˟ޛ$MjmĖ=ЩZOƉ<*v~j><~󇲓k&̿ZW~yEZW~f,֫UғlFsb;{C 7ZЬ[m3^xŋ/^7^xŻdE3nsVxۻzu#@g>Z5 rd~罁d}x|r,A^{_^Nkw}zzАTtn$kNΕoٓĉ f<77dov ؈7/^^ŋ/^x}GkǟeaIƋ/IWnx|=Z㵼㻞 Йy3ىhK9+\[?̖|G忮e۟9<ﮎBG>jS{;9;_^=./s>zd-hq)oޕ{ѼlD.wQcS6Sg>ŋ/^xg2ժ/^xu<@ǟvadݞ/^OMRճIytk>'9Ld;QH{K}K Ї|=\)@Wx;ًq.^3q#w9K$y+Z0#[d5hn|{wq3xŋ/^{x klRIڹa=^uxpHu$\J%obs]ԛ8[)%oMd#ݥ/I=1<艼Cڕw୽ȝ9+@,֫yj_6r?lJvQ.CM-껟)ƞ?k/^zySx74^-#4-j5aUÛolY/ y#UDŽЯ{u|n8AfK^ɨud#Gd(Y+or!롬l%KGW٫/^xċwMQ~F5&*}YJg?{gtmн>aUћ»wݼ_Zs:?n֕d^b' d>?ym}xDaG[]]ysًqr7m흔^n]وe'aIU-jظ=6ޛ_xŋMo:^x7I}/^1?ztBŻ,o/ 7K*7lAi_jsx痂EoE׺K^%j} eKr8+ރlUv"Nje7]hV >\5o?/^x>q>|T?Ǝ/^xk5MَrkP9Ȝ˷tۓSlɏM3{;y厼ծ|xu9anNeJ YO'IQ[lwe?1.(|/^xŋ/^{sxūS>}s|& ^x7Y)٢[}z6@=|.(^OdeJb{ooڑǦlO7Qo.y/^OWx/^x3>qWaŋ/^ozيԡ6x]g'@w1@َ]yshVd r/jV,'fW=|ҒՀ!`.C^3^xMŋϽ)xŋw>4>nv?' ^x7ȃ!"KA_{^N:W eĨytw(Z0#G#k|/SY?6kcxŋ/^xŋ/^ݼK Ї{[");1ME9x7QjvĐL:TE?ٿ 𚍞܏Yt76רud#D*=~xUNj/^xÛgK 3c|tׂW' ^xfzқv ɍ$oA.ZA@ڟ㝽uYBKY?6 }*VьU<'AhNy-b[V쎹!#zx}M#K3nŋ/%{x=u7@菏/^x&} ' 2mxz 7^>н4~#܏d3\C ZMY o6f+0j+54xMŋ/^xŋ/^K5|[ĜYƵ?>^xZx*Z5 rd֝9w#w!g; ٿͮ{q\;FEփJm-n7N$롬O/Dr͞<nx6~j5뼷/^y#(^eY5xћNjo"Yǵ?>^xŋ׋DLv5ʬWKo bG>ZS{;{de-hqWNy"zWEr/jIUޛ^cS6sgowOV^/^x*2~h|xMUқv0u>]oԓ[t䷾Օn˞φKoؑ%O$)J}csY fd+U>ֵ>x/^xs/Mŋ;]/:`$^x8[꥾㏿+Og?ZW^w:@|n:{F}ٌ)f׫=e5`Aw(Z0#s-ngxŋw~o/^xŋ]-:-fiQ<^x^N6 פ엎 o9~镮^w:@|%4fj4$Yli~}8cSBKf+1K6"mըn/9m_/^x«w%f fKGy).^zԗ{u\BbYI}1ђ7?q ׻{ݍ]p&ke><5jvcf\'dBCYJl.>c#8^KF5&zћŋ/^xU֨/gʤo{Mz>'pxL K}MֺHY :I:-K_oӑ|+o{;{u>g~Kوd'nyqo-Cv3U-|c~Li3^x5ŋ/^x]n?gIt^xU6K <^w^Ցx+?^w:@׫Ą7R˃dQ6]*oAd0j+Z}Ie=^ 3ۿxQRC,^3^xU߻2h~/8gb5N@xU?mvؐ}N}}떶/ԑ_fS{;jK3ِ!^Uk6{/pNBŎl#"9$R)m>ŋ/^^Ʃ9*<^x7=6| «7BdRl'Om.*>{U[wCwiFzZ0#L#ʥ=6%`JW/Z0#\ŋ/^xŋ ^c/lrx?@xߛƫՓ` 99.uby&?MyG-yߗi1ծ%L%y(5e7bH0_›d*y-yWOjW~t|Jn(#F\YϣQQt89W՗ĉ*vŋ/^x˿rŋWiI{/^xMڲ1d/S zS I$Zɟ _y)GJ?->~S[nqEA)kACco\JWoѕ1K6"9 {ZG?H|Ƌ/^x.ŋ/^xzX>;YI?E}7YA"'[S_ujGī¦O?ۑ[Zo؞/ƅ|_g yb\?ב߼a_`*^z(+XoRJWo|!롬l%KFO[4d7Sc>ŋ/^6x-W1o/^gޕ?h4 ;/^xK(s*"{S37QQ9xwF%@WN]jS߸)oL5@>o3ߛ?1ޘϡbG֏MK;w64[4Fgv)o!oДՀ!fC W3^xow^x6ŋ/^z3{W8o" ^xhRq$"ƫ}˙ެ/^J/(cu/w н2#lO~$'fl{hd5`ȡ»gd-9/^xj5ŋ/^x69;{m"- o)aCsM_ż`-;?3WO/仟ҕ_~e'g З9w3UY ,~\/[SY?6%T(5]F$?s{3^xŋ/^.yԗ5j@y6%/^x?n|!{JsjY}]*.I2GzM kO>~s;Ɲ'@صbd>;yYmY 2+K~lӝq^MW;r7D*=彉3*1ŋ^S#olNj/^{[x?˸O/iik7Y}u&] ,يԡշ֝is7>?񷖼#;6%@˝ַw|\;[=>ꍜe5`n7oj|/^xŋ/^W]7Y}u%@ǟfEppki-hMP_w7+˃pF3#jO3I/l{vGBeO{=@]ykGne3q*¦dk|]Z0#GV[io)b*^T߫|Ƌwo^xY/^x&*]瑫Qi6kj?'1x(d;b~F}+*IlVnGoɛ>)@kw&b{p/wWul\;t;^.kACM>|5=ٌnx|ռzGErr?V^5oNw6ogyxŋ/^x{z>7;S)^0x)X?oڑ)ӹ9ޮo^he?lG~#o|n ʰes??|G>zqρ>́lYϣf~&+~lʃdSUҙ 1*gxŻ4o^xY2S>]وd;nImR*4e5`^Ȭjxƞ/^xŋNo/^xS~ӝqWwޘ<ҼMxFUy6VޛoTbT%Y?lSrU^jW:4sl%KqL}e^Uû.Z0#ʹ\'[Ia>ŋ/^x.pzxcx^9mo+ݯ7zxU*}M۲^B}9V{aGjs5<_V_;^JpV>:D 4<{oޕd^y1]Fm-aDr>/^xŋ/^xu4^z*@[r18no /^x5j9N UHG}oq}l7_|#=ȗlEPcJ? /䧟_ @^z(+LI*}15<«7UdUO[4d7Sc>ŋ/^x.pŋ/^ ГWͫ'n華ʄ/^x25%Sԧ ]r&Ҽno{# Ց__:n? cC (+uɯ?^=\ʽ%by7O^u+J{ 95gʲQL oR7/^xz/^x Gݏ]w\|O x*55 u roR_żZ_ҕsIs>֫@7mh><:@kʏ]_W5@Jd=4#z[gUv(롬{s͞l,و 泥 /^u+x}ūVxme>kjvˆ %6a~Wo|.ьKWAoz)Įs,S^ɓmg wbWO|\Z~oZАUX*5jو^rd{׼aɢ#mUf>U֛W/^_xŋo[ޥ aowm&  x54&k]9Je+pK}Uo#r[╎v~#I|b3[WtS߸rM~9UPR֏MK|4[d-jj}/o]om|Fx.כċ/^JyxzTz \l\;-;a ^Ozշ֗lI"Y ^hQ_Kƫ=I$Q׮w:g=_T_ۓܑ'7.?񵳩H'cI/1C&C^՗tIC|Ƌ/^x+W1o'^>jONNj/^l 95jj_ҕ IsEוd/|MO{"@kɏ]s}ǨZАTL7z>o՗IQk6r揄9gxŋ/^1xŋ={u,w 2zϛF5f5Rx ًee'5>gxSՎJmU.ծܵf Ї|& oR+#-ACB*xMW;r7ɛ(Z0#[/^F/^xūī76+u4@__<o/^x}MzHYqC}&[9w^z_O=%o>s~ CŎ<|^)ᓖ ԴuY rojm>ūw8lqגPWŋ/^xכrHn.3~*e>S_;|E Θ{.{㵾g,_;K^O"?ۗ#G7.?~#վlO^ؔlzw9LEV=ol'OeؔP/^OzNRzfK~G@ŋ/^mZ}3/^_ym #ŎNj/^UbK#gkZ79?hKw%R]_vH'l _md{=@d-hHwzW}f ݛwd#Tmxu&_k;7"<ޥxxŋ1md}1]'p޻ūțvd?nvd}]֙s7]nKwRW:i_pD>86(ȗk^ Џ g4$hVB_z3xm~ËB[$YPV$~7VljАLU\-^)}?̖|GrFNZ|],^x&*ﵨ/^xʤ~^pOѰl Nj/^UZOƉlEs*v< n߯^>JG M;=#_W?خ 5ɯ_8d{-@?.ud#Da}yA)C͆C:hcoxd><{ ߰?:ŋ{^/^xūweҝ7`6{ qSFo/^xZ5 rd|߲_)~ŨK|.Q: x{=@َNWNSGߗéjC~[^ #վ\%a*;ьĨIrYL#no'~zͪ<%js^~1$h5gʲHx|O_헨/^x}Mco/^ZzWn땋j>xGoԐ!YOS_j^~.3{۫OߕaIW:@- n %Gx[>K0S{7-[lw iF$9WMoާy3_ ɝXŋ/^xk=l^h/^|̛(n4#76+Uɛ\J|&VOj/h__}2|n^F#w9s~/x Yee+YR _xЇo?{K&^jy{{x-x5~#G!nqū7W o֕d^by 7\hj`_ >}u]HG۞ȯܖܹ|bhmPo+x}5&x3Y cT[7rM-gj{>?̖;_V(/^xxŋIxo u[;}U[6K 97ʲ|$]>DBj\3[Ənxy_9{r t>U{j#Korn$롬O/0h&*?t(uPx /^+Kf>ƺ8["qa{x7~ڔ!f5]ٌ9_cj_ҕ Is[}o Ї?։t}t}R~[#xmADs*vfɽ718^>:?Sm/^xŋ︿2n 7=8xor){줊7I}&W7UHԖX;e><~/d;_{l?;.@/^ujG?]xޟ7ww1eq&If277xM;L2ɛd$g3SmN8q-VJ( )$H+@|(VpI྿:*/p]X׸饗^/7GOYX_z饗^z饗^z{ "NK/қm WpsOC\XR˵ynz3}ۜsw}{)]x/9*O/zmEՆވZDnL/z2@o?Xjz޶_^z饗^zu{MxgC^zwUd Ju[j;W7pumN>?^Eg0dv֧SIt^WRzTEx4lf/ƞ^z.+@?;K/K/K/gxϹ 9k2^yfKKf6 U]W=24Y.;sŞ1d}]~~ܩa]x&@^z[C\Oq5yvWJՄ6jcΨz^7M/ ޒXK/K/B{5zs?UrK/"zUwB 52շS|a{ōM۾O|=K`LX>|yxTo\u}Wлw?wI#zV襗/oAK/6ki;,2g~z饗E{MQe7m)k}N}'aCw 8~[׍V8xes}'Gsxq~g e;~wolb-`ګ~}#Wlo^_J^zmK/K g3`%z+Wn P0e}-M1tsΗ-.Mwf~B[5 ={}}{x}W;o>n4^ig7_)mzEr:6bD{5s $ncL/rzW/kJV_z]fx[w?K/.-@N؛rR]f`A; ]TWN2to W"e yz$@o?%Vc>Agx3݅׍]Q^zz6m\+jDyc+"nf'[u˫mй׏2^mwֶ^0K/YnVC ]iι_Y}NWzV}RuW^Ń]T>qw? ܯֳl/M=GUlkRx6ۅ5z6WWt֗^zͫio^zޕ";~U ^zϛi8^pCk1@?>lo4X~O!W+75Q*RxQD^EL?*x+E/K/z&@?ښVG;MN//%zțo!PLׇ;o9Ϲ4]n>~ .\+:;-m Ok$JL^ 9FXu زqH{׳lgzv&z5@_W-K/K/J]y~E:߂9Ls9m+7[c;UVN?q%op^iTbpwy9뻚}Wv t]~z$d}E* W¹ !{>6b : GXo;o[4Bx {{/+[}饗^z饗^zg6e~6_In-w.ۢ^񼕶H@j+7wĵ$Bjǯ` 10^}- ]|JeK8B`Rnѐ›?h!T-\+:K/K/һ$o^܉7(^z-oR!V{`޴1DE?q^)+@1O6jHuI fKgoPgz*X(;{m[^z%)@g}%f饗^z饗^z텟:qEX0K ;Ŧ4d _#W!e8y w+%wUx"@>L?b5^ze!n+-ὅkI Hև\+:K,^^{J-K/ws`[[̛.b3{pk9+7gX:HSh^i y+xa7}?y9"^Wxo>F)i-L/zK/]J o{kM> ^ze}6+wO"zeoưu[Sy" B =ƫ|=OtWK/X(.4 ERxs>]yɼ;^y֗^z饗^z]7G/$@1R4gqw,xyLr]^̈́p M7z{'ܳK/1S`kOŶjzs>r.+^yxg?T}ro!@ws=%;?KBƵ&KHlJՄ}oWWt֗^Q)z饗^z饗^l]-doΰh+bǯꛯu6xSUwzY^u!k\iќ{w~e;~ LX>|txX* gz :+Kzz'1Pi-V|zWfz@8U+nLcE6X~KK>~3[+ ?|^o|=~饗ٽR k!͔*ԱQtX/;7KJyz饗^zW]XnPY;o|?ySGiz^7Ui"P[2TTAC;q+eox/{s~ eKHU]Jm[9}὚9tW$7 tޥz5o~=\3\kK/~Ky|?>ogylR;=~[9c|1\i}oW:aÏ__Ovk!۟/w] }=z襗^I  وp3[]22W⟯Z}n/=_|E/j6WmKe}w^Wt:`q}K/f659uS~kTߦ~M0^Ȧ+-|.^ݳ빵3һ0^|m!ެǼbKhonI^z W-|~xsi3Y_zפ%z[K/5Fs] г[zgyc%t[^AxAG 9}KO޴1R"SxRϞW*[}EO=뿶;WxS}׃l/^_Ϫdgzѻ[l`-`ܖ̏G{\3JnRy K/Koyp}TcV|ir~z;Iz}s=Ml&&xGL9efs YJeHx/ _ܞ*@}‚l/K/Kc#^D*زq}.q=K K_6@z^zvX_֗s}J=wV`04݅."K/y>SlAo~8nқ?T7|=֩[d{-@z9~饗^x+ 7G՛U\3oK й=mE#4bz۬/K/{I˷\ H/"{7Wda6 xyW^s8wǞwwιtWKwɃ" !7Zjb- T2g>q1W s=K/%o^zWN]3?g%~{z+ +7H2c|7m u=_m7Gx˥RU貝[W ZZDAvb#Vj g6$u{7K/]Y_zEf;qx_|O5Z H/TL*)4X_zZв-a'SBeKQJ oN\}}bu%A*?oK!K|i jޫ6-vs[Cٌ4zիI敩 O_ ^z饗^zwse /XVZ>饗^zX>˫oд:~s'+/@; W})|-|K/>jznfH.}*Y-LEɼR}tK]K/K/һ:ѥ&՛^Qe= jb}ELݺ͛y"zp[x }-m ?􂍷=s t權|^~OxfJBCjq=-`7|3[W oZo^zEW.﹣\3 Gŧ|-;A}қջJ*VԷ-O}ss!{WmZp=UA>{\>(vw?kϟ~~7՘nn0-ozfBAHky z 眳{S+]׍}Wxw w?~ Зu%;?K/LJBCjmٸWd]bU H^z饗މ)z饗^6:cr0]IH-;~Sl3Od}zJWu3@oo8~kSo}G^]:K_9•}O^fj=GUlk\7Y".4K/K/>6s:ՎT-JSGy T&~OwyzfCDreR=˛1眳ΘW*"x#xwxq4ٯ[:KޜO6" jC oTkb- T2\!faIᕭK/K/Mll)uDKMێo9+wx;kINU/z[ћ,q3 2LU{ sxeo _";ǜswIS敮ʻ}&G6gy[^~Х>~饗^_{w:֣*w[j;`#^D:z>JA/K/KDފޤ`ŽZֲY_zǏ:+ ^zefml&ќ.ڔMc敮ʻ}#x?yE5Z^z-*jnL"{զ S$Cs\aW3yK/j+soZY_z@V^Gg}'@?.?ݛm.{^zf ̾+sY_7_ e<~}+]wYx{; Wv |i.2@v ;qwz6bVTjH5s@P54<_3f[^͜6Kx;׾r/ZRo^zf>{bU;s[<~F& bse/cnū#@?Z:~ߠ^z㍖Ml%DK-)[IXDB!UxK/*K/!o1Dd \lvX_zݴ6P/.ӫIUL(۬`ޢ!WaoXxk=WW*Z~Bo~;?􂍷?c<]^z7؈= ջC*xhA9Y{k= SwK/K/;Lċ5Tۖ4-ITj6Pe/B,K/כ{Xt)}kQۅoIvj װO*gK/K'-zk޸na3_Cp$K/ s7`W$o ^fp[gWԻRʄNM@_nZDA:@o?-Q>ic/x sE }v^z==CXkb- T2mMQkX|-G7;胩¬gz饗^z]7_U/o>DPm``zһ=8%q-wFO ^zO|MH(i^s _!{œsw>_p{/۸w|!n3kx_|e˧h_zŪoqofxVEjmزq}Amƪr:^3 mK/.[f}**5_z=]I^0G/^9~:xm-+O9[c;U)$yw=k]-._qz7=7y^ ۯ-}nۓ۟& E ]YM饗;J•$ѽzYݵny5;& L/wzMzA/t]<ݓ[DpA9+]}һ=?nrzǛSlF8[B u 9o<MO"*@_7Q1qg^xÛQ裛4j~Fܒ|E/.ͫ,ț<`=bKKፕ[6ropSlaK#{8mo^L/KK&K7OcmܛȻ};ٯwR*7Qq31,B{+7m ԺҬE)?>\v?xǂx7"+ ?7?5s.B/sx̛]z E˝{}~.:6bD++]r:Rz.KK#z饗^|]h"֡¼K g6CiV|zJ ML()6Y_Ѽ| sYO'b=[6n3+S>~K!—BK w?;{7襗^zOCLJBCj -4-\KjW,Xfp\zNh=WtK/K/n j3@Y;o|zI4o7gX6_Sns.".#@_zfW}#ǂ 07襗^z' WELu7wU-lAv5z^z饗^z饗3o>`3#uf}ݭ^|74ư1眳g7a_ '{m=GK+s~Byd4oq4~ нK:_饗^z]X( )r42L/-ɛ^z饗xsΣh9K^y_Y^YR7 ".+7pun'rLOTtZϥ\uy~Ov+^֧ K;mEyc#^8Rguy헿bhώf/"jK/{ -Se\M]joʣ=ajBÍ7No\9Lx^K/K/;~Õ.:>+W{N9zL>tp$o^uFoְh̜ &~oek [ 2g -uJE ŏfkOY苋 v~^/yN-Л+ .b}:X*s9o PCx3Ko/7͜) ? >r/x|=;*9s+ w3s. zWW olb-`ؒ*ԱQnkcthyfq=KK/ۤ^z;@@=d}7Y /n~Vkɼ{ #o o _![2w7-9us^x׾-@ 6Uoun0nO=~E饗^?{Km[9}὚9tW66=#r=K/yIV_Q [W8oH3PlZgzOoNGz{34-I]T+7g u2<_4@4,\>@o0>lx~a/D (F_ܑzWګ6m\Mj͕{kǿ |Jeo~⣙[n#6-襗^zW=lQkz^A2ogz`!p 팼v\K9zZ nJ=3@%KLA#.@5ho`C(.ʭ_tsXr۹}9U9rۼp }_ǯd}b !Drc'x=^z֚X(Lr=}pw[ѱUq=KWjzxc|G?SR\K/.Ӡn@Je wzrS0oj@BAH3Y_)c4[lٸWd L?9ѓا}XQ,GyNX@/[^K/^J&J jb}`BLxȝ{IZ۩rX_qsYx˲75GUdjB ?m>J93~Ap=h^^z-JMOfy•@-ʛv MLg"oF@Vۖ[0Ov~ ?Moo_\K/%r3#wY_ z'k`tsd/^i o^?y l ONb9jqƎ.zLEDK͙;Z A }t7^ly$Mp*.4]{Ћ竼dgz׿AkA7i8)ԑ1Rk^3w\s4{=?чy~w17w-r=+K/^&j6:bmW,os~ﹳ x'^zMh5L<7pugY_Og 9V`#^f3> =6Yϙ#ѷ MW{|7:,o;7T0Q\7\"ӑrUl vEj}/i ^dYz>nQ:@P5 lxJKv~^z-4m\Oq5YBf߸>sLx 7zWVoYf~כ^z} W/z5@?zWٯr8;ۜ{w>U%oΰhTjSWl7w5DHA~srHwO%m1@!@j.wT'7^zwyLX~f|~@P5o=MUT\{ǖoڗ{3zܫ7/OyW0o^һt"w)7Гd r{yEĪ֗ޥzz'o^rozTŎZw#S%W=}nkH_y<}غƳIL/6)Q̙[n#ӑY uGU+]i-zEfчo>g;Zó׍H9+K/x MևJ!K/~ ۸?~9~ s> o^z'*M vKmK/"[p#Uf}!5Xe~JG6K9~]ωn=[^zWm-io`3[FAFtD+y5s2&cWys^puΨ4]{O E[eKސf"PlZפ^[9?Io'咗 y;{{*JM ldކMn;g |Ua?mG}^j=G}_ٽ%zro{5^.!GR)P0_rnfzWdo߳~9=[ ow;TsY^ټRy%od͜ޕo=W}NЋ ^zop@Df}饗y[w8js7\2Ocޔ:@S iB]ϻ6ʍۮӴ'K/ ݫlk4[R5l!6񊉵bK$[޾\ h[gڳuc茺x5$ok$^߸>s>}]^q^~?pr/x˒yeo ^Hʐ^zW{XVt/U ߎHeus]ޱ5sb r+^zwZDA#ӑzmXzWow"';O1Gpɫ+z.T{]7hl]@z=L~g J~_0z-7ob3 t^^{զPpb˖9 FHm\8RjvpK ycXAV"TJ֨X([ڴ~ۢGBP|_:<~ނ {Q GF/ۑjaɵ;Wpo^OͺB/ iSQ#) Zбwd+W^\w˻'Q}s>=緅 J9=ɎE{bՁ^K/zכT l(|+ 7GFm'ӹ`=o@>HNqE /ZCtxSĵ2%5M=[h<~; %UXBP5\ lxZW^{5| 9FI q66-\Mh,׼~?~Kxrfon,Xٳ}[v8.@ov^z=US h~ώں;@]7\`+#_^>i#7#7kXI?)6֗^y װUvyNM=$~?c[vHp{8^^-+w4缁BJs87[`#^l.!^ozTŖRb=y^;q{C_<{K_zy=ZWi?.,?.oFO)ycU 96_z'zz^I|Eǧީڋ4*i]%[ n=jZlsxo |{QMO_C<~& !m4}'y^zeJ&65떫XZDv)E}w}[LҼ&,fP;@ߣOT~8+@ol ~~H'g=i: '&=޼$|j ˲nyzӇwvMzӤu?>N+$+WTI/ƪ6E2(o] o'?:j8IP̑.қ.4[n߼^o^z7C #]7ױU __"Ǖ6wWgzOR ]^ ;sgRrӫT?cLsG7/w(^o[l?-U"{m7Vj(,)՗^qJSИfn71&7wTY_zɛq# Jb}U>/ig%^do:@P5=|E/{d:B,k^ W%$jUċ9=3'mZ{s+j޷⯲}>gYc} }W5GOOO7#G/7\b3#WIj-}}¹.|oY2*5,263ۺr=K/oP5Uwa}W=O|fk)[V6wyp@\U%;?K/& ۅ&\^7UR7ZTAPmp=;~QXʏ 8K mg&cmmxNKw5@I ozgCDrd+z=iιkG?Rw./By˂{ωXyK]'*n& U/mn7\`- Yx:>/]w[CDj -U}=Mv-[gH'WK-G>7RjL)껣ְUt]oNo9z+`seO|Ek1Qn5%;_ѻXoJ眻j>޻\xEhyPL/~{U,7Ui"P;/e|]ߎdwކduyq• 4s=˪9Ŀ xncx[Gq\nKᕡez9~mh=ofxq^bƍTWD]Qy M5ZO,Jk{gqmݞ^us}4g!ǯl+zW]xk+ނyEGFx?s;f>3T2Tj(4,)3u-@/>i(xx.֛SrԷ-z32;~\3y/_؞+@s6qx7zGP5 lx^ q-b8Jդm\X~|tQ.{yF rvsw5fb{] {/Z!UߒdWiM>|mm'^ȴ}ltdd+z{9. r{ l T K$Bk$[qpğ>Ï}zsMOYx,s?P{=OZ"rxS :_f~|=z9jǯ"үs~+7" -yK/"xc be,44WWMּm>:K/DQ6j~!w~Z[I)WwC5@`'U-\V_W}Xq>f  hr+7G/8hNv1xǛԱTtY_zsA"Za}zJY_=V2x#{}5za3#[IYX&\sVx 㧲xwb+ ]/Ld//?^EmwC%+7o ٷ޷jUߤ`B\C\+I'qr~񢼀TL*H/ܫ Ml\+^ -W-0Tdl=U"=֗^z]7j-*jr9]VU&4\OUy KY syb x<޺8!yd>% FG.[xW_Ci_Mwbgw=.wבZ%Ɏ_z(@Wŋ+7kXe4lfY_zs@NzTEZ,Y}\Ϸiy2xU{t[fV/z˂{!jAՀ t=慏V%+ozTEP5x{4@?X 'w}wpU5h1\OYx׳wwӣ{od^]Gx>^E'V|oN]_3:~~f>n&^z LED&K-oDkb-`f}& !N꓀byz| ⍖Gv-)krǯwŶd7=g O ^ ۻ99ex~E 8j~ߏ[OJv=W>o > [c2^s}G7(gz6kҍQ_< w&~y  DŽ"ze//b3[E-lfJ-W`of#6\a]w97-7Co@NGl+׍r:6bDMm9tW_73׏L'@`y~I9T9\:s E?~e;_{G>~ܴN߯4J ŦL/Q(Y/itcq~'-hޒDތEpOŶZg}饗^y!*Р4,֗3Wz;>o`xΪ,a}E&j6:Bwڴqmk{e$j7/F6<~=+@ormu"G>om 3Y@-Bg,>UQRx3Mlodͼ\L/{|Ew?m_zjg /һ RZD^;wʼRWd]wFPm$ =B/K& ۅ&:r u{Xt)QBS^U4IGx C{u1U;I>~˻.{~K{?x{ߜuNuWh~כ9}YNؼZ߸>s\K]snB 8/w'X_z˛]wZDA`H 畲&C[S> Kwor:e75GJ7zQ^ϭ)׿JguO`ԡ4xSJ}MB1@ќ{g"ٯ*ל0^F>nLIߏy9  CisN^zsVy^yZsK,Z饗/{tU-lċهfY_yW-T7LzH-z..ɫ 9FXu زq#Udi6~ѫY;p^n` ox=s8;K~BOF-@ϳTOpv򫷼$ҚUB|RCaK/۹EA)<-f-{Kr=KYwnq%^DްoqUm֛^77Yw7zGP5& lX_zO^a9;& .ѫ4,\IhLlX>~Myqƒ嗔.As`w}gpiwmLOa1@3Ywwo̪MɼKAMkzwS’=c3#wt\XzWlo_>c7?nOަ?~Ӂ^z]WWXoEozTŎZ+[}t^)oo>vf$ GogzxHTR7 "ʙ6DrUl ]=|~,`=u+@o+>ܷZoxjV>R;mu~qEw;o~9#^1iMpOx_op]hbGC3mox-KK ǏO2l[5o^z7[p#]Aq9/nz_W2+7VT +]W o^zXB #\2Y_^!n+Z{ M גOlc~Ӓu;@oz:&y\Hv~B~8/lV0ƾbn/z›Z~ rvwr[`|pL&y6:cLʽ.5@);]z饗^z՛4V/"[zΛ<t[/~>DP5]h@iY_xs+"ndQ| _Gė'oxBEuK&&COa{5@i \~ٟ?-i6ƪlt\K?vZ;k?U/z饗^znX(UL֗^zs++[}G棇J& Mh̛WHoH3!cX6" jC oDk`-`ܖ›z^d>~XWZ'ml O .2@ooN m%o}fA.ڵ/c=~sW=}<;s-5Gsι饗^Qptnυ^z饗i֣*yWJY_qTPi4^tzm~O>6EĪWK.#@ooN y+HMݥ۟=G?-<6| W9_\~~Zg=Q~4@wkq~;MvќL/550 D?-W[^z7YʖpmBf}W=W+e}ƫ$JK/閨YHh5j^[hٸ*#ѐ|WiXcdyYeϟWO~k2G6*30{Ud-O/нtvwҙ ̾cm/ 8ow[U7Wb+#_s=K/bwmFls^z[WHopNPjY_ftbGq= ; u7K7v tw|-KK>S]E BCUfy ?dW.;@?!}tBg%eo~.Rw竲d^7Փ{sΏێ-{5 IfJw|E/]|{z]^z} i&" "KpyIAH3Sl!Y Fi4D '=~]bfߚs>ώG8 ]M}#=ltdk=!+z^yW+\qvmK/U|װUWJᕭwzMUTr/KM Ml)u^7UkMTFm M)\ϫ_HxW7[,EK_W݋ǯW9z==d۴Ro4icJQ߸na3_CbJUɎ_zw%$i=S/.z饗^z-G󟋍dW oHu^)=o>N5>瓀/r:UfzwSmxٻX}뾚.@ՅDS/ظٟ'R$ޖls O *y3?V0P2RWL/~.5@Y]T|z饗^zƪ6E2KDZ u{^+zjr/~c@NGf}v`#^D*زq}ɒ+me[^_į%T?e>|{ӓ4y=Wvz{vǵ~?m3@naIW_zכޥs>tn^z饗^S\IhK/zsuwd7CP5X_z}9;& #V W*Hև+x fj6.[pi={#@?&@l'Bo}»9@׵x B~9ۋFHW;\K 9ws;饗^zT GU$:/yyJw-W`op*.4zWvopSlaK#cX/yEUC oZDAdr=(@?!_NU-e".Y~ ^*޺!Ս}h _MvCͧr L/zλ};f饗^z-ӻGU 3 f$ngW92}DH3'^z饗xS o:jyy#]A5+[}ܗFҽ敲ƪp-+| һoFz4([{զk;7}Qv7ǯl+zKKN3>r?@k>z8..wס֗^z}ާ9l0S-z++L/>& 72؈oAL7}yJp>+7Y".44/Kflċ98K޴ZTRzxG2@ E-9w??+w|t7V`3#ud}Us^zJVy_wfMIK/{jk6K/KF'Wכ;:]GI@՗LlkH-֗ޕ{ckۅpq~zh~Bi~x>U~E |a1ߗd#z=-427-of#jMK/ƛkκc/>Ch_i;pmK 3敲x5{4|t֗^ѽ9.K~Oxf߰_^ _W]Ex׋:G{>{lbb}(7-7 ﹳٻ3饗^z7[p=UFpι߼iWhoZRIJY_A@d+7朇4BTW%$j^aJ˶E}cAD;.[xK:GxS;zlZoV^Gg}饗^_{M׏TBg.[f}گJ/>f齵ߤfXZ_oY+e}KP7jA@9d}7]c#V@ #i8{mEՆ٫ ?"@ˮ"_d,}^ çx=ޓ7t7@{ycͧ}$ӫK)޻f/~Oڡ__@zO襗^zɽќhd}'݂a}jm;"JPDoSNoF #UX_z}더F\+wx9@ͧw=WxC|ƽ+*\K^zܪ]I!p!σ'7O/zk=wR qJGEzM/x>S$ :Jm^y5soTbDEްoY\Zn-y;oG#_HW2BzKmB۩r7"2B JC~r _gx:@f/jI xpu2wE fYGl#z\˝};7}`"PhX/9i_Tkb{{^z{Dd J(4m֗^ἚD?Af v)mJY_у>A7:vMMW8ڴp5^Pxo>jc3[= ̗%=Mƿ={3"_l}Ѿ՞!GzK.|t Ѝ#=ltj]֗^z{6ۙ/^z[@NzTEJﱾKloFMr.wk^)+wFPm QY_z=M[J-u}sx{]GUlkRxek;Ŗ7O}>~{ _&DS;.[ȋgZ/zz|7=ܢW(:o9 g е)^>s/+=sFE5//`^e֗^z} F kM~Ugޣx{΍,•.+7YbBH3]w-\ ӑY_zX( o ױ+,+ʪG[GD~Bݺ:~d񒅏}5{}Q_,/<#q K۳dOb& ۅ&Bc4Wo{=~'hO_i޶iK/׳XuxlZa}W0q Mbx6\@\X_qBPm \@NGf}ګLc#^D*زq}k{tEY}g#og=0yru=O=K6wf/2@gN:G;:Arjb}Ev=˛W:]'͹8 n.v^@zפz!*ԐoXz -J' /NZէY_y#=Uw7BK7oX/Fz^fj=GUr=-@o?=W+ _]DޯAeWf/2@ 6~:G%xVӅ_/zfNGW.ۦ^zO˷,?/`t˛^zwmKc="ye}%{V>~+xkJY_ozPv9ד/l!fa{7ZDAPmHፖX(L_{' [YgC+ mLu+~3> tt^ћ_`DƖRGBx+[}饗7eAsnA 8z3K/ewetb^z%9_Mw~'=-Hv=ӛGsJGg}=m Wtz/NQX(W{Kmك6_z' ۏ~z9ͥ/3_˭4@J/?pEo4s)g~9?]*zқmJ瘮Msw:-U}yK?{Ϲ q?/ K/ƪ$4Lh:/J6@?!7>Yj&-Jm|iY,BLM7)z}ͧ9{m֗^zgo^a ?,uc;z͛w76QY_z;O?,"JY_y6~֓/-T9O-֗^z]*&" v-)Ecƾw^~[Ygv/d?{3p.w#z=y=9@@z+*sByQ;h@/K)µm>՗^zu-@˗+e}~h.› '>8Q+7zG #e}w`^zTEd'h9tWD]QVu#@53!?L6}G?7<1XJ _ ' ;s>o_ɗm);O/w꽻t J Ŧo[K/.%@'$Թ{n*ࢗY㹅Ry;97RZC֗+zu-@?t^)o:@P5=賾~c3_CH3Q4֗^zU6p5YBf Uċ98n/#7K duAG^}dϺyt^ۙݛoluj=!\zq=wixN82{ {MzOޤfx^z;+ +7\ 6׭JV_Uۅ&\f}wEޔzTE KMo4<]D>6 _u_ /,@#A݅; Z 9{iqߓ[ǯ"^z.P q!u`Kr-1K O~']p^Y0K/6'va48Z6V_zb~wpRw`SlaB>+[}n= K/)wG]\J5nO%ͅ>5 ]pOڵwqu;@U ;_KћOC>=G$[Kx߹i[9 r]I-oE=?K=c=J&J {폽k~_+z^zO{srtEse^]7uě^mŖI Pp޲ƻNoܚWgGP5X_Ŗ-B۔mUgT9ޛ+ Տ3_J?}5j>s=@ǂɮA_gd2@ȋ6r}sIl#ٽу>6s:2zW,פ^{3G{>z"@?W hC/Go @ޜ^ޅorqDVRAd}6ƭ'Y_{[r s՗ޓpݫvT.ԥˣqV.;@?!W=ëvkw\ DɵPMOYK٫q§6V/#@ÛW(oưꈕ[(֗^zWe{%6K!ZTAb}饗ީ7 |!?|agXyCr9z_ lW.o\3U.wjmsQul __uƯ~5{ͧCx}W_Joxm ]l ?pxi mǯDgz& ۅ&%zf}饗^zE SW欖;'eK/z=&L^z!"^+e}MU-UJW (U-r:63 -CLJBCj -l\KjW^UK_}~gs(A^GڗY/xs]3EO:?$nK/";~{\|YK k{)߰X_zUF?v=@?˛2tO)s=ݚzכT lP[3 ċޗU oDA[w \ɵcQdA_'\t7ߴyÕ.:>+D/zһ}xd饗^ze&C\OUp%!w>Wo޹ꊼ/)]_N MW+[Ya}'f W-)^xTuoYuoY, oF]BzS֢ !|~BӶ>Q8}3`m{.z(oa +ƪOM֗^zW=wUZ^zޠj`- a}饗^Oz1}5|+@_7\^);7$opL[S? G*朧j^K/nz8bKhrw uEoN8GЛ(@G& g8sS~ K=}Yǯ,gzW|j \l;I/> ӍenǧMz7\~1/yoetoiy~oQ\zPmLuÄ%7zG #]7B]5o[>o`+c#V@TowoI^fzl6~ /~)_Jٯ+xҘ(~s~ }r;/o<x~W\oH3PlZ/mKt70nN-ojJBÍ> !K/;wF[?7:5}~`}i^8~]]~#=luj=֗^z]C @wqy[Vo^z׃ޤFz"2u^zw0ݼҩdW9-T> (ۅ&:r [xlUySzQ.7?jc]h <7~;y?kƒOl??ZqKw]ky^;?_zќDd}饗^z=u-@w{[ 饗^zWͺ 9J[ l^G!zRQwMև)Fяy^ۣpKHᕭoXkb- T2VtGUD{B?=8k?׎ xnOz5@ќ7?~{U}~W(hyԛ^z}= 2n/іX[^RzÕ.֣*JK/d^)+7[nH -D޶O[s^ 9FXu زq#Ud6W/~i?{ca?~a<ѿ-|l^ ?r/$襗^zMnf؈-zDyxޤ T2]hزopSlaK#{8_6~G$ ^@z饗^Q;/R"%e$2y+KG畊-K|~NևHUi&#tHTBz3mYz " rVntt/t>~f3_mO6k6U_(^y~ٽIvʹY_z Ks=~_|}-<˫K yc= /KG{%%$6+wL%ZWhoBjL+z^{75$tKM ג=_ +C/,?]U냅uj 6xWU߬V^b W^z|zD}H/ +,7Qqcl m[xl^z}l W W-;rΐ+zJkt)j?O=%|~B\I 7`  {yۋEf!G|((\ Kqj~"=@n8Y|E/oyG'C{C| K/+&" M)W5|{ a~_.:e/[Bo~; DP5.6PjV_zfWP﹔ EwL7R(Nz^zr=OߢG6" v )o7M޶e[f}}\IhU-wbژ 7у>9J^zr:6bD{5s,2@_a'U8+m#/xpU+ 0Ňx3}ޒGLMK ݓ;Mbzw~,`=s v|7^Y~۠^zU-lċGtP_zJhU&4H_ߢd竣AA՘)ǛR)~CK/* W皏o><\{{&N(f{>3}6uW9:?_;ȋʍ}U7fNG֓+[}-K/܆pvd6饗^?x!nGOfRd^zf]7?FTrA1U`3 v-\^%Y}饗^r{.֣*65);Z |">}\30tpywmόI>{g|u7@_w9牊)W7^zے{nQyϺy1z饗E7X(wXexMz饗^yCjkJ؛Sl!H ;t$Ji)*zw^ޥxw EmTtt7<>[GccsKQ{'?ްy7tYGI]*(C-z饗^zڂj^zwQpRg}]W^Ѽm9tW jrǕƪr:"ws󆹞饗UlċUſ[6>Uo|u CO*w1?!Cl]Gea֏S9]~%6\+jQz֧i4lQZ^K<2Kߣ&XE/;7?zj+_›+W7Bc`+ U-UJƪ65Kzyt@ʶbk $4uKxڴ_#.53h|ƛk~ cucяǯW7\`+C1Rxe^pN2KssK/&j6&5\Koج2z #oF 6+7\"Y2=#.44ByѼ !3Yo>F*#7y\J'?%uvn|kgu»uk^;<_y4@|PXu͜A[ K/<_;;M|?,񖧗^z& ¨g}饗^z]7r;s`JIV߱7FEG=֛`="չ饗^O{ckJS ZZ5*@%:Еjٙh ?`UGǯz^Qe#hMێ4߼d7襗^zӠo\zûGނdD0 {`JᕭK/~*SW oCCwQJG/2] 3ioRSa*xwo{FAb\ ϭ!&W5{-\8<_y$@˛Vrť:-_z}-+7E罞 dz^zu+`3C3֗^z׃ޒ-4-\=7朇4Xo:=QEs}\IhU-gz׳BƵ&KHla#9d p-|#>D5Wϩ2dϯBٽ|v6@K6.onJ[yzO 3K/ 7pn9IDY0+W;s>Hp؛2zˬ/#oZTRg}}M MULoAPm [7kX؈q#s饗yf@V|u;5gkOo  !Avk=ࡧ-Oo{nx+V5V L 8?ǪO.+7#y}K/۶^?yީ^EK/mnZDA77i}n@NGfMMBb^oZDAPmp=K/FJ-E4+)]˭>tƷ^OZ> w_lٻέ[ɡio竳/Z!bu1g|Z"6.6PjWfo$I/>=~O7` mK9GK/ oyJf, '0}r:\޸n!6[n ]ߠRzTE~=|9_KP޶lbՁs8X2&@i ϪC gl>:8h==i8wmQX7|uZڸVq9?J&J %)^zƫG%JI~yy~O ݽ`v$wXux73(\oQKҽ z' WEHV}Kll)u[ߘ{у>A-U6q5ھދ^+ajRõ]BٓT_l~>;H[͞ oz|jcP73|7˪_{1@ 69Bz"=ltd^i^MFo^/yO.?^^z7Yz+ Yb}ۢ^z]7nl{X-\^wFPm QIv,3y]GUl)u_y+BeoG7=U{fYs{+Lߧ[CO[ۭ!d?o7lߖWGDP5]hrVT Īa4[m_L/.ӛ7^tZn~C>0XG-6GK&3iZZDh"rB=~/ZnTVdO6>= Ua0E|%7i8)֡lW/\+3|R.Wfo^z} WX*u֗^z饗ޙdB&K>u v5WXBP5[_6S -IvFg7R\Mjҽ.7p~>m7;sE <;|^xmUY{/ZH7Ni~Jm\|%7\`+#_ﳾK/N=wݧg}Sf٦;`_iﶝ[[^{ukNT^z+כތ~zlJp>%;7|=U9vްh^uU^z+ ؈p3[=KȢecݴ3O)CWw^kzx>Kph"|%7Vӱwf}w,{ˬsg? 4)-wzrxsJMnf؈W='ُ^z饗^76ز7T9O,!IAH3Sl!Y [~pްL/z-X()4GWqϞUgC/}B.Z6~y1,[`Ly'Wr{G7(֗^zWUhyz;N;`΅/^z[X(M֗^z5W,Ͻ fVA~WHofcDڞI}xw hpsz.饗^zޭXָEx = /k>d U^hp>K=Oxi WgyCmbӒ+[}饗^zE6~/8iUB/[[s eUyզm5s(U} M [Jb EޫQ[-PhZ<_K/ڸWd Ŗ7^ x2Q/}9O`k!6qd_.u_MUd"PhXRxe^ߦd^֗^z dC/ XWXo7M0A* ^{ }+WUJ$ ǗDƖRNy ۼ=ū6m\+jRCfKU_ֳP2z+ ST{<ѷW[f8svwh^_P$*zfNGF|kJW0ތ޳F`ZC H/˛qb}Λ^xz=MTFb.`–RGp9{uwvj5oZĩ//WLMzUX(Rg~LXq ɾF>gf I-֌Oex7[Rꈕ[(w7K/Ks]5,z^/襗^x75GUҝx~^z7+WQE #QKuuk=磇4ӕ٫p57<_K/;@``-:n9]{v4}N/Y˛6|b~1?[m' <~3M M 4+[}K8s-SN j2^zwZDNK/KiI| U-UJWXv87=^z=-CLJBCjdܚSxw`ݳe8]8'Wm|>k'^Lv'w)W^P^K/K13gL/~U/`a襗{cU "n֗^z饗^zuݛT l|odw8֛oXpm<ռaKﲽ9•ß]By{9G} 8ONp%{ ٫z:yzޱOOti:Wƪlt饗^z{Mxg~襗y!n[,4:n(.[kE s>圹_D.]wt`B{3뾷I/.@NM6F'7KrZk!Aݔ+&襗^zw#GT}ld&y{7VP.w[~.vL/yWLom>l;Ol߳񮫋)W]c7$+/@w{γ߮WyJ %W|A/zͻ}\VIC(tf߇ӏ^^b*5)/K/һxofcKc7Btwr@ܟ|"-_/#ܑ{2vF:Mr|Wl|ܫ,П|B>7l,q@?z}IhZofL'W|饗^oxuz}]I>>N[q]+'̼4Z}|饗^z%27lkMl)ua~ސ9=ڀִ܎p?]Ap"{#K3;\}~]s XNmV!qQ?6[#UhuyhJ-_z饗U{3xWZVw֍v+7-qt#>^z3^^{z*dC$J-M GXoX7RkI,spUK*`;-x:Ѳ M}3Wd@mzw^Y޸,O%$h4PxlK \ C<^]B^|9饗^zw!Xehfy/Vh}T#w+[־HwR=;!}-diNzyK!fxU_յ叽+ U6PjP{uzy>K/b;@7rǧ^zg;>D5D*]o^zןޤXƶZG|F -B{{ lςy+!̗ex3^}َb"{m명_~m5 zUQ0Rxe1o^z饗^! ۧ^zE:H;|饗^z饗^7`'–Ryy= oʰP)Eiqy^uc_Yѓ*q‹7I tYW-ނvx}^z饗^?;4qq<;۱VXEc UWo^)@`LJ_|yzMI [X ) +P D*!3__vYR >'w tYWqĎZG޴y}̫K/+F~x ]'ؒ^{w &B vrM+@/K z# S l{7o:HWp;[X|Wo`b[k.l~+7~A=8??(E,?ts3Q D׫V Hs}NJS~^z}-xԻ S~;aHe -K/K/{!m9dJ6,܍q/QD>dyZ -wtH,׫XF@57z饗^z@I]KǖRZHAeK/KBR*3B 6)uG/3ktwO&8\^,П: ,׫`'ŽZGiL/K/~,gsrq^^z{zk!ZK/K/ F}&ui$Rxs!'KU2_k:ˏGg~;7Corre[` Qt8?~b[*^^zޅ9z>\,ĭU%]7Rp'zls|饗gK/z+ j,獴< 9'K3|6Gwë3__y; j7o" \u>~-7%*R|/Rx~?JI^z饗zVAJkͻ>>bI,+7C/;0_{z~X.-V +w6@@5*/'o{< ѝEK>tsozf}:7^"({e˗xK/.Г rs>R?ڒ|W|onހ:G1ʖo7I/"xz}q>62U+I*[ab ; D6$^1ng;^}Y?h/3_ t/‚Wy\BHwOή+C)[XWWHk<]D!SOYz Vԙ/y ۡ3W~>s.zc5;4+[zϫq7?he>tka':st+vy޵ibYxۯ; Jp zTvQhs}K:K{])]vjQof}|zwٚey9Ѫ<%;*W|饗^zM+7n8֚R4lx[Kd ӕf =+7wob@ ￾bz{f\^E}cES lK^^oy[=wO~+!^0_z5饗^z;TdJ LZW@oG@5.g*+9սY?{/دNj,׫n !K-z{,e7pWTZHA`2_zovLHeL!|C|f푢 mb5JUL{>+n8x^ŏ}zV6,^EN0K/>X~BTs1E+7TVXf|饗^z]פW6of#֚LG8$jy<W0b'nDp7'Y{;7nۮE}mB>^~z."YH-_z饗^z}lŏ?ag[- ԛ^zc5 H6^xK/> ɲ-a1_zw262_hL|V{ssE_7? tYoecK#^*K/ԛ{?_ԋKh޸`Ob;AkWL/3]xJ\c U%>JM %ShvQQ|^a_7CC-O^kk6>|"{-|J9R_ Yj!Q#o\d>K/+=qao @zM[xK/ۦ37R!X0y}L|F"0`Ķ|dtso48Dck{}iE3EWV$2 ?zgϹ1zE/ϛ^ s}j>IŋŢWLs툆 Ԗ#^z^y5zCK`lXEC+Wi5[|Onu ?=tO[.Y*_a㥛K[tq_$*TT(^}?[^z>Rg0$}ЗKǽo#Y/K/fw`b3[ K/JT n T1_{G?+&V_yK<ZHAP0_R@b1_z+[F[^~aq/X6x@梅_];{@aZQȵ,^қW oRo^> 2c*&Ц^z6B -K/V-l)uMK/{"ޜ`7ob'ru~i2hj=\;>6CvKxG^{\,v"SzEpe+\9dQ*K_9P9כ,_xUzg̗^zw#n8֚!^zQ67ӰPۜ>NR?q~ӛNP/)Ù ` ] S~._aSxO*2# %+:٫H|=MKsnA^߭7pڀkx}{O}$jBmK/+|WT_0_z̓հW@~uj \KM{-tI hwžxUk%GTcˣU{u9o kzE/57K+snB\7p%= }Ŏ^^z oc^]|#62![ {*H (5 zW.vPV-^m杶@(Ղ?\Z?yo4R~*П:}Vϻa3K/ҽbqc}Yy 9#M"  d^z饗^z_-\ ,ݫKy Mu܍B{e^i!vrGy9WUu^}h-@+6wݞ{ti y˂yofTGM^襗zKL[弁n,'BpHXEde.ۢ^zXoS\opkaK#Ӱʖz&k}܎hHW'Ǜ6zg)޴d㝷@~3[*{\4{W|@{_,ZV-l)uD&U+zwE^^zl>lqSQ]Ȟ^yxㆃT9s|̫Jv>K/Fx|饗^Whݼ|+}TʀzF>>~+{;S3w;IE 򺍧/Wd{@˥rO蝧@_&v5yJpBʼ:^_{SzR?hO2n1 v$v܀ZHAPo3_Iz饗^z]7RJgz]7vvDCg֚.|u@ Mkz}B }V˯xl/O<*~q(16Uƀ+z]7A/+d=gܕjפq.nUlek3_)իK/Ry!m0_zw)lxo^5=7].ǻOnɣo;^RCWݓ nxJn~e^ to2fd M&ۦW}|϶7{y턡ޣFn /K/fʻ7!eX̗^z]7^41_ɺj X2xU.qC^};\z*h+WGO/z\ I]wtj6:…&UVGK/+d>>NZ踓޹^y ޸~ JK/z,WWjoFh|饗^Oxwsm6-G.ǛB}zO]+v3?{q9E g/k_wQ7n8ɵ֑oϹW2yK/W̗^G򏮏?wڽ>~+7Qj~a7 B|饗^zw޼^ia3[+7E//[0 (!/̫Jv}vydJ(CCj[نDxɄv@7_Gk.'W<謤^E+}pXw6,5R\e~D4 ՙ*?_:Xثٸ/ni3_z7G/qnayְʷ zdj0_cj TɑWQ?q~}+ Jϕ +e,xcT`۶.wxz>D@5+4P9+]]Wc̗^z==ypE︠WLRǭ̗^z饗^OxMzE}ldW'vejJVG~ 9{jѪ%7)Oߌ &B &P }+P>>fjos~!Je s~!J>缁=@=~>gf^K/.ۢ wi I9m‹X^z]A/[#W|sHeLbG "-K/neV*ސvuExu}|+i;xrʲm]b^^z&~.@ojY~wJQo7I/>Rz =%j6:v&4aNmKx^i l+Z!vr-͹_|^?q~M|5P`ѧ)y;LW@~}E->63U݉Kl^^z9KM+W[w)zve饗^zwq3K¼޸`'–RG`s뒷E/ݛq;a#]he |P6n)-dK?w f+vXO~=@|[okgz/^zW]h>C@5-K/īK//>%]ldUWp|w.o0ZHnޫKbHe+P>>gQ(+ywRɗ-|=~-r[^n5 `[kbG#oK)ɾ$~D/gz饗ޅdp."yǧ^z饗^z饗ey# +7I/ M nG4}+`m9ղ~wX|SEk0@J_6z|b/[x.~;KB1]襗^z׏^ tͅX:`饗^z饗^zwx}j`[k@i/K lxoׇW0b[kN?l@֧7*zb'o2ܭb{6 HeLJgz饗^zлuK/K/fWnfa1_z]7Kx[a[Jxԛ[ŮI{>>~˻̷*O?s_v{@NqZ9}y}w K/RyϝuYp֦Yot?QK/K/J=ld{̗^zw\k!{z 荔[W}|}-|K/K D;k>{q- <5+7O/o^z9˛曠^z]7g:,v4X|w/?+Z ~v9 T>FmԆ^E|@ԓ._ _"T:ڗY쮲@|qWYn>55>K/6饗^[z}饗^zmK/~$&y>*".0$bzVW^M.v7_7d/ǯo (П|K{>* '_u 2rM}ez}^>۠^zkŒs'zxo#K/Kb:VHZvK/z[F$y^z饗^z=wŋK/K/KGp5ԑi؏xkK/xSnG4_RQ|Mj{z[hZ¶/^@ s YnMlkM!zL/K/~>V`} 9nx@zɛ^z饗^z];z*㽦*ϊKް>*jvr-+7^#Bzo}d^R]mv6P0~=G_po+U zb^饗^z=K$x}C7^z@/gz饗^zTT̗^ +WCIfmaE bM mb5[|>z n X;}45oe~w)W]^^J=mwFO9ipWo2_zwޜXF@55ԛ8MK^S.ҴƂzb'nt}UyO*?]ėvY{@~yp@Zv@ڝjo툆tUث-p zwJo^z{2 G/M/mK/KlGpkaK#eX̗^zx+wí:ڢzƫM;U ,=P &"jK;&"3ZX )-R\d>KMz饗c@|c^z饗^zw*W7Xb#SEw+[2zS],w/tayHe|'-П8&~a| @xn?O_?׻XȷlwvvDuzuڊfK/K܂>o)zǛ^zsX,K&$˗^z޼`#=Q|o`b[kεl|. %/ݰ7op}߀b :զ7yKW:]K/9oAB9!n?xW_K/K/s{!m9dK/"۰p'6{Q|UClkMf}UY _?] ,{» fonXPr{7Yb-`K/K/w}|q9.Nd饗ޙ͛֐[̗^z.wxE_M-Tby 3_>,|zqR7`=3r\q9 t@/K/KR*".^zLf`- wH{?i7}&sO] 7xҥq^?un@eyb!6R%܉=?y}^z饗^{^77(K/K/F}w&w${~r^9n~?Y*_ wj nXxŁz@bud}O_an,dm9K/һ]D5G맛}-K//rKiqA@m 6̗^z׳޴av4wjQ|6llkMmv>>~+{_a>AsuqXo[Lgxl7^nc- 6\^$>K/K諾K-$ԟ68F/K/dC^eFFD͒+[z[K諾bk!mʕ~uj \{ƻ}|7BOo@?@E tasodᓛmlk ÙI^V,vgYɮzۛ^zW}|>q&@\z\^z. 6 :w. mqWYc  zuYJo2_z]I+/NNz*K/.7$J-l)ud%;̗^z7YvDCD"n8W0oڰPHW+.@?ՠ|> \n⫻-DuS;'Z4R(FׄǮK/^1G;~X(O! /[[6~u{%E _- ̡Wo;IhH>K/zۢ^.j,H/$r2_z]7R`#SEHo3_Izy>;.vvDCJ@oHo#6B}k??˺T?u^S,z5h Kse~wㅥ~?/y^z9@ћjy'/ C/K/zkWo>D@55yy> &I+WixcIE29 !vr-M G' ~B~?ZT_ō:&. o7UVXF* ^z饗^ ]' J2_K/.Żs2,K/KoQQ1_ɺj X _?q~M7Om@5 y\k}pBz= X )->KG +7aJ 08nel(O/7E/CoFh|饗+z=U'jz+j-J,hj Rqt/?|=K~u3N;"o4&J{vv43Nvޫ3_z=]Ihs'&}rK$K/{X,R曧^z}-Kistp?UvW|ZeX]?tmd/[7:![|>$66-܍q/QD>+[KjIzwOI5ǧw~N/gz饗^^$9o`[k@i/K/y w\,jm֚-? Ͼ66>xӞhsW7(]=Uo$* RRxe˗^zEy^z9}XOn㺽>>K/K/OJnfDݒ+[K/>ꍕ:X )Л P J= 'o>5{>W,kLO@jl"o@o;^ZHAPH-_z饗^z]j<3"7#ӎyMz饗^x%$jcoFH|饗^zλW0_o{#o|5ً>g/ξԻ g/-:rMw޼9F;/ޜdMJMK/z@wYgg׍ۦ^zg7I/KGjaKc7bK/JZC'#Z`ޜ`7ob'ךz~y?ŝw_tN'kzK* w9So3MK/y;.@WYMq7̳>>饗^z=P޸`[k"4lK/K/T}S5?LF@m`Oo _z?/ްW)P=\[+KmԆ^E|]W`y]-^:`1ǫK/mK/~mldدmJ/{摡x#m)7-Yr_Oor@rOXd{@žf`噿-o[abW l+פYzR"Rӻ ^z饗x#62UxK^B_eTԄJ=_oHo#6N]_}|/'7sԅQqK^,П`6XԠ5,yo׫\zBn-_zןi ק* Otoഥ7ɛ^z饗^zc5;Z0_zu7 z>[lɫ6m_xhɵFlH_؞@WmpmqE OuJT)=f}܎hHWye˗^z;M9-#Y gY.|oc K^K/~ ;:3_o{3<6%˗^z]7Y=abxӆ@P_?q~o:Fӛ/\K.J}uނvWv%skWo`ۍݼ)W|WjIYzg;/Us1E+7M/*'zuzy>+oP`#SEgK/7Xh=R0_J@2@?-Co4Z )W]_naG3P0m_z@ r_|l+zxox~#Oh/*@mz饗^zMK^aa3[CPo3_z饗^z]7v5g֚ ) pϗ&>]^ncv?۸A1x7y#ײy}Л^zC8^RţJ5ޔ^MBwWG9do"77K/K/KfvSyuM,Wm q1RS __ZI_ݶzd변TVc +7I/"xۓ{ϝ ~YUG9z饗^z=[9q)Ma "_@[J+Hl˸!R-_Eze{=g7^zÛW x'7k2(Z̗^zWVG3y[`̗^z]77g:,50 ( tOlc-YG?5_l^an,{"!^z=MK)ttKh^^ς{+\Ha>PP|uz9% ;ΫMXOR0wl7:W,=\^/y>tVP iW|N/gz˴FAiM|\7^zOf襗^zO+O5? ,Byw }5K/KϼRk!|+}ײX{@ẍg"k@o;<=ֻX )dK/.ۛw.YnɃ;c襗^zw}|/ek T)uʖ/o^)SԻGEM+7qț u_N{>}s-.6uimys"V-K/K/KszBK/;M |6k*6]w/T^V-l)uZșgz饗^zīXOS0_ys*+(^Iԑx>7w9ʈ$;饗^z7w ?vn.f\z饗^zg{-Ǐ~a4|-_z&LfK/*;7UvDteO^E|3 _?. %y_GM퍕LlkMK-z饗^{-C^z=6WUץk T_ě,_z]E/;7^kJ7\odß @_@ƮfJs>g$n+U z̗^zW]E/{nQyǟe\7.3 z[w\W|-_y;_"62U)R?7sK/NN_0_xl8ݕ~o{:|YԭY_l@ec=nTuaymz饗^x--_zWvr-l)u9gbzz[K/zٛ5W~O7ј_Jz饗^zu@g D*65 &^z}MK/MQQ 7!Y& W1G .Xn'kPgz=՚ވƾx}|饗^zOѧ؎[q^z饗^yQ3_zeCTZJs|饗^z饗ޙن<%+Wig;EXlk\Ց(y>yoŭlK/K qǝv|0K/z@g7-w7ob3[Cn1_z}2_K/y3y.B :0}! e  6.4G|G`=-7#YK/ ^ڧߡ;*襗^z=hB tKxn/^z饗^z5W.ޭua_ f|LoT ۉ5K/a =jf^zu[xC!9|Zs+7Zڰ`.ۖo&|^{;y!BT,aI^,g't/n zb/Og %N2c3_{u_zy>Kj/uK/{zoߤZ{+[CLt6{幙/|W>oڰp'z%Ba/*?pŸL^{T^M^zW=aJq=K/u7t)Qk0ɫ5MK/һ oVrBh`z.ŅS.Пl[&v !W VZHAؕ#ߦd3K/.KP<tW|N/.7/}a{]|荔8tW䛔|NKh6k2jŠZ}Ӱ '__v@i!g>oP2y5z!'#Zȷ!L/x +K~֛rܸm^zE!Nƿi:Wl+mXiG_p^1_z׳ގ+>һBo^z饗ޣGp-hv«mwK߽Y|饗^Oz+5H諾z?j;&"|oZHnd;I/z;O/K/ǫ {(-xY_w3߳EzWMK@+y>$1w/ߜvܴdz{G1o]/Пhwq?k ߲^=H|dle*!R0_z饗^zV^~%z~MIǥ^zK/$F%s> G%߄d3K/rxwrM܏u=͛6ғ5޼ۡ FЕo@1WPo^xզ7yK%˗^zEw+w. 婼I:z5z饗^z]7^"Qn۷WxK/KR,jɆioaNכm~3i/C $mK/K&]l)uK/zګKsg'we|K/.ߛ^z-at^zwY̌ެDXK/K/ZTLXCQG_3s8$K/MSxT,ςys?.}uk\ՑsZ|^aN4d qaK/{}^9O7 [(H/|ޕyFg@N|Ӓiɾɘ/+7\c= -">^ 92# M6~u:@dz%7Ex}|饗^zI0^_в@z饗^z饗eJN` <ߤvz(Ed3K/C3lH-)Ujm g @\ !Q3_Yz& 5lدt/K/;w3R_+6 MK/һRoGyls m_^SԐ3mo^ l4FG |J *.g{w&J Zz ; D^y wy_z7E/>^'e6tz"K/6$v[HU3 ( m_ޱ旭_$Iv>$^^z]7I#?%Wjƽ5]ֽm >Z.Х?[byK/zXX H57Y5Š/x3Z-yӆ@P|6ȗ^z{\mK/^N=wVqzK^^z饗Ez.ʭ\etC Eh^*KnH{7TX\Q "7lkMj pjoG@5 /ȫU܎h̗^zWmK/g-{B~x'Y^z{xzvwheց=t\+?z7N.O+y}^z饗ُmH|'635<;6U knoHoc[k"V/țkx#?oG|%襗^zTs>Ȣw I/K^2z!e=k1[C`+]ݳ,*eC}zjK/ ]G2jU;WiڸbQ7'."Y@~të쏾7]Y|MJ0wZoíL՛޶dK/J=z_WKz]f饗^׽RҺ?/޷Dxr|3K/MzLn*a3_QQ|^5BzY7YP ]K/> MLK½ ^܆>owF{W`K﴿.Plt>gcsi t$ӫI׼޸`3UV,T/M%滗wU9Qȷy)߶t9D*K4{uK[čg}XҸMK/+7Zڰ}|茞_+p3" *mOx ZGsdW&oQBoZXRg|ޥxZYǟf\7^z饗^z}Rv^~SnCOxJy>o~4ߌd3K/xc5M2,BoZHA@5x>0n>?k@o;&jTRһo2_{9k!A|饗^z>8K/xU 9e;@eՖ_]]/Q|^zW$nz$}|=ZC'FEMj|>a`lZWޜ`7ob':v~K/* w9n|ћ{~.@?|i4rj^w4{uK/RyU qAd}Y:Ho ?Y\q3K/~}GE.ϛpF9Z!V4}4l6W3oZX֔«H,^+7E\8i}{իK/m+7Zb w\^/@,ߦd3+7CSvRuKxukEh4PoG@5.^7M/~5 zb{M^zf=]y>~$q']/'D m+^{z_GTpm9 mHv>+@/QrVI#Ynz%w+[ŭl'j`"Ԡ5,a!ڀִ~Ԥ^zs- wyZ6{tIvzv襗I9sMtHBj^z7/eÓ|0,1*e9饗^z {IT}|% 5 ZjyӖ>Xj Rpk.@ rЛܫK/=n0K/oRoba#SEPo?=\ZRy8*'yU{l $/K/nGHM+7v,YwOoc+[b |MmX^y7yK3K/B]oG28:>>aWFow_"F/P lk -XqIT:pd,Xoܲ}YsK/һjoz$VeBنEM>7R`3SEaxpt~\Ȳһ8oílWXo7I/@϶Fg[Ӎ9zK/K/MlfkH֩_i7=um|]G.K/oja=a7Gʰ/yB-p LJu @ؕ+[?/K/)'q>d"J=ld{y*Ǐ|~7 8>|WK,9饗^z]7n8L"U3_zX )۞ ;v:-荔[һ$J"r iVdEO/K<xU T_ۤxa$k=s>~wL9/[^zuٻk~$D|ʛ3Op;hu,v"[O(.W-HeN3n^z饗^o/@\^zW6|9o PéW30zݚcY_]y ]O/K/~N_-\ 9qF>>Q(́e`8z܋/tٮg_z饗^zW͛X7/Kj!E܍'*Z'7Ta3SE#Yy;Ds^z饗^z%VO7烻iǧ^z^^ѼqĖRGau@ sε-K[~_|v@^zWWq Uܯ^7Uv4ʩg5n:~; m)Yzú\kqG7(ףxj \sN0n+Kҽ*ELsg'uUG۠^1zy>Ko2F^wQ/a+ߞxs~}SC8rz I}w&4YwxMwJ6]}~6饗^z6ӈk̗^O{զ7 /<(Z."Uʙoɵ7,/7gʕ/'{ >y%jSxuz[WJﹳ9 . 7^z7C/ ͷZRHs 'oM7nѵ˞[CZ;xHso*K/`Tc|7QVXFBͧJoʰPM^z7M/ =w<^|}zc7G* -K/篯r\JeW@kCW7Z=T^qizW,߱7vp?#W8w/ĭ@y[ޔd= Ձg^z^z76I0^/}DcyeK/%oAoba3[El̻k#OZ6qWC _z饗^z HLKAHbZhʀ $&vrGgK׼yz饗^jK?7^9EK/һro>D@554+.@k"6::Ez~m^q1 z#KRP{ Z3F*lfHۏx wyK`^9Ķ|dtA/K/һxcuǐ֋ ^z饗^z͛֐[.@On?Ȣֱ&*mp|^;l?,G_6(:B߬d+zKѪx|t^zㆃt[,ҵ>׷޸`'ŽZGeO5RHj R/K/K{@rBI Ǜ^z}-K/+F65MOz.+{_v|R;nYxU w78]7n8͛ɵo8曠^{wrMGHZ̗^7Fq?z.Ud%܎\/Z9^Ķt}~K/{饗^o{ .z;@9߫oj˧^zū+qopP͡gn?-|n˛{ywN/'-G^qMu8y}^7X"W L+^zWb97 X70sWZK/K/3y[rxw }5y 7'Q4 --g^ONPO[p/F*TR+7W7VN2a -_puB{7Zlc- 6ßi/6饗^zsnB\ߍ70}=)@/zEzs}ldSO#.K-W{Mz\*g).Zᚸ7q|NJp7V{V0_7mz]w+Sf4T|E{w&J po@VXEeKy ; D^/^%:E{+@/{m[s1@/3]7VԱkA3_yg)vHe[K~}C7/) ,Ywqㆃ~5{|KV͛X7ʖLPLZZk{ wVWmKv>/3_nN5̗^zWՙ/Kw}Yǝw9z饗^zM+7n8֚R4l_z)[:'R|eؚWrWz os^0_ѽ&z#ES9g.z[.bhuyhԛq;~2޴a!69{\^z0n^zޅDӎֆ.rK/;T|흴@Ih>_ kۭ/[xe ucהz@ob-vW^{!' W[̗ޅy{3*45so`b-`'bnz[+}Tr_ lK/K ̊ /ǏOޤ㺵D":B{ K/z#62U޳ g87&rct.exews K"Z;\ka7o"peb{GHV$=lj7vvD;h9QoHo#6Rxe˗^zWfN/g?`'W3G'KW^zmK/kMh#ǾoD;gyTk݋*Nj7l|;pXI1qN|}5&y+ 6ӈkRx)ݣ!>."Y«4m܍ƾ~b*RdzxI3_z饗^ zw4۟6nvLz^z=ۢ^ze ;:R%h}{MAgϯiGL}k1 m_)%Jeʖ/rx# b*ҍ!׷ͧλ_VXF:ոd_h`|饗^zI/w;tK/K׼`LJ_XGօHӖu7-!`-^F-nGHLKAl1ʛ7lʸ!R|̛7 &&b5[|lMKz饗^ {]~C f8٫(LJnx5z%7X`_o Z/*vq/AT2_z]9$^`3SE:lXL`^9ĶDlbp/KG* -}|>ZK/YzM5k|2F_'> oUMf7,^E^fC^F*s4GSoea/a7Gڰ/}-_N w%6L HZ Wy> -,$t^z}K/RH{K/ zT)uwpC7X*г9@?5`ay g/Z͇c}3>Jofc[kbOo#p> zE 2bY>wrc?~-Pusy7ob[!ײwG3R;<FJ݃-K/}|饗^zz@?7xϡe}O/K/2y>y�>*?Y j X@ރ{Yw{Mz/KLQZ;^K/K=Lux -7|t޵Ql |-q"ob7}~Ly7RP ])+%7Vc7Cڰ6%˗ޅcb鞒ׇ s C{7Rlc-`[k|л_ Rx>7K/K/@O6Bȝhij7'7)7A/yx>62U3>[@2s>+xs |>4YA-B /ݜWz%9^b[kb`"q9+)w+SF4T;!Y.@?mOIL|:@@VXEԓ+[bh^_z5!>.@g饗^z۠WofcKc'ׄf:y')O~y߉=Y\1ulg/ݴTOSJ{zUpz5op[s{^Uyw&#i ӻ}|W͠q zTv~Wmڸn_ K/yzㆃ\ [J=w}|/+vVZ&%ۗZ+=Rw~-{;Lqgߖd+xCh@ <˓KW B"M;=l Zs-%d \5!W ḱ3~襗^zz^v\w[y&Kl;TgR?q~Mk *mkOa)+ rGpo2o-_+uh/[eʖ/rx!' W[̗ޕ{g)П8&]@2yCk!ys^E|WndK/+x yhe\7ǧ^zWtzy>3nba#SEPo坵@?Ye/qps>KiѳעC<9g >tӞ;m.%yry So@~$d|wW3w |f r糀my/0_+7G/>.@Hq7̳>>K/K5n>D@55gTw$p?X+8x%wl?a9#uhKpZwOo#6ٞjK/󎻧q/F,_cz;O ]Wiڸ}}uqs!or^IgɗyK/zj=Xf;zn?K/f+w7ob3[Cʰ\U~{{9BmM6|5w o/j]Ww+ GKz}~?^z# *ҍ!דy \Q.{J*65zu @eK'{z^W yEc襗^ozSK#Xb#SEsv |ߧ6_Qјqu6|*wg}wSt{@˥S׍׫Ǎl/dyܸ`#YD ]0_^?̷|ws m)-Ggzy>+7M^]|ϝ^i{`_lK/Bx# ¼(Ǐ}qة`;+[a0! +T?hu@jqㆃݼ\ ߏz֛gI#Q2ɷ ,W}lDʷ2nGsy%7g:+ׇ̗֚^z饗^z=Mҋsg]}')X/M~7E/|9o PÅzYg-^^j6SDɆ#W|7l،e/Z˞ZZHA.V/ҚޜdM&ۦ^ze;zn{qIJ^$y՚"Re9YwxFsXC ߲VRd/@ꂅ_xG^[Mq]Up,M!KOR 4MKj":j!B"yhMW0ot*/3̗^zCoB^ sg-V>~cGacJmK/op5ԡ6yWUO"x/U8z̓ïiًc_^^]T%W|.q?E2Wf:̗^zU Kk/ Y~>gnGsXO?W ڴPB{K/K/Nz`uANZ/^7^z饗^zWT_ܻoEQlM! |!h 6=E.\ZGoSUb!RxeWfooZ@ofc=NBCڰ/QѦ(yc!~<~3[Bp/%Y{Mz饗^z=wң#֋H/y;|+ߜHeLb3^Oo7.Q4,m<-zK^q@zClkMM$i-k.b#Aw`"Ԡ53 }wR/y}>޻VXEԓ+[!@j۔,_z.sisx;|WTs_W ^z=mK- t>~[m,;xŮHS,|覍'O;"^t^Ↄ݂mX#Y zEyi ^=y>63Uɼ~i mJ6j܍{˷5Nݼe9饗^zk'o%Þ9@z=kJzw5^EopkaK#ݰ=b>>~{{8~vY,J ot@z*T9 ʖ/'/W.7R^TA([D0_z}V-l)uD3us/@9ߖD 骫EW6,zG l+7K/k;jYn -;*z;T=r>>qDO!ԧE*u{NE)e^E9#z7^~}ib7"i UmS,y> [X )͛y+?S/+7E' NHV97 X߭70јn vz饗^zuX,@瓛7ײuC vybׯs-|zRUpȳH()_zGHV̗^_{6U(`a[Z/U3:gyF rW|!߄dߏ襗^z snB\ߍ7Pz=x5z]7^"P[o~)Ǐ|>/m`?z#V @MO_c\p DKO{zi5K*ҼiSe+7۰p7:!̫U*K/2ys M&u{n7ǟ g|zK/zܻ7!eX@? nK)oW@u{'E(e^}Tr_Gz7Rp/". 2_z}ll)u >Kf%WVoŭl M-TbK/һ2o^ g}Yuz饗^^|Ֆ zT-|k>>>}jO^C+^θ~*e^j6j{z[lߏ7l$KoqN|f.Իk`-`Oo3_R`t;W[^zwQYǟf\7^zw1ބhu^7}}UCoR;6JuŽb3-tݯ׮W4qnNPxl+w[5I#Y2/]leWpoCޜ~DE߽2滗>ɲ̗yX7I/K' /g[mqO|K/;鸣}^ oV0/Zt3YRZ~}K/;ɸ>牚%W}|55Ph/FmԆ^E|ß/K/};N7}\΅}mK/%oFH+W}||IhŹ=^̅^)v/kZ.*V{MȤWDowG ޸`3]f,T|s8M$Ml+5䚖^_ۖӻq+"X2_՘/K/}\vqoz3K/;7VԱk!g:zE.П8~v]kXy[u)vW]?u‡nxrIz@׫`pǼdߏK߽#i$MKR*ҵ#7g:;绛7k=X_hlK/KH8n}wk_Yz饗^zԛo;֚R4lὢǿ`*ma㹋 b{"UK #ߏׇ^oǽHTÑ"_U|eF}cEK/f6j{z[|[~k5E* Y?׫\žf SJs}NKHzoK|oec=N2a1_Tc}=@2_z׳xx@g{3{Uz饗^z[z@?~?;XK/ "UiK^y_~9-E/}ute +~K>|~+>zCZ͛ș+7/7UAeڻW0P6-i-Hv>KTmXfK/Kw tm_으>H'^^z9}Is54sHc@w-rn<:o@ꂅ_z3Fl(6&b56^K ݼ{4 p?e5٫ޘ7)<x5 :<镻@Z^V^zuϛ^xㆃ\ [J}Ιg[#d/[MO]xK7m{/{YEp?28y$^ U p/ -"t/o>-mL/z<ޛ2,zG lK/饗#^$ tb{]-~;Zq9ɵo y^zJ #i*홼 y;OU(@ lK/~k:&1_z饗^zW^ I&8mܓ C/Kwր27}#}-|K_ܥqoP 6Z~D/>m ט/F*lfH̗^zʫ4֚-?:^z饗^{ZO$.b}|z饗^zMlfkH-7t~ ,WPt?aW t/Hej \?ۭ^zEF*EUCKo=oBo;Wpo^z~>ɺj T1_z饗^z};饗^z*"Z1xw+6>qM~j x>֏;nc')н4c5Z{zߏק޸~@\A>`7"j|饗^z c|饗^zWB y0v\ǧ^zW-Khݼ|}U=gm-| g.9NSr ysy}Bzx#i$K&wq+]leRxe˗^z饗^zw<Ի]5˻.7yǧ^z]W[\7n8 TPxJKfۯ_K;m,vP Dgپ+7T^$V]7-YV]"ͧ) |饗gzwjKNc2.0~d襗^z=TYwD{G\K7m<{ѽq^72op>\Woj^B8`Iz7\c#SE7'YcV¯]Md>MkyqZ|/@/I5>.x}m݂du oz;ݸqFXZknD@!״^^zWuy!>z5z]@/=E.ʲ ǧ^zy>RɵW^IXQo,Zԅ>O_X̸nn߸``b[k"V׋qws-GH7RyϮz{C63Uk=L/W3r+Yş g]`od}|z饗^iqĖRGa !_]O|i >tƓ KnxŎf~<< z}+__ٮWN5}܋dQH5y[WhF}cES lK/ͫio^zWbﹳ> [K/z;T<~3[,_6ri9z@^w$ϣ> ]_Sy x; ibzΫNj{9+[K@K/һ,iK] 'f;/-K/By#62U6{0/}'%m}u㮲@% r_dNGob'nDpyɮWJmT ]J-_{zaɕo3y襗^zw^^49bazWHo^zj6Z>Ww&~Ro@e /\^EE e㙋.@?|m j6oЛh޽{4 Kԑ|LJGhv|n^z饗^z饗^z6{??^,Zho x[KqAX7P4-+լ'ї]x.[x%9 _z[/YW h=̀j^z}+et׷ޔacPG܂v/K/K/NmK)QI/Kb*b>țjU? w^=ߢ/ްC^ huFEo^_ްWb=Q=KqĎZG޴/ܫKMKoZ|饗^zo k^Ex饗^z T,lfkL);7X?z-|.Yzpka'~}ȾG焀^zEnekI#Ui3_z} leP^z}{o^)z饗^~!G׋H/۫K$CTZJs|fr}$}Yt3 /khzP 6ZS4HeLrK/K/J-KDsg[AN~x饗^yYz}͛֐[̗cߎs@ẍ_9T_>RG*TR^z}T,܋iKi f$˗^o{c5{=)߄v7+L/K/һ,o^z}@;@@v&&ksRK/nk GHLKb*2^z}MK/K/{-*BK7:B{7lkMdC¸i(]/WZg/ 9f `j R8קP7"D Hetid2_z饗^z=MK/̗dsnCZ7AŎK/K/^*{$d|5=,˽@M=RP ]d$+7Zp/n*Lf6Wmׇ s CK/{ě%xK/J=(ȼ:n:@n%&%&W4o^zuë7" -z‹7/R;^?>牆>7l،e2KI)w`"Ԡ5,o^zK\^^z7uȻqV?ԌO;^z%v첷|sz7~Rox']*v^?w/^=n8-9K/"xwr- (6y>/ѫR*R.^_zW++CҼMzsB 1ӌֲyK/늷I諾W<1W ܰK^-П|y,@z}+-/zx>ވdVJH7KoѪ-7#YK/'zMz]E/K諾K)gjqpUMFW^^Ez饗^z]~uL0_D7lkA^Ѫ5SVx|V%;鸱{go6>~fk2yŮ w^zRCZ͛H/Իb#AeڻB1̗^z饗^zK/篟ioB o4vpC^z饗^I&{( O,П|yT$?}aŮ g.kr z@ joӍK/x &/̗^_{#63U$+K/›ԛW|o^z饗^_񏎛={bzwzoczדxG _+vW]+=\#So2Q 3_zWll)uD -mK/K/qd饷p{hz镪@G{֝ ~;a{ MK/W 6+kEUz%U`OosL1x}D}}ν-_\ ;j|6;/ޙl|0K/K꼥.г*9;]ӿjᇾŇ6WRd/@>W#6/yxGHU{+7] "[ -ʷ AK/K/K^O#ћ饗^z饗^q&`ثO]@ٿG7 5 '_9~kK޼D=Hi|7R9o3_ 9z饗^z饗^zW( G7^zK/x3;~vp+p O/ Wx{sK?{1 tƐ[o>D@55K/K/Kf饗^a @= wFw$~?a襗^&f!zw/ s{"<{g_ ++^qqd[qzknĶRCi1_zޜޤoo^z@9pָZ^z饗^z,#hٞ*[:g:/&'~9-/{U 94l;7M'} ̡4f$;饗^zϺ3K/}%&wK/'x;̗^z<sz6z6-/ C~/П4 SYWg2zW6RelƲHzҜϺdW|-(5h +z^^qMz𞴤2yy>K/^-s4㺹>oo^z7,ճDxr 6ΗS~-П`3;IKJ ;H W,_Mox>63U]+`FeN/gzW^I/z@α,㺱>/-z饗^z'Yf^qP6_7 /}7T[3~+?pg/74=2wQp7"YMG|wjѪ-ނv`K4;7K/K^S|B ?TS߫ЫJ/;-!<ڮ8)Ji 3;s~*uew^2,:/7nfc}?턆a1_z}&v5yfK/xuK`GWu@O4WvK̲>>K/M:6?7/^xUh-E*G"}GHn=Yx^>_~H 'ݯϺyKgsN[V%#gӼgP#DWs&ތf^@//^x.}-@Q^@zxŋo޿,Z%&8 4<_T/orދ2l«׮udeזm+/ZUkzWe;[գx7/^x땷߻t̶oͻҢoū7/^ xŴ;,)4 ;5xU~ Zko:] vj_ى%U9{{7sM eRlu /^xŋ/^yx .aBۛzxŋO|nOɣ+y99wQޗ'o9}OvX>ʡt{}9Gӓq=/Ě\=֡x^}|+~[xundr3hIPxd5YDkSFj(=׻dݼ śċ/^{ ЇOŎ]{Wdq~~$y%:[{~W> d%`zOzU.we-]wݛZ{Sx;/o/^x];wMݸV1@gɻ*ɝݤX67wm-k7n|Ƌ/^xū7ޥyAf?>/^x;K>~Ev~8~%#\7_G_9>={Zūw+ߔAK" ū7T5mIu?^xŋ{z!Y4?/(s5@s/V%@Od;xŋ/{ Їׯ?~9x9Y_~X~c׃l_x#܎9ڒv/^ej_6 ٰ+mt/^xŋ/^x3@&ٸn/yx❓wޑ9iwzs~~t{}ڞO& |[~ Yxu$QlQ_ͽ1ͽۅYK$U97nX}ŋ/^xŻXo$O \4o;ƫ Ї׏}nS.Xî(rБ?z>-l+s|l?_x- gKFQayZoؑTY"{M#*׍% /^}^3^_ fūO>.[ָnk‹+o/^xz~}WNs{"h#ߖM_:M=0l*vf8#[VN޴a{JOvU57{Wso /^xŋ/^Zx.4@艂q5|[$xu/^x}_Uwuޑ}&)|Nopj*?-gori=^gFow8nڗ;=Y $Y9xnH%[P_xmj5ћF*͏ū|62@Œ]pdR=$o/^/^{ ȟ]J~=WUEo}#_ ۟f?ZVȒ~?oh2zSCYM*P_9xuUP_x_ūv>maœŋ/^Z{?y+;O>Ϯ[]Oo#W;^̛^]ȯܕ Eg/{ӕو$e7Sx*ʒ.5/^xŋw.W/^x9@/xŋd"_ږ5m\Kv=Ͻ֮]/vϼM ٯ:r3DNZ]WYoڗ]LUŋ/^xŋ/^ce.-ŋEf$@^ˇJ;Ʒ \GB3еo1zūwpSbiQx^}*rZ*]P_[e>ū4^xK w%Lsp=}sU0xOyxŋt#ox|UK4JŃ#yY-:Xxndr3hI̩]mQū7$ɒċCx#^(6R˼üԷE}ŋ/^x=ݻ=8O7Ԝ0x/6ŋ^>!_|)x,ovqKS]yſn4@?=S_d%`zOzU.we-]P!NO}ŋBixŋuu yqŋ/^xuYAY _t}Ϗ<]y3ڳ9aU[hnݽJOnEsH֡|e>e#ې "F׈|lo^//^x8ŋ/^zs.{l+}x7"8U{W' U <\s>~>_xR%*/^N[R%IW/^x]7w&o/^x]t>Sv|7x5:xjWGXjVڝBQ_ڱmIu?>^~ŋ0Jݔ īѻ}۞>V-@KKuw#@g*J(-;IG'ޤa7RIN.NO}5ŋ/^x;?s 'Odӽb?;7j7a7/^yU Ї۾+|kJ+A*^{}It/yv̑HZRūw3הtYŋ/^xŋoYo[g,ŋ/^{Ux;;גRh}7\ |EihmtXx7n ][!KMWiv@VS%I/^x7/^x=}r.UVSWg}mxuk>@^Z:qpoĎ]%c֧3oҰ$oؑlY9I{s^lM^{^ /^x</^x|ޥ˞5|{W彼Ǜŋ^xuMDBc{]d)@s~^6nڗ;=Y%Y92l|Sdjco~oݰ[xxx;o/^.-~#^x2ovZoM/ޑ?*|J3sݗxȓ [uŸnɽyC뙚 YkU_泪^gޡ&K((~+x_뭷>7gE}eU{ymO$xŋ/^>6 Ї?d@>'|îg呫ً :9Mz_6]u=ׯ)R_ͽRG-4iV$^xU/^xw? ?0q n < ǵju/^xG/b[j&} {QGݕw<Їm+.wf8+j]WYo՗L]63U5/^x›Ƌ/^x=:\%/--c [~`0ƋWoޓ?y%+o|bMGC\Huk՗gS~e7dj8%!ū*$-IzL}ŋ/^xNmUY+y?Yxŋ^cf߈+?Ԡ]=o5_ݽن YޯQ_J{Cs/^xp^zxū=u=8y+1@fzSxSo}.?moK/t]:W_opHVB) $YS_z宬+i/^xŋ)x*M;N89:<r<o8^ x*uxM[{Z'@W @3|t+}JOnFsH9s܌aū7RF!vEr.ŋ2^ o/^xŋwwb/ژ7do /^xU̻ߕ?xΖ7r-){}W?teqU Yz{ YoR_J{we5Ydŋ/^xmUY ]čf쭺7umV_x{-oz}ԕ>=۸*a$Ӗ%lUڻ[s.4/^xMŋ/^x:6ݑnB7lv k$+GԗGJ{7M ˒uد4:|?//{soM\F^yOq͞'Hz‹VL?trW:Woz&7ޯL۟N>npHV%I&絨߼ux#||Ƌ7ċjĈ7׳kMЎw!^xћ{7My/C+xf~5@We>' [nxwe9LU.we-]՟ކŋ/^x}қ‹/^zzz{79xI$/^'W# @?lGmaߏ,p++l&ru/^ej_3uٰ+kt/^xP_x>^xK}_??qc5Εŋ/^x.^w>jtkޅ~ ㆭ_ӼwMJ|H}*rZ*IzL}ŋ\1xŋ/^ ޜ37ߋu ^x龎CŖQVW>rWYz7 YIH̩Q_J{CcYM$ߢxŋ/^xk7k?~5~|& ^3)K}oz$٪>jDϿ'xґ]{_,RLIާ{{jW¹s9^xŋ/^.xxe["^/^x.w%`diz&?m-ۚ|wCյ|=_E{ƬߜAUғxNū7RF!]/^eYxŋ/^x5*@˄/^xsxF}5e#ېhw7\X.}"ESh?|q?{ڕG~'g(i]KV6kZ}Mnd-UTŋ/^_xsxUқNj/^zv}]qꏯÄ/^xu4n$`W%TUߵBG~锼U%WW4 |#w+?w3Ї5ݯgf|SVD#wP#ɒD< /3ޅy3x-^3^x=yrҍ>^3yxŋ/PXvUv>l:T/^|rqu ?jW~?{~SWbGVvmI:ڟS}?$`We;[|G}ŋWQo/^xŋ+oz6x {+{IU& ^ͽuxoܕL]6M3{b"[=޽t\]ԕ_<~F*=s$%Y=6kn *5%.Kޙ Ĉ7|o J ^xŋ_5x5 Ї7!Mhxm~u ŋ/^j_6MY%R <@~v+]ן~sWtEn,I7ٟs}?+wgPV%IV_3xxz7/^x;GW}wŋ/^s9/{3,%[p<yyぉU5@Վ~E2i~e9hI8[2gnhN޴ϼ.4Vee>S_{xŋ/^&}$@r&xŋ^ gP38Sp0?\M}*֔7}5_ӵW1@ow_} دvKY gdI3jNHwoڗ]LUMWo/^xk̛ċ/^xu gi4nNjO<^_x)*=6d3הX|f$? O򽟉J/ؕloOjn(RĞS͟7tn۵TT^ys&z[//^{xYx~g{[Y7//^[NKvMd孿.?7>6gЗRVg>{] ڵ?ruq' ϪWQ8gj^i5?dI[&xUF88H5›;wo/^/^{]Sv3uUÛċ/^yGw9̑qW+Gstc׻gw~=r0)^k7\Z"!!NoL}ӆgySxŋlo/^x]2Z>k ^Nj9^^l;m%|yg_MU;~|~7\HN6YIպxF}YeîH5뛘W#_ׯ/^/^z3xۻG{jݼF9zԗŋ9نD=Ѫc?>QSh.zpG_m\tlޱrg7)!3ޅ{c3e-UtiN|xŋ/^ysx]Ӌ>psVe/^x]wiKnwc+-~H~˶~ ZG~FW|m~'w#ې%1Wiox,ɒ[/^xŋ/^x5]/o8_?^xŋwqPXvUGZy_JȻ6zfϿ'o\s>Ցy+yΛqԄ7|?PZ$}$\]VŋG$^zxŋ/y{.KG?suŋ/^f] 5rZ~m*?myIw*Ip/w^z:<tF*=z,#ZH?8BĮHѡxŋ/^xŋaޥ?q?qS5ÛNj/^f}5%VK3}y־5OE\u{/t*ɭ%VWio| k*GWoo /^xŋ/^x~ιޥ0}/^_ހxūlfwj"C ~G;uSouWUlzؒh2-w$+]nS៟3^J{xŋ/^s:x]wWOoR_x*v$`WegИ"[Ɇw \G|mJ><ބ*Tr(-;IG=3aߏ F*= U$Q_x/o/^x‹=~ّWڣk ?^^,^mo\oғL]6Ms7ZI7Drn/_~j]mOwϜ}z.g^yoY%Y=kZ}MnH%[P_xU[ċW}ob5ܛ؛WsC} C(txo^zxxUe3ߔL]U.R;T/*?|#!뷭~HWVȒ~/ߏޡ&K(V_xϛëC2~ޥQA: 2lm7ռzxū7׷ޝC U jv@#\:_ۨ/>ߕ\kt Ї2`vڲcI8[2kZ}M:*nI}66ŋ/^Ix}kn~Yr䢿,ŋ/^6-u$`di፞='=:nߜH/{NIr䞷e?-%kصŋ6^3Uٛŋ/^xj]r v|n`9uw5[Nj/^x/)vMx]r$k==+v^tLMn-5m?]7$ɒċmꛝS}#^Il7/^ny[xŋ/^xǻ6ĭf 9w/^x$`W%d7V;e!?6뻞|W2}j|N-p;PJB^k7\Z"|]Vŋ/^zxŋ/^x?$^xu2]wx2ff"G=ŏ>9tmOd/z>'<ܯ܌d#T9^#^H/نl5{G[0xٯ楾x1f^ŋ޹7?鸣cB/^xyH/ld$79~*7i~;=]OZG{A*z6:WwMU>0f6&yJQ_x{7&<޹Nj/^x gygqg}zVEo/^xٟ.%`dԙiCHP/o^I?TGa zƠj#ې%1f1)P#ɒD[/^xŋv^^_{= G?8o/i^x %`WegИ"'~dWCS}#\Oݤ*$+{}M?lH7sN}wo /^xŋ/^Iz<1pѸ'^Oobū7/^%rW3u7%^Q_)reE䫛EOo5 ۏIUғќ2v؟*5e=]lC}ŋ/^xŋW1oPooL3B፼gYrf"^xūwxz.sU'U6?G]ϯd s/l?*j$BX^kwp $ˇ/^xŋw^xŋwgpzɹV}_^0o^^ 쪄J}DJ"h#Q>|#vOA_t-%AK 3^bGVSe /^x;wC}jMUқa>wU0x6Wo1P#|U D]bďuWZkً пr' گBŎڲt$Y?' ~d7IV*VO_oݰūW'u7RY0kZ}ū7/^zjz}_//~_ s-ū/^F*= ;uI7iC_ Jux^hjO~blw=ۑ]Fo~kd'Z$-13^bS"dj6Mū~uB=L|Ƌ/^6hW}&V/^x5e3?8<\QBG~ZJ^\s|_f\2#îC=yAzoڑ|+?w#FO*G?j-][!K^&Kb/^x7Pj/mM}7oX}ŋ/^x G"[޷3ڛƋ/^x;s^<>ǻ[˟(}"p70;oF#o|bUլ{ʇ= O78|#yxm;mY&%-IV  kxxū7Ry>ӗOl:&^xŋW=q3y>a,^_x}-u$`diS߯'}OP~^>g6E+}g:{+}h-ude7#[F9;WMoڗL]6]/^x}TEn8} 0r'[zg3ixju25&|]h>|]WO;z@\ 3wx^ aŋWuoғlC6sMUFog|Sx f==hЕ{/u%RsTr;'ᔤ*Gxn9-YK$]=xŋwޕB_ۻ7{Vŋm{1'S#=Up޸]sxċ-%&Ĉ`[ʿ{Ζ7N>~iKǾ Ώ"_.u2Z?|4 z&7S_x e5YxM}ŋ%v}x=ՓϽYo/^y[x}xy1۸ꏏ_^x IJp77Ry־ ' x|ͼu۹ӪrW~?WW;{JIޗdo4p+k9/^xȻ[y*ٗo[S_:ySxŋKUY>yxxUP_xl9-c̗ç Ї׿\V'j})pU>߽3ڑ6sܕliju\7c#F}6dîHѥxŋs_*xO{}y|Ƌ׏^KSi&d\7Ë/^ F}5e#ېhgD}_8%@^۞#ٻo]\k=m;VQn&%Y>`vٛwiZ$ܛ7G<^㼑OS_xŋ/^Ǎ/;}ç׍zü{xU»snwؓ?~)+o|bm|G[?z>%\~_o}y3٪>ՑvW9vd~kJВXtYM$2x^S_+/^Zxq^,?>^xM;帡TE>VL[O k?V/zٝ[/u/Nu~-Wz r0%.9JOvU5qZ}/^x}GN}ŋ[7/'>h0xŋ p+뙺lަ}*֔r%v;@^?rJpio\ )Zou%RUsTzr3XFR_ ow3הtYŋ/ ,]w o /^j{[xŋwu%@~d~ūwx.Q6^]j_6MY%2rι]˯}r5W>~Q*S筎'wzUl?\G_ʛ>TGܯV%*Wiޡ&K,R_xū׋|ƫ|ŋRe/Ӝ ^֘-mŋ/^x *u{'w|m.!<#ox|U[G9+ɞs ƷvWjirВhbln# M/^s𦩯'^t/^x]we@7lŋ|u ;3e eۜ[E>~Wr蛥D焫Ů|پ\:Uؑ][=熚qd+S|G}{o/^ַ/xŋw>k]~^8iK @x[Ֆ~=4@fxky>oݰ/^x.fK䍍x7N}mU{Ulz.rP>z-%{gͧb׎䋻=y5oE}+?Jj-]AKŦ״DopHV%I^xߛw;Kݞr^3^W6ū71]W%7/^x7YI;ڮ,g<^DwW_h,$:@Վ|Ů.|JmY&%)~ˏ7YJ8Wէxxxkw}+o:_v>:H85j?TK(WRK}ŋgޅ_eD}ŋ/^x6=gk;rŋ/^{uiy}>783gґoϑj_nd5TȈ7l2ɻd-Utŋ/^_d_7fX}ŋ/^x/@?}rV|n {xŋwl\tt>=83㿬|73u$`«7Tԓ3}T/^xŋWmoD޻h :n ^xMkGNtt]sƍmOwdw(ˡ}IWx]YKW$S_ͽIcx=/@|'h%ݠx;7/^{.Kσ'j@x:xf{E|rG/Е7_׹>|r3xVRH/نl5/^x=+@j/"ŋ/^xmx.]vvEXm~ݾ_+^xK8OX#@_`v]ώg bW;VQn&%Y>p]~ _zy *IrD}ŋs\/ޙqx.̛Nj/ޑXr2nVݝqŋ/^y1Nik'}L:7sMYIHPsu^T_^bGVSe5/މA}+3qƵcOnnz3.{^yxüxŋ¼KnB L@'9/^>6kwG[sЕƝ%@spHCiI$U~SW{#lgko/^xM0@߭P_xŋ5o/^=K NZ/^ kw}x}ϓOܵƝ6@ՎH'7y DmIU;955%.KE׷}uZ71֛/^xMūwi^YO3x7/^xg Ї}j]* ;{sgi?_y>W%$Yj~R5`;dIQߏr}5r.ƋW/oR_xŋ/^_T;}xMqj‹/^x5M)Gv/v]wtd|rZcI4_1g6 ۯ|Mɻ[s.4V_x/^x}ڀ{q>/^xMZx.vw _r>:_Ww+'9T-V^RQ7m~;o]oڗ]LU͞^^x{ċ/^Wm}ٸ;?o/^/^3^>}2@~A wmOoIܕHu9R˭#kǬ_]m3[NKR%k꫹7s-4sgxū|Ƌ$4?Qwk7/^e^xg>~G7=@3yݹ;iݿ|>ק*7$Mu XM${~xd5YxmV}[/^zxMxxffū7/W}v!gUnk7/^x]X~_qsfw>od9pm_=sp+k銄8>ԛƫ7/^x/^x8@^gw'T0xūƫGXԺ zW~Ů@;O~i[^Qg_߿c}Wޡ,RegjM Ui/^x7/^xŋ^ {V|& ^xŋwg'/?tg:ϧo~+o閻F<+ZW oէxŋ/^xŋ/^2g7?T|~KO>@K]y?NoykJВX+יuR^ǰ:_x&/^3^xٯUλ}x#ҍɟG|xxGNGc ܵO>_GJK0UTom̶~xŋ/^czSzy/^x‹z&odxq/^{;Yzc#8^mZܦ/>=x|;}f4/뱌ws_xŋ/^O5xm֛obū1^u3^ueaqﺷ/^]W}xo"s2S[&Kr+hUlg>YZ{Gcxŋ4\yxzMo۰_~bsЧmg4nNjw,o/ٽix*Ҷ|㫞x+of O7Xsci%\yM_x]6ŋwJo/^x^Ixŋ2oc1޹bӎ;k|xŋo ޑ7>&?~vA_]^:'BŎ,ڲm%U~ŋ@ŋ/^x^x*r'f鏏/^x❱e^ßސMW\ȇ^r/CЕ|;j_nY%U=Vr>+~[xŋ/^xxWeo/^x]=Oԧǻ: Z?>^xŋ{W_ؒy20SS]y䪻Av*7XŦsӰWKobŋwq>f <‹/^xŋw6+z-o1;8&?7/^xuūSwuݑK;V'酮ًLG6Jp %a/^xŋ/^?xxŋ/^z.N/qŋ_kW}x}ϓW_+@3p/:@o:DNRR9aaլ^^*y3xŋWKW{C}ūw~g}܇ŋWo /^ͽG?!?37]ȇ_ʃً 奮zG}ؗ;ᔤ*GFߜAU/^x;7/^%yx ŋWeoq [/ޅ{uЇ׏q[kwԕ\.^d~5՗hMd=S%17c~/^xŋ/^sA^x}g?9_ 4՛»PDKB>ŋ/Sn>}w ʇo{>^DkNOve9/z7/^x]7/^xŋ/މt&f{]7/^/BF\7%R5&y3xj?TB3u |#NWBl%rJaŋ/^xŋ/^xfj;i>YCo:^x;GoY^ /^xktR&g~2/>^WO>9ޖ7y㗲kZ};?3K#@aG+]Yd%@4#jǘ~ ۯ2*9B޺aN}ŋ/^xm=˛k ~,ewP֯7p{?:6@^v +c3y&o/^ [t|+}YKf2o>eVŋWQo/^x^im=˻tY{ .ޔa^U;nkIk)\$[Ş/^xyw+}y' 㙎|i',Xɕ͛uxmF}/ޅy-x5&^x}_r v|n`vqk)$@^lZn׍:xYx/liyc_$P8][=3^xŋ/^xMŋBC}Uڻ&dW佺t-G%ג5h> [xb@~o"s FW^V̑HZRc3^xŋ/^xŋޘmx5z~q?,70stO͸x3@IgS=iX}jM͜xr* [.bWK͠%k|Ƌ/^xŋ/^xxFqxuugܥy~?4Nkr { G%%Rx{7T> @!@L'΁,Ζ.=\/^xŋ/^~ŋ/^xaIؚd\7x 8G 8ux*t䷮&u>Ց?ӓ;NGVYٌ$]?//^xū6[/^xI>|~xz(@赤I{Qߘa/^yN羶K>F[پN˝pJR#3ޅxsx/ڜ<^xŋ8^->}zٸ÷̬_^x 3l~ Йxx]*g7 |W&ޗL]V$gx;˹x*^۰-e>S_K};GW{WY?yr"& ^}mӗ7iX}MSN'镜5:kVO6CY$dKg>ŋ/^xŋ/^xzMOMŋW' N?yr!xzsJN}5ǻ;cՕNWV"9وe%]1#9W[o/^xÕū76ŋW=o /^x0@?wxmƋwQ|sU Щ/ṔFyxuc!w֓Iv()^xŋ/^xme>Uy>gK>F?ȷ^{s]7 VwIO9@?4xUқi>}7mrВX|ƫƋ/^xzMŋ/^x6`^wŋWio`+lZ^تv_7r']loF}[gxŋ/^xċW}o ^g/{V|]& ^v^xkx͎x_LȻ&Eg:POVyYڒuV_S^3^x❻סxjua^s/^x 3MK:LӼYz#UoF/d c#޸{«|U_R2@JG@On$*r3hUlQ_xU[Ë<^_=M3^x#(^詓s폯P&@]y1y 7v,/^xx/^zySxZxŋz|d`yx]-t[{M|ƋWIs,u5)qK/tcDzk˶tG}5:_xŋWWio/^ggx<@^;G76帱O|S_x콖h[Ow3;]Yd5tUNj/^x*ŋ/^xū=3?;|+4wVo*/^xEUϯ}vsOuOU&7$/^xŋ/^xŋ/^xU Ч_?ɸnNj׏pO}u‹%N's'<]y.y(7I gJ/^{Sԗ.ċ/^xUܛċ/^ gyB`q華7/^x}]1Iב,fd3tK}ŋ/^xŋ/^xƫw.g~/^5u{UWr}S+& lGەpNcYI:Wn#&[NKo֠Fqx6/]wq#(^mxŋwo /^xO@_y8xθȨ/^xM7D]c=s_k_ھ0@ՎFOn$Jr+d7RF!نD*=Ṕx56ۆy~{}xŋ/^s:xx.Ek?'}-/7px{‹/^OwWt+[˟?/@?r%;Ĝ*W#Ӗ]RGM=q/^xkU/^xϕ;wt=o7/^xUZxwwkRN}E:;O/W^r(-;}oZkWsnW6ŋ^ԗŋWIo/^Z{zU}ZexGyx*u25k"ޓt<3[,{Zvs8oѕl]bKb>3^m}9-Wŋ/^xzu2l.R>D7/^xw@䯢=ygwu[ ʲ$Yj5j_6sM5$ /^xŋ/ٮ^xŋsI{^_ ċ/^x})ۗܽ08%@7[NKnXɕYy *v؟ț«7/^xŋ/^xN]:/4xqp"n ^x6ȻU˿_/8W=@O:\Yٲe%]3uo88|gШ|7WiXסx_Uƛ/^J{製c@NW5ÛË޼aD]䉭<|3QxbnR}#վ܊d5t7\z.Ī}+{cxŋ8^xū7/^%y=u[@Ӽxk&U MoJВ~7Rf)뙺D*=gxŋ/^xŋ/^x5.]"H1|xŋw>k}S wp 7I gKFxMsMϦ_)x5̛ċ/^Ixŋ%7 xŋk,vzRGVYٌ$]~5 U._ŋ/^xŋ/^xkw-ݸӜ/^xz%@|G}Tr;/wvS15F*9繦D}+xuqx❓7/^xŋ/^{܆h7[;x~5g-ٟnd$^~5s.wٟ4e|ƋwFC}jMŋWG/^Wxx]dӎ;,`qŋw+:n$dKg>k}yӋo/^xk o /^x^ŋק޹^? "[ŋ+760u3@_ה.we%@4#Zgޖawԑ]mm״ŋ/^cxkx\7&FQ/^3s/^a„'9[pڿko /^=yF׷=dIn,~7RF!نD}g7/^xj͙mP_a9 /^$@^gYlKoix=bG}5eyǒS5n>[{:FxM^xmŋ+o/^x xmū}^4[ŋ/ }4W#}T<e'U眳~ IJp /zmxMŋ/^x6ŋ:kiZYaŋ/^{/jwMW;=.]H}ǜϑJOnFg>\)7\Jɖb‹/^x;Wŋ/^x߽)?=n9u|xŋkܕBɻ*͠%Ry7f)نD*=WWobū7ho olNjz^xŋޅtḩO5:.ƛċ/^RWs wiɍK"^S% 97i>~ŋ/^xŋ"o/^x.ԛR}x d./^]ŋ׵t = W#qg^x7RľMIrH}5F*G}/^xŋ/^x5Ë/^mx='ZƝW|xkv-w#ې%BjTs.wYxŋ/^xkסxқWgqu?>^J$dKѧxwO9?f[7/^xZ~6ŋ/^xU;}W'x[> _^x7\J8'뱬k꫉wsׯw~ޚa޼O^_uZ_xŋ eG:<}P{q?ޖyŋםdIn*P_MJO6 9]/޹y{x~xٟٯ[Nj/^w.mgyax/^.zx6f);Ĝ*Ȼsn7/^z-xmŋ&^xMw8nޟxUxu:wa^kㆊr#TAcsN} w9/^xŋ/^xM-ޛwJ+z6{yiZ ūxf4/k{sΩl;m/^xŋ/^xŋwa^zxMK3'O%%ݾwcċ/^{#ިA ޱ]KfВdE}596$R~ŋ/^xŋPo؈7/^-K?S3_ ^xŋ/^5[NKnXɕoRcӒ]Riŋ/^xŋwҿR_/^x{yOI_o ^z/^MbGnlٲ؟f.95xŋ/^xj-ŋ/^>.~EtNzo ^zcxSoڗ[ӒS_MrWNMޜaŋWo/^9x5ŋ/^xgx^]:'Gj̷_{[̪ k|̛WqoԩJkiT}%ߔӐ\ /^yoƐ$o/^W/^s,x5tkA f7to/^x}ֻ]8[$%꫑wi=/^x둷/^jx3xŋW3kOk^xŋSn#lswͪoSwh״}C/^xכk7WOoR_xŋ?@϶OA ċ/^ϥ4k@}#վNݔ*gMrW3u7%Z훹~ŋ/^x;M}UyfŋW=8Ӑ~*@x*mŋWEF!AK:e3הL]"V^/^xUЛƋ/^xzmŋW7e82 n ^:zxŻopHnS%S_Mۅ U ;_S^xŋ%WrZo/^xuw@ޥZ\G|o-S5[Ë޶;޼ϽrWV9 D3b:{ݞ~ U.5'i~/^:x~nIjj~Żo/^y܀̣?7px8|θxNu2g>IV()9WF*99&yWo/^xŋWEo/IM/^-sbū7W{7qSSƋ/^{x7Ǝ%1J}5F}9Sr}/^3yx{7^ /^x]čvӛqz-|7T<l's9ywO9?6nūסxP_yxUŋ/Kqcŋ/^x3nғѼElI_r9Uǻ[HɶfNj/^xŋ/^xm~mIO:nƅŋ/^TYn-IZWoғl9_xŋ/^xŋ/^x{>&?nNj/^x7ewiɍK"2Ȼ$`$\5M[x.ʛ‹؟ċ/^x‹/^k.w'u fŋP#˻Ln&Xxŋ/^xŋ/^x]^=Z?yz6^xě{鸑j_n NKzL}59rZF^xŋ/^#qxŋWIo/^q8kV}~_^xkkoե s7 Tz{d`7L}*juދŋ/^x؏<x;WWv@n$e7SyR[vMvK/^xSԗŋSo/^xŻ8o/^x z`B4u}:xūn7Sr8+.k׎e#StȨlB}zxcxmuڃū71֛/^zkxWz+X!6^xŻXoڗ`z_)IWc@}宬g㴌_e‹/^xxexŋ/^ Ї_ /^x.ڻmȍwOSoڗ\9_iݽMxQ_xk6ݣԗŋ/^ O(?:LxMZx7$7) $S_Mۅ U ;Ʈ4^Mxŋܽ 6^x7/^aܩsF=_ <2.we%@4#vC}5wŋoDoKo۰ 9/^x;G/iz9XxwU0x/:xūN$BIIP9D}s&ū76ūw,o/^y#(^mx[Ë/^{{mӍ{uOx5Wf)7v,9U꫉7Rf)뙺D*=/^x7/^xŋw>:^xO8Σ?>^x5k F(-IGsΩs97xg:_^+eX}-ee2Л/^x q?>^xrxjTzr3-Zj ;]80j>~ŋ/^zxŋwË/Eyx Ч}jmqꏏw+/^x}]Me%hIԢx#S_xŋ/^xŋ/^xx gSϸ˸˸%l =<\5M[xU˸ގ>S_xkqqqwd ^x7TnF؍Ļwxr1/^xŋ/^xŋ/^x=sV&^x7R˭XAӒQ_Ml;m/^x]ŋ/gŋ/^xŋR+6ogGyO?1N|]xeֺ&+AK&YoғlC6 TW>Zxŋ^ū7Fߏ/^x?/;ve/n ^Nj/^yy rc')"Ȼ$`+MV_zx/^x=ߛ‹/^^x]:H-uu}nYOϛū76=P_-,ω]mV1# U j‹9:^x.[ËWo/ަa91֛kw>_^UGxÛ>vb_n$U9kJ}l9-WmY/^xŻhxoΰo˰߽ux8N,n ^:{x/m ލlCw,թ.F_6 5$R~ Zx5YxŋM:xxŋ k5ۻtc`UxċW/]=HJ=I7WӖ`&F/^{mxUԛƋ/^x❷/^ݽK\ii9v6^xU]Y$~C}5wū7/W9c//^xme>S_xUgKO>~@xŋw;VIn,&p+뙺l6 /^x#(^mxfj덍xxU‹/^x'@O}N5Ά ċWGo/^Jy7McI4_ԛ2z#վl暲KkZ}ŋ/^xŋ/hc4ŋWuo EY/ ċ|ƫ77T<l'I{{Mv@vUBŎQyGxYx~y x3xŋ/^^^yz5M !_3^x]7RJ4/k[Zj ;]8`ŋWIo/)xśNj/^xN|лtSA8[xu/^yY Z,د4F*G} ^xŋ/^xz  R_xYx~.q?[7ps ŋ/^N[XΖ&H/9r7/^xŋF^xŋ/^KnC? < ŀ&^xUF NP#7BlYy=꫉wgci/^xŋ/^x❷7W[olNjWo,< nǟ=˫=x;͸qV wiIW&9xŋ/^xŋ/^jycx6û4/)iO3n]k=x oNB1YF*=6d39&Wxŋ/^xŋ/^ia^yx Ї4'wB3x{wp w)5j_sMϦ__{xJŻE}/^ xŋw>/V{ڛ«G>|~FkmZ{ Yx:F<'vK}5swZ3o/ "i(zѸHax[ y[yXxvb_n$]9xwK 5vڬ_zk:xNeūW[Nj/^xūc>.kc|޸ӴG_qx6\ن,X߫S_Ms7 T_zzxǛċ/^C^{xŋW+oJ^>.,@솜7qx9߯/^{-Jzm?{n&-%&/^xŋ/^xkC}*MC^.4y[x7d%@4#vC}5$JpȨlŋ/^xŋ/^xuūk?ɜo_'.L:/^>r##zYӾ{ioưGxŋ/^xk6[/^x*~9g 7oxTzr3j֡xCc U;d5 )xu{o/^Myxŋ8^x ЇoY}R n^jM]KWd%hIԢx宬g겙oJ7j>~GQ_x ڟ x3x5P_x*ju/^z:}Zd$^xzvr}ǒpd\}Sz眯gV_xŋ/^x7M}/^xMUy?y2&{+;xŋWoؑ݌l&rb7zWv@vUBŎ뷉^(Λ09lo/^Mo/^-xŋ^!:~\YǝG|x5;o^Lsp3>kiPެ^WǍxH/s+{Mn#&N{懩\x;/^xzx /^.ī5ěFj~ ^3x] S.vNۖ /^xMy܍lCnX/Ԩ&H'sYxu/^FzxzMŋ^ /^x.]Mkzmz x~z0%؍>YӒ]p~W+/^xP_yxj?S_xe^73'eXms)ï/^xTzI ޡxG?bŋOqxË/^xŋ/^z.{t\ho8oŋ/^5ɒ-IW-ŋ/^xŋ/^xŋ/^ sYG?p~~xŋW]f)7v,+7y}#mHkZ}ŋׯ^zxŋ$xūw鬟폟oE@SxUkWz0-IGFjMڲIaŋ/^xŋ/^xŋ/޹z9}0<ï|8x;7/^ygŽJOlҒuϚxCc U _ū7feX}[/^mx [qWkC}/x*]B[dqŋk銬-Mckk sjue‹/^x{/^xU{_> ݸe{/o ^x*mvr}ǒpD}5F}5sn|6mŋ/^{Sxŋ/^xŋWWس^_?  WP#7v3ȉQߺ. ڵ;Fxo޸^y[x6ū71֛׃#/ŋ oޥ&YC9Ɵ ū|VV|OӒQ_Msw~/^>^mS_>&Yxŋ/9zgx_K~Īϯ? o{}xŋ/^x]:xx. q?70,D}uūxľMIrhׄ U.7ū76ī7J}& ^xsx7wi^YO;njnqūwndrcǒxF}5F*99_x{7/^c^ixŋ/^}xM_'ŋWpHSLS_ 眇]cׯw.ނO{xݯL;^yo/^xū~>|~ޏfZ7W;oғHN֢c׼;{'5xŋ/^x7/^O9x*ūד}wڧ)U:xfĻ,JВTM}5:kYxŋ/^xŋ/^x;ƫ}xqg/^xϾR3x7McI4_1kB}#ldw97i>~ŋ)EIxŋ/^&ŋq 8k\Yŋ/^yCce;Q_M[NKvsΩ/^xŋ/^xŋ/^xwzv~q9ҏ/^xuF*=sd5b]P_MXx7/^x7/^xŋFޅ觟.;<ܪ{ux-ŋWYZ"AKb7Yz&V~‹W3C}ŋW)Wio/^~?|ƋI>7v|x{7Unڮ%ljTkJiHcjMŋ/^xŋ/3)xŋ/^O7RGnfd3bG}5n;m 5-uXxŋ"uFykxŋ/^x=JK~d[%zx^Yu0x[f wiIW^X߶>PXvUvU߼Bo{&_xYxwxQ޶^:^x Ї'g{?4^*y{vMw,I5Pp+뙺lϦ_x˼ ۠xŋk&qxŋן^xmx_5YQQi{7x;;I E꫉wxz.J/^xmxŋw4^xŋ] x]H>N=JI /^s9rWwIޝۢ~n$`W%T5]o /^xŋ/^x*mxx=Ogw^ŋDm(BIIW&Pss֯ &^{#(^mx❓7W[olNjz^xŋW=uҞ;pv|xMλm ySwHw9{M/^xŋ/^xŋWo/^4x*@IIu?>^zW3ob\$kB}#վl眇]"1ŋyތϼ6^mx_Q_xukŋW^1 F|xҸJOV"9Yf$SP_M;{'~<^O9xUNj/^x;ݍfyaxiOj$+AKR6;<|p~W;o/^xŋ/^ymx_e>j nޟ?>^x7%|jTzmfnp9$^xŋ/^x❫7/^xו=~KvWqy"/޻&^j{CŎٲtnobQ j_眫^_xŋ/^xŋYoR_y[xN]_Eƙ^lïux2hH'7cFlWopsΩ/^xŋ/^xŋ/^x卩m;tY>0}rΪUw:8^}^pܵtE$M&RGvMDksnJ}ŋ/^xŋ/^xM{P_y{p?_ 4՛Ƌm-׷- gKշ7smHkZ}ŋWo^ /^xu6;oR_x_Q?/~ {W}xwKDN2.sN}rZuj[5M[Fxkxu4j=_++x[Nj/^yOdǫFE-mJ7hok2Yxu6ޓۻ)IW&DUIVXxvx5:x۟ŋ/^zsx#ޖa1t4E*Gּ /^{xvMVv)5oV] صxM^xŋ/^xī7G}o/^Fx '$-ū:5fFx?F}5Wz{/\xŋ/^xŋ/^m>|Vܛ>U^xws~ŋwp+ᜬDzito܀n;m #~ŋ/^xŋ/^xMŋl޻z)өiWNmū71U{*PRҕñ꫚7T<]C#/^xŋ/^xŋެA؈7ޖ;ޥdcz1h7/^%نر$^Q_MrW3u7%R~/^xŋCo /^xŋ\eO\W|oONP_x=F0-;؍>`sKcr ^xŋ/^xŋw^ o/^x]r2nIV?1ͽJOV"9Yf$SP_Mۅ U ;Fg]9x*ŋ/^Sxsxjŋ8^x. q?7pUϸxw5Y%rj ;]80z&P_x]} ނϼA ^/^_xeסxK< nǟ?J7To/^3y7McI4_x#sSo /^xŋ/^xŋwqxŋy.2Kiƍ<^x{P#׃iN:b7zWoڗpū/^&FQxzxcxmŋ/^xuMqʷ;/^xTzr3ȝpZ1Ļwxrk4~Rme>S_/^xso/Yfŋ/^sЇפw.uŋ7-j5a5ӻ[ȍ݌l$rEyw\Ӓ]sn״?ͪv_bWSL|6ӴË/^x^2^GUo//^gxy5n9y&xg:x7k|Tr+'wS~c#k IJpum]˿[Kŋ/^5x{7Wobڭa}34o/^S-' ^x&/ر$0kB}wK:5rZgxuVDFOҹ'ŋ/^xŋ^xūK>| ax;u*PRR꫉7T<{9ނ!b_í˃s]t/^xŋ/ċ/^x?:Lx6Ǝ%Bj _;7Wko*kM3ŋ/^xŋ/^x0@ow0x7RJ$'kьصD޼aUɻ]8]Pc״5ow U ЙxcxŋWio/^xŋw o/^My |>ݸ= /^5ɒ-IwBSC#o/^zg ?@nb}Wyx‹ׇ3^3^xٯzŋCxd&ūwiK .we=S|S"վy뷁Y/{/^sN5n/^xŋ+o/^x]@ 8N$-o/^P#7Bl%4zWoڗ|S3uTz}?us,^_{g}}PH}‹ܽMxMxxfū7/,Iu?>ɽygŽJOnFN];xOs~^7@xzhw^,^/^xŻh/}wx5˻%bӸZzCŎlV_}u#@WkZ}*mŋ^xƋ/ $x&@wY[LUk;N[$%-Q_MJopynp9/^n;Wo/^xu u3‹/^xN>n^tZ"u-&m|{[ȍ݌l$rsN}=<\~5;᝴6^6^Wo/^x|̻s3dIgGy).^/^]uwj_nnJ^S껳wxrQٴ<w>ӑk>7W[ol/oTޖ:ިބ ^xŋW}o /^*{.{L ۆ7N^/^>6ر$0kB}wK 5vgx?|#ޓ{7/^xŋ/^x[NjWORvq>pDO7pxŋ ޡ\$hS_ JO6眳~L [I߿ѕN_i -hMū5^ #Ǡfŋ/^J{F_M?_Xm~y@xM ]Y$ˊ]P_MV%&rרl5՛;N}-/^xŋTo/^ż9zԗkw鲷xS*@{z3yo[EJJ|@}5Z# UIWxu'@\'*ŋ/^xŋ/^xk[;w0pyxo ^xZM -j{Swx^˨2l;+/=Y/R_xŋ/^xŋ;$^x.]E'uū7$׃)IIoT}˚7Rf)نD*=՗WG_>e|Ƌ/^x˟2ŋw"}:gx/^5JO#9YfĮu&m=xaŋw% /^xŋ\1xŋ/^x=OgCNWxUһ,rВT}סxCc U;d5kuk=vOBy0ŋ/^xǛ^ū7ݼ$@ A7/^jz\߱$P_MrW3urZY{zxNҙJ};:^x7B/^xԻod/xU"H9䍍xx} ;r=d˦LG}59_%RI6zԫ/^yxYx[Njw o /^=yx_e>mū71֛qѻt_/̫?7pŋ?H'7 ŮWߚ ޶ۅ U ;FxZ^?z.,^}?|Ƌ^3^sLٯrx~[/ދKnA ދix=+cIؤxC=OڞLŋro/^Zxŋo?Mx5]6^WKnC? N4V[뾷kӖ;I͔&9=S_xxŋ/^{xŋ/^6~ܥy@? +^xo渻F"'F{o=nڗpk|^x}[xŋ/^xŋ/^.yxUٛŋ?O3n9/1xV|OnĮQ_M;{'~mū7j7o/^x5ț5‹/^x5@^ǟd\迏_I뙺ر$]}S-^/^xŋ/^xŋԜiꛦxI3,O8סxwgPSn}MViJO6 9kZ}U~D}~w ^W6^mxexukŋW}8/w3b5w>ycx,se%SP_ j_sYxŋ9zysxUқNj/^x+o9Oϟ7$oa¸MūUFѸ{9geOo/^xŋwB&^x?S_xU ЇyO;9~0zSx*5 5꫉7SHɶfUΛċ/^xŋ/^xNun-^x;Vܻ' Ou}xm `ZS4Wo:8<אHo|}4o/^xŋ/^x57ËW7o /^y}o䰷0Tͷ/>;\ogy9+/^zgSd=ٲŮuRm%&vc|6mŋ/^xŋ/^xŋB1Zg?{}>-V9@끷WWdIn-WW/^Jzxŋ/^xŋ/^xzm:9X>{^|]& ^xmI4_xl9-o/^uxŻHo^ /^xmŋWo/^xj;5YU0x ;r=d˦LgN}7Rf)نD*ެI/^xߛe~½ux͙mQ_gXmY8zܳBI&޶yެޖH/7 ŮS_MN[vMvKi/^ӽūt.U/^ӼuW[o/^x77=uIm#҄BoCoʍKŦhΪ{Cc U;4kZ}ŋ7W-o/^x56‹/^ZySަaū=qD9۸?cWKv@$e7SkV_z宬g겙oJڿ(R_ŋ/^xŋ/^xu/^ͼ Ѣd-w{[ȍ݌sit9뙺D*=/^qxŋ/^xŋ/ 1xŋ=3f|xMxF}ߓ[)G{ofA쪄#/^xŋ/^xŋ/^f *k>Kiu?>^])뙺\NHbA}5 U.7ŋ/^yxŋA^xŋ/^.xzD,?>^5swgPSn7Rs9 ^xŋ/^xŋ/^cxmx3Rh0M|xM]Y$Jޡx#վl眇]o B9x*ŋ/^yJR!ܠzsxUқNjo/^xu-@5jgu}Z /^Ӽ.P_M;{'7ū7/^xgx,3_ޑ#q=lf]~go/^Jz-{/^tY>0yݾkzx5%Bj-u$`di~jMŋ/^x; x5_vG}ū7/^xƻtٓ}`6}Y^Ĺ:^zxGr=TA2>d#ېs/^xċW}obvFooo\Qh>~3@I"Uwbo /^xŋ/^5wK0~?pE۳Pk7RJ4/kьصĻ$`sūwx.%^xxxŋ}x=W!oQ_mxY*7/^xj ŏ~5xY] ċ4dIw,IZWopH"X&xxŋ/^ښzx}(@;Go\sL}ŋ xūw鬟oM~*7a޺y^ؒW-WUHPPu7mX}Ukŋ/^5 x6^y2 ռ}/^{x*}Cc5@^@xu:%K٣xodXxŋ9xY>˛3y/^xŋ/z 3ux:{#վ܌:r;vה%`dԑhM$k5;uū/^xW>"|M}M6/ŋ׿)o9Wzj(6:^@xuر$YlMV_]9kZ}kWo/^x*@;VMI9כ2x5ŋ/{-x'g'!^$? mwp $e7S2;oA] 5rZFxM/^sgK}5e>+D@| <8̮w 2hL@zB\Z$N8BH ġ**ͮԊZ "qABA_Ȏc'I%ɮ7IM$^{_f>gl?{{>Wի̞ >{g6ިsCvzGw#g_}s7<=yz~W^zջnޡ~OUrW^zիWm >.V z/W=B;"OVubj+իW^zիw{wr@>[۞^zիW^z7Dn|?NvޓתPr=秮WzzwߧW^zݍ=O=r_x;$ի}zիWޝ ;ߏx?BBA=u{OP=MwzOv:~zrc:yW^zիW^YրG{:իW^zիw{aCvζW.~Fӗg/V|wwc{lެتN_{QիW^{V^z?¹=s|s7W^zKz?Vȃ~?֭^޾^;^~ՋH{7߿|{2z/իW|~eg#{~{/޽h|իW=zAhիW~}}{Xz;ҽ6W^|GWޫNmVOϯ뽮W^z͵EˆVzտ]xիWa콦W^z]'u]_wp{73=ns+{qzsիwzЫW޼{c@O+꟞w+ޑ=Gszޝ]׫w{7=?uohݙ z{OvÏWz}~իw;o~{KC~od֛7o@~֫WャY^_6F^{^Ыwz_ԫW^ջW}གྷ7w7Wիwz׫Wݗ^^L_o{-zwﳯWXZ=uMW^ۻWz_ԫW!^~#o^z?7իW^߻zo|| aӻ73:{իW^{7z?;"oދzիW=WoGRu׷n^zZ^~:~xW^zSEջ޳wӫwzw>a?ػazիW^{UތwW^;ONubmzw<_zիW^z꽏a=_zիW=꽦WCշwxz{^z%׫W^zwܞ{/{^zի7ށ.;nߏ? ozO͛Փ'.^uy^;zիWޡիwx{Я z}m&˞;O]~:vz^Q~zիW^zջ8{zիWA];/57^90dϼnubmzwիW^zyzիW^zUzw@?wO\ܺ׽۟X7L{ӯx37=_zիW^z݅a=_zիW^z逾;U?uw՛go>tϹW^{N^zջo螯Q=W^zիwo }Ά?kzwOVubmz׽W^|իW=Կ~eWޡ=W|;a'^`9?umt /t ;_Q\V=UټL t /g6oVOnܺk7 >N:~9u<ӯݬ6t` `u݉(Vky ~riwsOF%|n!lLWg `gZј1.Ɲ &Uo޺0CC˫)EQ;ΤN3G{_L+ Fy]}iywg3B8c\h6h򉘖{2t#_>ڛp6{Fѕ)-N:~K:={ΗB8<F7P9vJ0}GVza9}޾}%5tֈ~kiog~ a<Ƹh4f@CW硣+5t!}R{W÷=n8v=R !ywdysi0w _~yt0:!}0 `@Y\<|q `@n@1NEQt:Ёn;c\h6< cBK)͵Y`@<+[QLq=Ё{ʕ>t=t=Ё©_OVnX:3CaBc#t`yx:AЛR<7 n}(kGW1fN3YE=tгN]Y|:C1ƅF1`@ő/?iǀBY`@տN8 @vt  B/rx8Ps&Ё܆ZgqpY9djMEQw9!Knw(z՚v`@>!e9ngstq8qgtNq(zәt`@,p Ƹl690C~RJs9:dhstRJ`QAtNF1<}pCٔ\a̙l6B8<t:9:! ayL 9:jMEQv 萝=1)txY9:8:˲!;0CvbSEQ;Τ:dNEQoZ ߇-,{9F1c\p9!K9:dm)1ƅfyy@0Rs9![{ΗB8<I),R:<}xB1.4tq8ٔ\a̙l6 萭N3YE=8<}@B/rx8Ps&`@܆Zݞ-r=`@,Z(nwyv'ZitC˜{@yx= 萝TQN3<:v1t|0Rk۳9 dhsRJ{:Z @qlr̷9O)ͅƜ `@';{ΗB8<:sR:<:qC1.4q8Ytl60N3YE=8<:9C/rx8Ps&܆ZgqpY9 djMEQv0n;QEjM;N~oʲo۳9 8y3 d'8UEL:NvB8c\h6`@'7Yc)9th4fb 9 d)t(zJ dyјqH!RJs)˜3 dl1.,pytt&sMxYFc&Ps&܆Zݞu9`@'[Vk(z۝p91N9N~ow0p^,..r>0L:ى1N0n;QEjM;N~?,{Cq8w9=o{;Τ0gg)1ƅfyy{!s`@Vј1.,pyгR:XE=ty{!ƌ08lJi.0L i6s`@Vә,crXeYwjp^k۳eYλV5]EN8zb VkY%z0IENDB`vagrant-1.4.3/website/docs/source/images/vagrant_header_background.png000066400000000000000000003173511226132634600262160ustar00rootroot00000000000000PNG  IHDRdelIDATx]K[AgElJD9E!7roz^F+qYn^NJ<;3{MǟZ*WbPA{rWWWyvYjϿX9r|oiϵ { 6BkIc_6|>i^J|>8s|m>b뻻ߥ}E3XvC˅+]kҥ}p/Aw *CnoaS_Z+hFzY;CZ2M2@&i@&tM6mȌ=3󝥓T _,~dҁƨز֧^]@ԟ5^6[I  #RLu,>2#&jM )@&tR}:}}-kE(dvzH9'g~ cgxDS72@Ư;L#J@z\ż'6EHF0fx A4f"WP*tzdm I2v6/E2NrLT@ Kqr dMSC犧"r2!!=ٔ)kH)#C캢MHK_y^ f8KdƬ~@bQ(V7m26ȤЦ/cDrS#[=PəLt dԉ&żT7)ubl|weiJ c@&$N9JpLX}E^rS}{&<1` lL(ԗ~LP/:1ԉ!ڄyNLEg; Q_7Aw"{Z2!DMg584͜^.>o3h$ԗq6N@ft:~Ʌ6 CIғא:1ԉAd'G2D}LR rƾY+/cߵ2 e\Y.4n@z'ۖmE0ﳨ?υd5!21C~F"Yk@&)@&2l:!ƾYx/gO螛N-E*pr dMՖoȤQEd=FB6%+ t?"B90I@/{gI Dp&$ 2lpe2ﬡ뗰 `fv/ d<=Q~qTɈ۔JuCY9̵j9 b(f'!x2Ds&#AY238ΑZb>{ Q1ảdsmhry9̻49yLwS6'n911DX&St='F2h7W%EW]E/w#)O2>OfDڶ?"BfOZ'OuyNUF292~ݣVdN<6Khxʜw> ZÇ23d%WXd|@W*}O(|_S 55T /r )&S@Ȳ%] ̉qڄ911' /Tt|.zdk(FwLZ!S@&ivqqf7\dNg?@fx20'~œd A2Jd :&N P ( L_"sq2"jDDחYmLG:2ē1#91hؿS!,~A3_3Dw Ę^(i=}i.>sL"gU@~ά3 2Z)@&.ݟ'[;L_GF̉!JkOI !} 1f2cmE/Lȴd\ž|OTu;9L-KmLRPb~ OaN459 dn+ du8@ȉmy{շʉa!ɗw2e d@frLj4 @&2Tޖ3d°Ϲ dn2Lf@ȉw bOf!/C7Øꑕߘoz2Ȥ 3}f@fm{*>qdfg3_f2 S\ 3 ~@= JZY'W/ƌLo61kz2Ȥj ,@&T5`=d͍^ OeY@&³5dΎ=d'dlҢzM-d!bcFOo@&d2^ȄuNO=m曜sd~Loh@rbacOJ91yѿ ݑ8?z-ȔJSzdRL!# :L 2~s379}ɚ sȤ2 ~@FNLVaaĨ)sb 2DCe/\+զ<2PB@ϬϏ{tB&|Wؒ}O79LȬ^L `{ ~@&''FN91rbrg>@H 0+wz77 s#@& ?kOðLNg2+dd2=<=m;i=#ǽؓJP FnEJF-f?G" /"g1jt{|qRz$j1Q dF$ᡜ@&NDLg2lR'?Pq S|3gg2y1l2TKvt d~hO}(hu1XL)hOv.QϗLYf56f{ҹLPG P3xYd$Kk¯S|p1 5G)bmq2ΉK#vdΉ191gr2Je}amͤލ@̆:Ξ@O űО_$g=b :شr-d ~ عLeLg'sN@F)(e177<^u2=J '`/:&9 `b^Z|ٓ:ؓ9'sC:fhOVW"QJ9_F9_FX@3vL@Nz2|LXOeYn 4ڶ>>g_xu}$' da/Zno'd@F{2=X`1_{wRj|m̔e8ٶܠ긨K.U Ǵ eYn$ 'mq:@_G{| oWGt wO׮ מL{2;wRtFўL 6fژ/@ژicF>1I/ SK18ILa@4=u2qQj@ kq|K'dP LHԞ_]b ۇj6Eu2v. Ĕ=<Jtל1,i"bcFlؘmn d i$Cȏ2~ͥSOLOaOאC"=ȼ+'`g@ˉܩrbX2DtccV7oR3U ~@dk2ۓ2yye_ ۯ䛜}L{~j#'&s91rb2DGe3 d iC@fړdt^?x792 t>'x{j 3'ɽ591rbܑiBj=@Ї2f愛=lz2N(2{>Y:^t' dv: Iy< aO=,eOJG dS"/C$_~* df 4d2NR&g~Ji 3my:t;Ȱ'3=prejꙜ=-)2lHLNEj@@&_ρ I\Wr/ٲ9k2Lټ럟7925dg=Y~ᛜ'@&gX{ 91eQf+9İ"55'`Lgd1ccvʾ@f2kk:̜dkz:KpyuJ2O5dO'[;P{dx6rȉs91,i"6fZ)8S;{߀2ɦ 23t^i^Kəߧ̚27ɁbȡLƞLN̺{ɉᜠrbwƺ1ӧi/&ⅴ#bא:ow BC\ |rN 7Ze{k#L7蜗~91שGAkK :-k?@=91psLDMړA/ eL7g~A9z9oNǀ8-W25gtkru>?d'>%'{pGq{2ȉ T 2i(ܐ8ϩ_=;}\27ܦ:gL=Ym{2n`45{= ~e1CĵkP:2q )@9S~ȩ.k2`3k)@fSNL}J Qӌ2=@!BacV(7qz > y.01'Ěd6*ϩ_?ԟLV48h>޷yb2+',8``i֜1#dbOAac1lH o2q Yw=XSjsj ᘲd -kcS*@rmUʉ=_dBm샤z-8q(L@5|@& FƬ뇜zeM#iUW2 <#2Rn 7ȉl9EN Ԕrb2R/77P:ޘƴ&C@&@@F1쟵'ټ0lLȨש3| ~Q|--d'#'f[f!QӬddB )Cj=`O68B`O912|e~;8s !~NSdSP`Ꙟ=@'!s^ZU37yI6ړedbu9'J91'cN @!4Xؘ!2YIg"Ɋi8N2}?7kkT {N ȸ=s6f | 2uqt}!=dt`.-v_3 B3hkC+N@W}r=dĴϴ2=?>X1 #ȶ^{ 3o>@{@ d{BؓaELT@!Bؘd2jY(&Ӂ!L $@gr*LO: T\d׎BQ@@o  BBsY|^tơB%XoQxG7]e @|WXiQӮ91c6p=@!,L: l̰132c ds@225-~@F/ks{2 'L ~ Sݏ5xB3 CNϾ@rbxGNL j/#_qK[M~@FN@&oz7&Z/@Fsnݚ7Ͻ6Q'Qړ/-{brbȉBDMɉ!' Bˠ+ᣍщ ? -Ti Cxu퀌69_k ʵ d. In͗}Zfuܳ2#'^#jIN ٙ 2=z~rO dO9'JOMɉdB莰1C< d dWnNymr֯}M£LwJe}?@&Jؠɽ`O=dXi}PSj=Yu 6f(6U ddfkVZ@Fi}-~ so 3;IST/9D,d {2w{AxؓaEM{*@ؓؓdB37,!WLU^N @fԜ&9 x_ {|w 2Y[72OP42b¢m3 ?#VZt 91c"@!mtƬ c9@r,c257!k׿=ՠsۗey]yx\.LMiLdu{[dȉ 'jȉAr#|-[SQ{L˨g_sb~^Nש+aqX6i27G @q^hzC@Fȉ!'iBM{A;c8b 8?oG Z RKܱ 51tL\wӍ"_ n0@&`D d*{y Uc;SI@FAv]T 8vѺdVnk @ɽd[j2.{2 ɉA"ӄɼߞdB|ȗi d!j2 d ԰ MT uM#zg8] _2=ݣ Ymd':O`uJMɉ3 3GPwizd7Mu;9@_( Y={w dTo{ԟb.X=d+-j+{2A!lЃF?ۘ8ҲԿə d5 2{9 xd GkluR B-d4'fq'CXiQ>91Ӣ{Cac6f!d$'< 贀M, :?ܠWLu״ ~ݣ;PhW s~NS {-XiQSrb?fڽNw:m&^ ̼2~4SədAgq5' @~v5o1ȨrbXPSrbȉA'CN ީe n9oedv{@F('kjr]!5dsM@FԔ|x {SVh^S5Q{2rbȉAdP^"'o  NL? @^E62lNrəd4t?7.G}>d:='& _&J]\ dԞArbк4!'{2!=ATe !h82/y ]@:TS]L2=PH Sa6SWMɉ˼ dɽ@`OJN VZWSrb'*{ 6f~ac4d)6QSAD5!31?{{lD/0NM$T>r_~ܮi549 52i ާ?=}iMD@; ȜWִ'#'+-j dh C Bؘac`:@FUMN_ d5!M 0u@]Fؓj`EMɉpA BJNocVpf: l̜~srZ6kM/?.27Hrbo3FtnH3`ӬeRBqۯ2Ĕe۟}vA,ȉAԔy(`cD}z2RLQz27|ך{uI}-әhM-@y4p!h |z{SLHNdԣ jd j(rb@DIe5F}2Z(nmzd79K7B3SMoi~0ϗ{ͽ< dgdt|@rb"ӄɼwCIey"_/3D@i ɜkM/Ll~_S?@с\ 6EV{@FɮbOCN VZWSrb'[eL41C?J2j?ִoz- dٯ}-a751Txf&clt<'͉{@|>=Y dz+ȉJ=ȼ/e 3. Fsm?Ȥ4+Ze 59a dqks2 xMKs, 91dKñ'Þ +-j<91eQF/d$lcc6f@Ɵ_M5;169um@A00 jɼ{Lz \ 91 U,T7{gF;@cě\!Yj:.Ax '`rԌTK[g=_*)91 6f[yS6fg^ӯ:C ﯩn^⁌?'2С%L :߽Cs5 ^?ϠєS} H 91 rw/ PPSDN Bsl/Ne_F R 94Sey@oO!2?p] 3d0tpembcSϗ'RLٓ591F ?ڭ[iiBN-^+/ȗA1.: d\'&jM\ ۓIN> sYMG 3lf~5/kۜ 2jOf26K91Xi`OfBX dPBS2|f2iS\ZS&=d@Fkz<jpw~ȃۚ(hCjSݾӚɽP ᅨ=ȸjǞYSdKgm Bؘ3 /裏oM\&F"zg2~{ ?>'2W DiQv_gT'Ƀ}a@p8|)n?Y@FzP*d`EM{2Fz!l6fFoMi0\wPNLd)(hY}0rS r8h͐xҜ 2cNLYC tȁ "'+-j윘(WP6GdMH C\0)@fQtm:kg{'Pd8i2a$'&F{2rb)"' 6f/}2D 9LhJɂn@26 @ƙC2m]4 dk@.0J |@'}ÿ`O6&܂$'&xbB19__50`g$DtY]]PbW|  Xm`L@R)6/;-̒s?jn|gd B& Ε ĺh 02 ꀌ69C6ߚZӾyIL^S 7vj~ROժ232y<V25P(sb'ÌwnPO]\O̗A̗kJÏd dtUdipW^M´@gW @FIAMP ٹWqt7h x2? Os^̉!J QSɈ'CUeJ BĘ(1f[7mzJSsb.T LIKm42M覠i Se>ɁL^SO f.f'02\\\\x2+ `wVMc7Lē.cC퟉^AZb̈1CĘ˜i(-jڕ91QF/ zsځ )n W1f=,7}|Lq2c49}L}NSH6A^@&,ZS]@bƫS #:KMΉ2 M3X91Ԕ91 TonȀ 1fQn3,L뀘MlhmLi, @f5 dW{d!hS.g2 9hu*P,Fu91@F]L%&:6 *0`q Wė1_ @yM842kxVm\׀2L42RS_ xxB ϗ2'4d,\ēN4aN d*SW0^A| !ÏϷ2w0W/ ڝNh`٬8섓SS 5j8F+fr]ǓǓ53 ?{ܣ⸻.y~pSZꂘC<fa2%z!*%' N1ydvvv~kH}+ Y.ȨIMMLlk*>Svo477sȴdqsmdt}/CMݱbn3|}%JCC<B˔΄{1f+&x522'&>@f2<<882_ l.3o _k j&3dd~|ȴFuFQ]ϧ{dpB @dq 6E2XSl pQtAn=ړО Ƞ ;\.5rVZyJ_ycQړqbN LJ+̗(ۍ"'M[kG H cPSh@{lIZXX27s @F"B<dp:>dPKWO$dyƜ)@$ GM{FJ5t@iN ]-hOFaT<&R6fE3ژQQ4P;V`+1K8Y <@3G 3::z>ncV׿ I^:C 2͝< c45ցvץJoȉxsb2IWyNێ Nz&gN5!kQ~<83q-8 sb hOF C38A3 AgމgL=opo#n wdnnn4؋) dZ"CP'"it  dPG7~y{јQkN ÜV7m>m0dC{gWqP -"f-T >E["AAZ VIb,1&4!i!M[jndo8!'۝?ݦ9{o?'*FƌL`HP&9D1o<2WW[loo?"9b掎wu*\Lv1m/rL!Lk.G[l|ɃZ_[[[}bfߛƊJ؅ckI,j(ts&`O  cz( b2 dv|C;"sd|#Lm ʬ>477w_"8FcnUʌ'&&Nӱ@F:68=@"C-ϭ9{Nk@&V7$ܕd*;ufR`O@_2 _-iSb?*Dyg>!В&ؓm# d[2@f}y/Ah 2^=R{ɲ=wgLu oJ$@F^dj67gVtn̲ltt2tzz^`#@&H,Sк{W@#0Si<םM&ܤ,ط؊KB`O&'S@FK0FX2v{H6ؓY>1ծ!ZaaRB?=m!A2H=p칯WnJ$@FfȬMݻ~_CEJK91WƱUڜ}<ɤw@ X)͕h STAS+ Sq䒫?7,&5F~bs3=I #eSZ @&xh {2T*&]o\8'`Q "'Ƞ AȬ]1E҂'r6̨YY%?dELޓJhYݞvhd2g c%WJ v޽^@{ntb25㗖gs 2a2~m d< @~v*=YbZU5}bĽ: kW5LML~SZؘ2 @Ddk66dח\Z"qV= ;^V1/9i1*drx封̫'ܥ ܧС~2-Vώ!ݳ!K߻M' k$d'{5d`cA*Bl Iox?.Q2Q\" ɞli wFEWϊ> ~BiUļ%fZ5Js1m Sep9̏SYY5>1,;@2^s@F`'fxOL2u=vN0ؘA|Ihcp&qѳ'ӫI6Ύijjz6%τh)6Ys O?=Q2ڬyo@FL.bJsi:/4i]9J-'..5"Zdg2 UDOz&PTb=(Q }b (;eLcriIpG= b2Ѳ'2$o,e 9^R稬4rT6_qaX@Fz{"dяvSz|+q22!$Snr=Q #-d۔,BvxӍ@Fߞ PznlvɁ2 22.yd e\B:8Ң>"U`RJ7nPK])vBK]XJCҍhP5IƘL̝$8yܞ#9LOy@ @\o1"=]j"+d xB߁$/)Yhm>k### d 9^6AS2cӯ@}u_B!-3IKcM&KD?~2Z4 '^㳔/!){ƶ;Hk,W=8'ms@FA2pBJ *uNd8'Խ hC45)he`cAӾOny@FZT y-TژаC..U튙4&:x7J78̉A-`OVJeuc 3Ln?lmsK @Ɨd^ر'c=mƁv~C0sLI݅2YUQL4H\"{td=_ # !*D"[h~@3F갼h|7kfN9ӛ`~C|^S~}ĔwdAhnд&E6f|hni3)1#三-2'35Ep.ŅZDnf,teك2ȉ픷ЂX2~AUF?b2T f:mgȈ†π& d v%2ypWDGbLmh ɡXn!cL&.ڍpZS] #dĿFL.Ks 9Htyz1|,r ͟Gne~!KE #}e?e+mBNF2Ҟ ,57߽bɇ|Me֗M5?E@FĈ"/&b=[Bxgz*E߫4Sl/s|B[ #= d d 'i5ʜӷh~}ɒc}}x FUٽ߽̄0_\c ߫`H)A~sև7dVd`OAȉѾ# 232`c#?}N912c{{246u98ISJÿthiʊVa9A2]^Pc ]x` ol9z2Cq ӧh=q^O˕|MxccMo雟E<d\xk<_Z2nJ n_t~J d2+ ǜ !L23bƬ21o@hN} cZ*V2RǾ :X*VEαd&g5èuM`jrrem S~@F(B9 Ckͱ.ΠLlͬ cAo$aMTDN=9~ #cWd}FRJ_.*"/"c".CDd7[/΋C  Y?&V`u]?֯IM$MI5َ}+Mr7<'e xBQѤU/3ZN!lhmd|h]m| f󞘭 ׮̻"iOcMg7s>! ?oiRqF2ȘW@r@/ :UKt;'$c.}I?2#1f|[|:rboI~EȈ[ I`>Z78}=1BF2?m~O B< 1f$cQSX/;%{b cv_& 4giIͮ9pY717?Qm2|D{hE<͑NP3y:)cfL;(oO"ŀ \ר[9X*[FWHRԖH!DCy&?A@Fv>!`aSPQ 7 YǠcty2if؉4e@@&_=1lGO zb4ï 3j9i; fgǘaaGV2ˬ `2 k#X+"I $r % dHĈbL!ufN6'9 S)YdR+r\%Թc6 Aщ爓6W"nȑ32) |N sC2iyQZ2]Z O 0/A8ه~((@F` x29 õ٥N" $3 tk ;?$%u OCArDdR+GQ<9F%^ŸZt܈IT؟[Q'dR@Fzb J dUdr^ݵg' Ą%\@}AOL ?&œ  =NǢyo<:鬽Nd"c ɣIK2/c#nȰJD=͇Iy']_||q^qmCIwǽɱ]Q* @F41Di! L`s'Ώ'OAkzbV&i_ < M1|O k'N 1ة:Mߴrf f[[[_7;vxUd(l*x?)lDX=uO=|v2_ơYZc&›UGGD,vQZڎx2A s۾5??.HP!hēi8AecA8هڈ'{bdv(~{7E.ޠcpg͐jvwHJǙ9GqFz@F`L]@Ɠk~T\|=ҸyK3teN@bnj1ddD `̿_?)J*hK&&3jN#熖zdM8@Fd74Qdꛋzb H"lB6b̠;и(k+(,n|*;YnM EpՅ.t] +""(]RbM)-"d2$3Dc:y=3`{HuI_1C:}}^̙3~J)OCFSVx#\1V -Kg^izz3'iO !S+8e/S0CFSf8} ~"|* 29 Ni0+ Θ 8`ڂ3}`(l uĚyR L,}25yh[(m jguɎ!#׾}CFv )ෘ?5WF'S(}aϐDg#Y苲cLBq+3>aE 2fdd3*ː‰!גxή38g1M.O֧<֐t%>NG/Ȏ!#g k_2ܴa1:O)-=(OfڟWW~pF[_(4F-fɕ1C2V755d(wK{ݶHqLgC>m'ΤsA^Z]38V mϧS{C"ש34Bq02p_fHCF  1nC'[2Mށ!)4Wq^6qJ/; |MKR\=JDۭEm3d$sCXfzVΘ;`U\&%A^΀8ڵ %[ cug]!,e@Kz:ٙzƒ!#V{n\~: w, 8PJ mTO ʓA4'{ ɉ2 ePLp,n&aFOgG, N-CFre:Cjf Y%߿bFޙŶUq\HOE DհTJP҆}QT(B,#F<:dQB TMNP CUBƎIŎkluo/\S:7_N:5qd*kn 86y߸nP(iZ5ێՀZf͚3jHVn3FMYϜ ='$A<O 4@3Ab6WV\SS^bnm{ᵻ:CV2M8g?<#U YapK;!D;~+{f#ٜqfS-l̼: 8 dC[g&TW#msV2kWW*x\_be ژyhb"X̍r$0yMRFdNCFiAϋǬU ya'L(Ou9ƛmz]! Kr;GGdjTjJOɹ:un[kw:,cbccӽ϶^yOPY*,31!W}T;fC}XC$0 ^ d/O*=S !J *t1f}1+&rr2s f|a^m=vwҠM7(3u-%y%sMu x0#@Pa ɒŴO`L /!~(~:42\_ d~t|i)u`lɦ+d m?#16ߔg 4oc_Z Ә9>W<Y9aHI @28)=S^B<dP3*h1' d20,t ȁ\NaT>yqz@3H,HZ dnMJDə32@xZ `4Ӛ10^rql W1t}~{k46rE'{ dnݩӮRwxbӉφȤc[N&=yY_SJ>ALpo>ט/A/riBʉ'C'^1f0!'c& dJOF"a+r?J̛ ǿeݝf~JF黱!~P@-M_k{fB136 CR'sY1*P9 drG[zQZ(nCē5!41"g~@Gۤ JƗ2sxr24,a)rG0)B|Pd/sgO~L\ |M5)lX(uF_&O hJe:p^rM3l WP>8FԨfSkL& #zFG&u0ҙY8UֽsdB?jl^k/\2 x 5cV6.(eSܶ }bO=:&A3me 1f/SP>'d g(ӵl2/e'ps*` CjC 0TI'"\yDyQ@^<@n ݹϞntR>ky.Pi+agp_S:8ɤ~ߗ^q6 Әx2o)bknR Ð@  QZBZW '@ʹ 23AbH<Uȥ+czΎqIc^_&Fi dȖ'(bYb\4xk"ؿǃUd9 5)2 /5PY,o QZ@F̡_h4Q:U])?fm<,īN$yۋgK)20 C 3Di߃1^9?؇C>12BȸzB*s,# }bd xOs{QcL+v]hǜ}^VSm?#6bԲʃQ7T W5;w'!/4o> @&) shê?_ QvYՔ"'M ՆOvw S{IN)deOw$үk:O F{ϙ7!J+߃1E;8_ .L̖Ng f1Ϋydl])cꢉ7ďF2chŘ0 R(v~QFKK=oܹ?鿇 Κ!x' 3d LL,'ƪ@p^BךV5;y'ƬݽԨ;׵[GSɠ,e=R zbƍԎbOk@3%Adeib_cN<zb " @SPA706!;d1pdV24LPWkbcĜ2L,ƔҋdXܱc&F %b4C> -ooI- % Hû''6|?(- "v/!dŶx\YdD<޽c@̘2>b2\lo֏d ApzbO& C 5} 2YCѤ0f+G!#OdJY4JKW̳Ⓚ0' @2 藁 ĘA,b"UkT@F@7x2;d;dv5VwM*}7#}'BTa@H ` _s;dt2%d[Xj pv9ut yM?)M(-c&/)~xNEvHyBO Kd]AU!?˝Ob]~L6 SfV\g>iWn?xH3X#G.7˲]u-NQų>ExsΡd5{ezȔ:">b'8ؓ29t%)t,iLL h =1;4{{bA uĘAI2R2'F X(^Ӫ/;&N'Uɀ ޟF.L1>wthIbd ) 23,@2 d쩸"F)ٰ* m{|k =1x2ē` Ba*Hx2źX}-e2=1@ rZfɚ6ى_% ?wkb0c[heU 7BFQoTr4u=]ҶPzSЂPFj-U156%Bk&im&ݔfMܝdZ|2^M ?3/> :`e4l5@ΨJM}bD2 ȠݝMJd*}~(Z2H2 d' dZSbq!VU;Vp@&:VZv'cs(&'2BaqcqUI̗2A{2=@F A8%,^y318L/2'u\WNL&;80m_U@72@ ddz[ Jv<>Lhc|MF{2 sbdH:P=DȠ=& cߦ'p[a{N7!b}'#im@ÙĹ1fz6C. p dTbL)Y;^=G^D)dÕ@&VZ1 00Fdsb(zPE/X&A9'Sd@ӵCvuo ƠٖɎ. zpc$d2=vPUdpi`@ϟ1L%7 ;ݱ@,2U͉i8-5e91},EՖ=sbd؅NZWR[bgo6m"d0'N`ũ*-x}wV/TKNt{zδ=Y:a(2RE/ @sbt'rъЇ>A91Zzsb(d'#a:E3duKژ_ h2@e/e[1`cgʀ jLJ'SsW`bH#y(k2* Lx@BN> x?KJ6RҞO!#ȄiIr5m=sb(J\@{2@]1(jE/ړژ0C #{̺{ uʼNN)lr'j de,մ/ K[~PUdOܧVF7*^m}Q2o\@f BGՁ Jq=ff&pɘLQr&ޡdxJE٘Ba32"G 049\̷p`42vQd|0_4߽ R{绗 Ey|i3]=dȬurxܾMJ+tžO Ϩ_]a2rVZ]S6ў8i"3CI S%(J@jL|@lO)MdM[c0_}3E@Oxgнx͛oNT@ާJ@fjFxwd\<| fspa=~ ÇLcx#gKO0 s\ü^;"Hvs ̪1hYi?|: N H e#` yc L;2S-;,|λk wd9V0Ch2~P`}FY| v dO3Ml9\O #[O d@B e j&'2 0SuO uP~nc?9[>uMo,\>uͺzW@FYG( 7u dX2=Y}`˜5wWΧ 62ǓIĘ5œ4aH` b!Hp'pckq b HL@Ol )@z 2͙W11['&Q ATҟ3jw`!sm<L#;JxO !(YdݨM 1fLk$3Ƭ* T4'SifgmL̫'â5~jvSF@F{zVW&f7O^Zݛ[`=1ڀ}x~P`}Fq_Pc@Ǩ4 Ƞ'FpcjPLƯ}@fb8nH411&&$@k!J|Ed ' _u:Ԅ9QjHu! ll[w"?s/']ܝ{v B`b" hb<" /ƌ,C&mQVO ='E?*evoOf٧]}: M?΁zvWz-uAz,ܢfEEEЛI}GgΜ9׸NuuWǺ ?ޫmS:S\k!a1~_4GSAƿ^B3q7}i!>1Zs ֕[H@8ǓO @b p1c;O4'3#˼<w'cxFL7Yu/PjTѱՆju눇^_H?Y<{`Ut$E,^"XjVw;Gqa#\N&!x2y)2&$e( BƱQf'|I'}bB!_ NL[! /r)DQ V+nTӼ2d2n)/k'J>T\XXqkIȔ6g*Q&CuS5Mǒv2bN#inD.^>Yrṙ?Y=3Wf~]R9S= _1;V 'd! c:b'@Ο3wEN.dL+Xq>GCX;nHR*7n bnMLml:nq4wBSskGVqO&/d8'r2ס);P[ݞ}2|!}bcOd*@;0ûgL,x u"& O dV>L&&%_52,h (.1\BH1$($F si@X tsL&vccc`zZy5Yvھ_٠4yOK"䌄 Lh4Ot)DB&5UUSb>_eZ&Md^1#bἶJ/mz {US$*yۓ*dU*KV;!+,'c3Y\Ƞ=1"z Cڔ '܆D8912 2aN eA o+ ' Vwv=0%8L>Isz@=*FPC"$dE -A!㢣GZW(֖vkDZrsz^2thOdBXCYhcО y|6fJǑ8uG4'BFu^BfˁRE|Y'j궶!C2#ۓ>MO*1,!!96ܕ[$bu7;EW%dϬ4K|  2#gP2d"˝I/R,3 ce=a{%$TVDk!n6f^@`A1o&MzV! xWS{f.Msdsi_wӼAAu@hG: "{| a1پkqV%lMϢGs$Yx(h"2&. w1rk"dg!dC„LZiY+!!2}N!gI.?Ad=QHn4 |,q\2(xٿAWM\9B5f ytlJ!VUĎ߯;m$[4.|JRȤ{B:22SI kń c[SLq?d'! sb @:|91ȦjOe d$dxBƵNBf枪??/J:`ZB&(C4QV$`Haki"!C-]5MwHL:͝wB&h` t2I$ x+-.d?5Eſ5^ȫzH6yR ]sj1iEYm3 iw 'f2!d:PȳVZwMQOQ`P1Kj0FLp/iјF!ЂCHD7tDQ&(dK6ndǞۻgyY49VZ恌Od.):j0%yCjr(d R 6fXBeS-E% X2N222-́ Un.9\S>y& 3U2k) B UdyeC;G YNhLe{k;sWȈIi00VZ& -]@FƵӀL7Sgݿ+6,~tj@GO9\\gbǝ"@F= bk7p܉@WjmAɼz99d ctJ # =MȠY1^eҤ=+;  =m6'˾ d2,;J6Ȩ2\g{u._@+ Q k_Q(O dnF6i`(015uQG xJdߛWE ceϗ2h_ {2%\'\*$=A8i>1 aF}bm<U0@F # d86psiu7LС8 #f{,,(p1qV[Ĥҕ;zs y`8Vz3YŸ H)L\_|v_ ?]uuyeJ6 #n{2}İ=(}#@ e m2no+)S2z}bl'~n p >L d x^6Zd H\1Z79U2$dܸ(c*… 'fH@F2w$ě"TR1!3"y!vSCA'#Of᷿}Z q"O̡NQ2s5y,a}!c%J cJBSֈ'~'Ks=^'}bxPA|@v=:0~r m zB(k O^c 'XZ;SX.2!dW7'ǻb&sBft7[&d$3'ĩHП`ڴI)'f(?NI1-gΊQ)ODdO'dxVzB; ?N_O>1QfWb =B dԾ AEC )|Qf}S+6K2ġ1. _Fo~2Vl/dxٜKǤ ILX:OIİ>1)rۂ<<&ݘhcqVT:/flfd"h?,Jˬ\yLBƛQ8|LO@k5SAݳB<=3 ܇K!)r.yZC 0EՖm LBw Of;Es {ѯpZJBf9!c̒ês"mLXb=26=<}bN>1Vcޑq_/=ʾ5~sP8n N?yN|œy6p{X>1V2ޫcĄY̥`!$U`5@,|G(%"Z/!^jAm)e!֪*>\ܳMp,3\֭`/x ] Fşiep&d$n |dOLZgἷwqɖNh%O4 )Q@ iI!ǔ "Gf}bJzqVXk# @x Ę' Fkޡ8̐PQXVvӃ%wԍC0B jMU؜aryQxa^И|&p!c@אj(dB%f /3o1FBFL)˄7Sd<2/+OإPw6ֶ"݈5[S-^1N2 Ԗ[#9P%9!z8"dO>OL+&6TAb! ھ3L̇v'06v&fߐu*NzColŤ6#d!Z7ߘ"˔;[Hxi\i ,SV娐(K&s:[ EU 3&mbIȺn=27 'FH2gV5 mT\]TċH_8Ei9.d2!fzxq vO_)p }bP @C{ڝ2שrV㤗C.f[b 2}^4e !5j/Uq\dCԡ?nA젣BhSypLaꃰ+ҷbҩ0M*> ɰa{ KuZ4{M~@oMss>|]H{Ȗ-L,}̀ ^vCd(G߰EfTJi'@[O|;@~G͋%Uq}y's?qQ{2K혟2ܸ|鏧ݟ@F)QtM=uӞLe=jjjs=&O+{;Q8"hc-X_ubj2!{{m8:u/* @XLPk Jδ{2gˁΰny,oMͥc٬M@%lzZU2EY3 Q'@4tP dt?Sl2_??Qd D !՜O),c#EBWw?Е)@2k8Oշ!P EFmhcNv@YFaw0Ӯm6:hj5,T2h ct'i?]C15hVsĨ^Eb`Q&u'{dVb_=>TING dl2۲dY5M,2MY'l:1dz&c]ו)~=]<*?ўd(@H{;,OIOd.(9}5|8 ֺ٘@Fam6dg1N٦'}&qܕ?UqMwk_ȅ'~/' l'&]M-KWSsh #۔ubhO;QnD}5@ T4<d(@Inm}6L2֨<Ql9FS1 z+ d_onOOU "=g92G{1VZae1 ~qm:1'SG=nX'Fy k@@ 6foQ}vn!JeѡJßn;b|x L#q@Ο H(BNV̈!UړSc#2=[lq K}J ,2geҞLu/ٓi4$;3MY'd(6f\U Q'&D@JnxEЪN/O #(@FfDȘC3h dDoq|G:12S9Xd[6?r+z|MiODXJ 2tG:1E CQ1b dbɜ[R$o{pKe{;1F@ƯLK2@qLT2$F1g?2R>qѦ' w;_h[UQQ'>*SlcSdid"`D A.jq8:fԒҖ Rƒn:7"%M-$?; s=" օ (&ɺ$v<‡4!d/C\]}ޚ/dB\S>tԊB')gv0$c?~|-y^s"c,{qX݅LZ꥘C_CH]NáN $d,;V$d I'B|P# d g;?ˬ@c B3\Ȅ2!7d}(c([z>3޿wN3 )}ʟ2fȘ=br2KQZH?3AHX&YkQ'F"# ܾ&0W dKo#B&! 5/z2/ۜ}BČg}nm !c[In$IHg/˘]^+ 1oQO%d5|ABBxQ aV UyS4& 4d~:1@ͺۜ !!Dx*/@HR eBs%wr{ B|2yk!C6mPsk~L!y砱8>]P?S/dJ'ubvaRS.dX|':V7#E{N!d.I8mB<%WB&UO2N ëJP4xʹ$R"%?.4* ET$>IJnP&n*>YQA!joV$d(j>5-fƏjUHw'U3C;arggf8?w;{'{v9G;71t<oWOFY {0f22ն/*"0_~o9îW>i.32~oP #w@ЀtBr#: B AIR<AoKK qdiSmM V PN!̔dϭ8Bdch0IE\Hm<!<@/ asip-BA=]&6nD"d r%z$*[J˜1 4R)H?N2aq\yn)}av#o<(;=s q<;z!sMrVOG ~kUc1iS; \5 B3՞&|"5@ A WC[5/vs5ouJ!C!nk_w8DP]3[l$z@@TO[c/uCݏS {gWqDZNZhjlR(BBoIAfSAMjnEMUPbwf`}cdK E!d673ß{ws3w93;;s891i[^Yz 6e@FM2/#C@ 1 GZ͘Fd H;vaiDN f g/QLҗ՗FH|Vdh]uo|쟽*xeJ{2yzm>|Sy ?@@S46;>b}&zx^| mjȨm| 7N mH ayj8u6" @d"s"ZI]'E id {9 1c}UV/ ;Ifۉc: t|@5rNhZ gf ch\ܳY!Ɯ^vՉpd@tAa df+4]݆ `NfomNU@> A2 =HcfV>'u+NŬ-N/}@i'Z2$"/}^O" s^{m}{dI愃Z(wtp|3c%D׳ ΋*~~2]9[Srf4d*ȶJsd.~`6} Fq~HvdcF~DyO̯)@9$ɦt m #dXGN ˄*o3S^1N> A2dN cd\Hc~yle$@eN/dtGlTvo-@1Y'LX OM.n=#C@f5sy2`ȝEhïoIi>ے9W)/m)?_S qwyklOL;(*"Bd CQl̦2ܦaJOClijJqox.0E@ Yk[k{@"!fj>}Q;3d̋-ṕ< dȨӘ,sQ[LRAS;5Yt{8NMd @QrS;JGl>10@FeS5Y=6Ez2()ő@D1 @ F#P@a/HE}j6ڕ>,C. ҘyJ dÇ@Gՙ2<A@G?i/:nEl0yP%;͌S3 v@f+tjN^e@Ɣk4ڦ l qo-HO!YyMjw fRz A2d:rdZ2C7cgƆ=t L2O1 /1l5 :)u 029}/t~z|ZȽjp]} P_ld⩴dٴ8Gs)C@o!Y3 ߤ A260s=]ԗ r")2l޲dV_@f9rvo+٪sl<:ѱ[s&49̏Sƌ @_ioVa-kc@FXJ;Km Ϧ$?ub w d!0U#z12وD2 Aj94fPyBX2W8 =e_w!Q g}/E[jq{ra!Skت\3:WcB"B UKSd屩: lP'1wPCT?w%w4#d|yhΌ!Bb0)o⸾ :b8-d~fSLkcYЄ (?A8vҚמA][ªU6+u֔p<'{r!/hKq yW˕?nC\|8$2/ ?8HF2ő/)d2)-bJ!2&q* %MTs/d<8w.!2|M~\_Y!_1{Lze f&dJWu IV )!Ӗ̉^&ǕM[Y+]}1NCKv/ 8#IeoEj*o=WSJ K~]{b{!#KiM6ShbF˘RH+{dn)˓1S4O}u%ƺlAPB %e˘֬<)ׅP[ LP 7t oBHjwb B2 HBȶjZE,hTdj_hx7Jk9$%6 !?!SrE;Bf~[2c YJO S5ZS k񀈁8dy2|oub 3c BȨ:1f,cF0uE Ĕd2r} Pp}i[OIW^OFmV6ae-`o$:Nǿ|oڱ[S}$$2/svXO<+"9/A?2#Dt`>z!i DL9։18F@>~_MUzy2/gB!C2n~c2fiy!c:\'ѯ/g!S.;<^ȜqDz簭^l8f4eEI{f~UYpD[?@\-ڊ'Ls>r<pݿ+l*QF/s%dFNzGȘ2i $ dL+$p=䚳,Ou!ŇXƌp}B$ dLK<]ȤK PsYNB&v p$szABE}zӏ9IjH+QGqc)_4 yli)wXH4Ey!R複^UZ1#L/d^B15-d30Ky2l Y!'Z_FV>K B!C ʘ< =`Y]!c:sd߄Lرj_/-jce-2qWl?mU>,ڴxnrc?j\׃ق*_uƎbDGHg7~/ʝ?;W϶Ljr}2= qW YJ ѺAY2SBF! ˓)U'2Ϲd9{e!ٍ),O}›޹FQEq?"1Aۊy*?A||CFn[m} *h+h|` R|A Rvc[Ǟ^r·d̙;3w9"^46@SkNLi̼dpy#L҆OBrfg}v-̯Kdyqr?E] j&|f62[hAE՝19G>9>hc>V^~Jep^\uvD@}nMM]m|lm. Ӆu*-{^Swp6SIOv>tG"/1C-H gH3HH$JpI}ߤ1 c>vYid2OcE }ĤO)P8MffdלhkbiF@)m!7Q 4饖ݰ?{Ki8OQ4N"P@4A>@&NwVھO@fAm"82TZ2̧ dOd^1v(KOfQ?"/13MD`cv`+ DLv=mԉ2.5g W H-G2诣LY=9E(ݳͯ.YiI7A0BT^iܚ_j?!0|!r4=G<{mMaN/s[,KW}9ā;aH l?nkGd>;ibFOѧ$=Vp>{GyHH$x0@GK3/y ^z2N􂭘{/c?b̕ 9PbYM~8=i'J&)E~EsdoA b@G5FQセ. NmҔ(-f2jctWU;E,K|st:1]_]> pȰQk qJs|ϗ1"gų'2"HKM7=4fsf"-5@Ɲ אWSӷ4/cI'CN/?t-28Y׳m缗Yv R<Smhf\:Ȏ۬>&,VeCz߭q:OiKb*ZDKvjE ]w9k,ءzFot,PzdOO8uT:1g'5&D^*AJz2/# D.=QK3kG/Ɂb%qGl2)Nl 1C (*A@A5^*g FOR[wF#=4J]Nﴤ]q󪉝~g@}+?%z?dMJi[^˚}~fc9}]4r62`# A/ d:12<5 zOjL<sz%58dH$@F$DF R|MT̏1= lŁ 63{`+Jn OOm$2v_ݻT Z)jHoe60Y =s\[֗J+Q ea113dKՉ,>d7x&=Ԙ>]w4f"HH$bS~=R_Fd{l_@dL_pa+?62Vғi}z(ʂڊ`r&@fΆ$ շ^ѫ~wM]B6老(;^60P? DWN̾nϴFw7}j)Їf Bg^lyRf@~ JK d 13d8!di䱙&Łvd02N0C>-{$Lުv?%hSa &+uA B Z;ymZ}.6O/8lR͹*~m̠NQʮ])wv +y s4~#󩯀Lc;k$8+O|ɬR'&=IvHH$qA!eD,W/'$Z3)~pa/) V'do܋[.vFr@EǑґD"bTm豍{(jpdXu?~zk(|ѓ@C61p} _fhb&b!HhX!@D ](j*4X-j?SZa[JH)sL9^v{{ge0;{Ggsd^2>Y'[Pc!=xy  ՗A3`u)鉶:=~y L!#eBF\KV:1}B%ժU{,t S'OrJsŎ:^>0 J:+!ٗ#Q'ao:/z..giU5kԠrc1*Ьmrlk5[C6?>z\eh0wEz(ddg>dH—A >2' 1ABF'/)sf rM-ddz2^s ^rz&uŮ,I1]/ѤZNc6$!3&Z ےki?o_HYB7I$'KnS-/,d jl5cmEV2^/,b8EY3)[Q'e}'õ 0iF/0򍅌󙷡2Rv%d3{R Ɯ[ƜJ)g_䯾EMWŌ#-4~i4PΕ;x>r8S߆{o8Vjk}a˂4eovZB} 2wKw>Sx /=ڹN LRN'sˬ*5&n/ #==R!aB[><]Lf22g|_V \!#g؅HO`-[ "qznSե^2&mMye1{! &﬏Bu2r,g1(+YǖL z.d$nMcƟub dS'C:1:u @Ô\^&3>L/La!#ٴُ );~k[ƸDHO`_Q@91cWU4QTԉ1:飃͢*6U7F )XNv 5jZ/lYTCo5Ҕ9JkD3N~3Gߟ Bfh}d g Az1 2-ub0b֗ˏ;_9t̳#d}L!#e 9ɱ/OX}";_PA?EFnו+Tk+e&/{SȨa!s(tcfO#d?N{Ǹ'BۜsOG!8 rû bA3ҏR!HN FPKȠ0YgCSx]SR膃H,f̜!3b^OY&R!ޒ2dgjZRI9+n4Kttc4mMl[5qtݑ0ڊqfuyj[-{wG4G'ك^2 ub>C@"2 xLOK{2ez2XOF3Ml{ّBF4SBFȎLf#L`ߤK!f-iښ.۴Z^&ENqKb2. cK\ a)v$Uu' d< fs%{ zA φaHJXA--h+ hSM)8_m 㾩#ۡ{f{fy.ݙvͼ/ E1<{P0ſ:zֹd-2;2 1\~|Mg^i˾ j``DMNLD bpFn8LY@#ox 1?& CaLw>&Ӌ@&8<{PWđTF>O2KQ2Uf 4=(TT#*ZYdPR] @aGtu @8'*/ e)E8pPf?H63S:Tu gjp e\2+)O  ׭2 yl+O {P0 2,w?<l3 ~pqyN"Q'B2(0 %LnH+1iB:9ߩ^L c&:v]PKk碝~ {{uom"X֯E53kRY&7 O7]y}_l[۱LY-j+([kc_-e:1tuD }9U1=2'+$bDӫ ko$;l%(Uwƌ#)@'/Ce!R@ԛk)7 Žu yb2rla1lk^σ1濿6VMm;/3i4}sEC;B .1T2"}ʏOmPWq;X&2f L26Mjlc^dbqhOfȠ5L22'fD /OY/ O.cҪUL6C?yUMdØLNuw {,/JKwt]3kpQVL UoU63y ~kE7_m?6<)$c\5dybOoFH`x҅'cDS EQg cF|ؙU Ɯ@S.X*k`iD3-&ޖؖV5dNAT\&1ھG6`ƽ;]^lO/7aA7祭fts )6"EQm뗹(D<A@I oS W̥w:g x1<˔o"(]ą w~(1sP@aoax}NCx2I~[7<)VkVqUS#T;gqOrlidUfKܭ7hQ%|&G5 /&䉱W-Hq2a('bl94 f旉/ EQcQP9/Sn4<67.ϩéX\JdpdϞ-Z)@jNʌ0]T/5wi'2dPʩy9UIdRe㙺j~ù_ZybJd.7.@SG;$МHrPNE CQ)!L×e/cj944f~*07^/hD4Cr|\1"B4mD zhbmk7-XABC( TmH$#Bh)-_n1U2:lY7D'دyJn_>i~di. /go~?8?<2pK 1ad|ɐ(ϪNQ5^Q]ʅ$ #IhZ2OE-y )uJL@'t'&zi[Kïe%,$C@&ae.n͝fkǠS&3 +ܷ׻M=yK{>}o˳Y; Ƞ (N |2$N^) 8Ю!d=HO P${2͈ 1sLȤŽڗ0^ ;&D8 Z˵ 7F~-, ԴUlYOfo7 KIj<_r۝wƮ_KȔ3qO F21'L:|S:"{2yL$ HܓIA!7fZU>0@/;2&@bd%)lvO5;ﴨ*RS U-ە-ϣPvqy|ySzN}[ ]Wx1/516u'L9L͞ ´ܦZrO{2VVI$'4b12|^gȔY"d.+e;vOeO֧+lDu(O DG˦Ȭi1GfG%3ڣ%l #B)^Eatŀ.22S86z2? Z&_2&J2*i;iŅ~>h;/ L/@uʰ#>ad}̾J7]|<_#}r=-0\6l R4CEgUc/$ybW6NpOmS? }? Æ~5FJ7wswOl$\I@F$-9)Ie&l8Q߀/h '":e䏝pA.ȴx.fnHZL7M)kͽm:t1nv:g¶(irݗ2IgdɏO2lG{2 2eJʼnMIp%I@F'Fkj5t ݓ2qI81Y ;@0}.?H&NG -+ NKϙ8;w9{T~7f2{r$?@O=Y '81 ÿ2$8'F^W$M•d$Iqb$ ҥ1@@2r3~'uʰLʼnqd\zc6i"򧼣n?q=ed 1d|-]됭8mG瞩;j<翛M}ǠrT]M/ܯ_@d_֬m2~}m?@&򬑾\Ld_@n>:ʼnOĩi VQjE"h)yE ZFE|EFH!$IKJF@&!զIS%-&6M2waeύ{sIOwmOQ2+=؃2m,џ>uztT d812v33mE7q `LqA2V?4Y@f6ζ 7-ӅMyyȨovd\q012;2nmb81e4?Ә1=ӓ1# }"(RA4ft_vdt0zNdl ao';7qbR22].T dz@ơxã;an׭>,5eqdc~?Ͳe:.\3%ugקΛGvrz ׁ7zg:: dd?UkɹS]Ok 2U|;cl> #L 8 ǂ(b(.-ֽ{y;z)ٵ6lSD=ʁwwúN ##{(76yiOR}¶U2gþN=ݘt4_0sN #ljATLW3-!Fm3C#Muݴa 5Qe$d#kS[րp}̖υ@m .'Ѷ2 / Go\m4#k^)*¦t$&bӴzqbeuͲ.E CQIǧe@k@ EvʿMzA2LXHa0@y`=e$/TuiQ2ڴiwswiav8pl4kms2C |D8hN/|t$Rq'LOqyS E1ԓӘ-ޘ@& .ҸޥM2q d*[Jw!@3eF5:K{;DoΛUɗ<Z7Z @ _@7ĸhN/>35EgbI"(YQ{P )&8/;2ڀ!d)6Ez2Hl d`<^wKhshg]y{./ruHE]rM`!UԹAm/h&Mse1@fZ-@ )ʒnu7pwz2\<2{ojjGCq>;R2/{gUhb"`M6",@|P4JQE#hD#!( ԶR_EmA@ P>7'N8l]=3cvݹ7ssRA1,Ø.#F(>k@&<1 dɒpxV@F13d$lqz>f]ϪvTg߮w2]sI֧4kp$dlϑ'7dP@F'+,)d8Q+,Jޡp)b33(8heN2= lÓd1,i Zum{UC9a>ʬ^^]즽Ӑ(Lj&ൔU8&*mL;*}5ُ{ׂsyf\k?}y[gc3rgouﵜx]Ł%p<u8_e(Ax=q@&4>@&, i C2O 0KQ2%g1 Ș| Vq&M O+aFR@I͕3ǭ4]gteRCIR"u|mPs/kkA߬!|7dWq'6l9˺6Ze"m簭uBuX:xCR@oOOA+ 4datxvzi Pt$ڷ'Op)@l⌴/ÇMx t&u@y]\ ې2 -8X83T Vx0A dI gs~w hN.6QvRU4"-@0*dT#_ |t$Z1Qɘ'Qc(N¥d(2&_@THa"<1_f `VL:=;S^-Id^sPےRGۺŕ#`C ̞mp{js?w[` ҂lb~_l aJ!= wSd vzpGGx OF12'R ť!F{+"hW7恱Ql^?m7׏mրPFlÖ#_ (rh3 |o& 2uC@,lpI>sow2PMź`.t|@F3- “݉T ~rWqWdd#p.EP1(Π!$( dI“4{n<hOCA?- 3 cRsLU]NUֺjއMnF !z3pGXw0rlը.QJ~/}JZ6O,kr/y]G3VoprٲRvmriSmX9^/ڇ2jg>;'&WRRq|Zw 8dȘN/1+&m*(Nj$\@ܒbH*O0fVC xɼ2:MÓM(1,nMg@+mmy @GEU }JZX!3}N.>v|$ __H`6-+v#b^3S@F'NwO! 2eK '2iW^t$Oe JF?p)QƁ{s˫g'&  lpX2~8X-O_'> M@ eQ52G@DDb}܊=d @2zvz{1Qd|4~ tzёXؔyb(FDR"JvI%gBQ.<>xH|ۏ@&~ | y' dajge !|>  h|ݤy™?{NWA^+]伢_.4** JjE P5>mU5RU7_hƴHڤ`|&M$5Q۠*DݤyPh]!{w?ɝ3wߙst_j=2קRh1:v$zИy#ӓSK7-ni$odfA W%)i9BLcF c0=Y`K)6Wf3;}rҢ_Nqsd d,tp2$ϛ`bdJ)HO}z c1ZӋAcK})439.0eɇ*dkLoP{bEQD`3\Cӓ遌G'gS9N dd},Q}dWcөͰKW?8di>} =m_6V+<&/o[8jYu={3%I:_i&MDUT3RXnׯڧ^fgR79柞LdLy:G^:A6Xdh1 ƌ":$E1"f80L6li =%Jk c&TFcy;[3C-&D[lQ8Hfָ+W-۾':86hJCXѲ]H' dih#6G377طPR2,+ \@b0bDZ0f>jsȬ%@1QT:\3i̤3TᜠHքX@I5Ytۥt0Lo]@&.ȁLZ=FĈdր~~*hyoӋDSbS։):;W@mKP|呢O%֗)xdo&g E60i\ck:O@? N^{3|X? # 隝t^-lMjCY{pyc:QC$M7A+ӓ%d '30OU'0Hzd O1F#|N}nhb ЧW@ZG4=LNQF|X3'KxT@FjQ' #?Memk^ywDV?=Xh(:N/.2g-zc;LS$M.R0=A=zǶ*x⮭ S݄tzёdnSQX'@YoKPMR!5x4f2.ғAjW ˧/׉y~u7j^r dLl[vf~oŜ @+ ˶N @xf9 :@N/%^o(# ðF/11`Ԁp1iՅ[[%YQ@RJ(KL-RQOvA/L@4xaLTyK=.3͙9s}Bޏ" HZ(IR/ c& !?)^@PV+'ViE$i0Q@eR! 12“%Ȝ=<\ `!@Ʃdq3 zkz6K# >K%SV׳r;Wַbu.kSif;N.DE>=piԺq/?o_;on3jþybI~WbV2Zx2Mr @1twƴDs&$ӧkviIbt@َm{nI]_Le dLDybdb zOag_ v,—Q~sq7ڥFڶamS=>~>8PƽGױeupKߏ>_9|eJho4v,g+s 9՟0{QFm-}YV+?.W-b:^/Om&:>iRwQk<QK9Fc?@T+ϵ?\ybw?yX'RsC&iB HRt@5=1j=T57q@%?y Uu#@?@˝_1;F:8!ba 8׫eJ/MFXÂ\5x8vbOl+R!6w{Ӛ_rXeVyl, d` bFٟmІ@by>6ɌacmbuVk5ÓMMLRT @`&4 H)EdlδGDQ1@&'[vPnPk2 +Q%d|8hǎ9gǺ445v4Bim6{u`ŹM6tUCC[7p%*T{w5C\oW6QF~9<'aʔmĸms$3? ˤ{5gd '];_a/ #i$ #%~F^Bl8?\^TL*<1xξ2eF.c?L uB+pnVP^뛋QYiS=ޮUʭ_F >|yhhdPc d_F7޵hQI|ע c{}^kwv4{k*$“2g4qb\ip{>[aL@F0@&kJ_<6*Qc?LjF(t"7T,JU9lHжmAh * VۊF0:;nvf۷o^w{pGV1J .Ο>HO=]$dd6d*FKW`kpG5m[=9}%qG1#2 ne(yyOꮈI Se*^q@ =dO "!HD7p`l8:S PSp :vFbڞno9&B[z-bYQQD!3*&zDȘڂAdr"b =Bgx._ Ԫ2LcFRS)af4f@[ ubH @Mǡ!λ z`++΄ ~;.disܕ諽Յ%y b<<,,[83:=6u,n4J[#ؼΰ ato7c_Ilk_X/*_mQ^quAB&#2&3CUw>v+2Ώ=[Fzm}_`=Bt#POv7WwnNqݍ2DHJoRIBpEBf{nc3e$*p\`v3ƠW0<Z"dD1@H](lDeM#rۓ )K,(ة~]C_wvvݺG]n/Oc{lH/~b!#F/͵z(X'mBk@:(%B\#SOi/C!Cx#q4_e2Dŏ[FOcF!ӏB&OB\'&~$d@~8.d0aOPaWF2u);6gCx0f[Ҧ[[;2FDȘm?{{v*SCWGU\GfBfY߄L-zQGIeZm#B&׬;)d\!Y( x[ɘƌp}e(dGR>H(?g~lJB&U!qWd˂EccIOdѩt@ϥAE5$P1xjjOSȤic_+7P}]~@mGw?7  _ tό&SeF+46jݯX>n f;K*.&kr.rHFz2{t 36 B& OEpJߎ4qPʩ~t{p}74f273BK(uDZ \e;7c2L!dB#%d0;~q(O#< !Q\&dYs遲Eμ;PDJouMG8۔-6Bݾ9>X&!;b!dݷJ)d{HH9_6-4'qd ·B&  4f ~pg灿 qvP90 t?BH|r)[} =92Sd@bϤO[ɲPt z`Bn-ܯ9㢲Cx4Y[ /~Yp{Gׁ.,I d_{ޑon_~$]?YM@v(*DئLR(S+\T/[m-ȦA#ɜ.T2r~ѓ{\kf9Ҙr) )d׉!8' ꭻQVMHB&1={Y\=[:1 eQ@!@`\#ȟ,c4+'ed(ťkmDrRN}۝z#vJB|F"fkXQ}TڧŲUϗ e wvMaO51%+0*vc - ƐtcPC6 Q`ZaF{=cszF;|=ubƨGnLs%ݏGڔO$ i <'J_22Pa|d=E@F @Y@I}w,L@ iS։yNՁQpCdT1z}< Lv@fp#xvw: 3ls;->%nG_T'.!pݩ?޹݊%|wd djag-)U{Xh޿28ݏ& )@ @@ J3 b$G4= eL Yubt ՉI(ǁ 7tH2wlK- g,GeiW?.7DK>Mds]]{4@kdшBئHO4f@/ !TO2)˵+W2ȸmUJɁ'#GS۩FJjI=:Dz2ۃP_]Hc  }`f. @\Hb! /^`Ll@ǀ .ȨIHv!1x@$39[.Ĥ0iSmN+ _sO@F ]r˙yOOv͍  1I*;0!ubiXտ'ntudB3xIL= +d =sEm:-KOvh(=xyo>%er 3Ϧ *v7 E {P4=r )yVc|x SH_έE> ٷӌDLAe3p+dx8ZkSzm_t{Ҙ@A>!#?ɤ LO #;g t.jzrsuYfQ]#WP¥"oEPvD2|¿vXep3߇LL+ u6Q|U|2U EPzc>6SȌ|?vdSj B4f:A Cԉ a=#dqx:+sX}^D)eޱ&~#v]t|Lq˴ol]fKK+5~Ir7`1y5,Hεsphw@wq`ߙ}嘩TǠxYCm˻Fv=A:Q]|R$%~9=HOpej Mze/ avdV" CeH45 ISEfxtivnS>WƽQ^)d(db* c"d/mOJuںѴ^_)@/|" ;?zOuamXoc7;oܲV2l6c.6Qixwf8hRcB dB}(i: B跅!U?^w@X][>ۣ2 kSɆsx#(cnLQ9`UkS*GbJw17yHD\ ekx Sa n~UI75e#0S]V2b$2'42pC>(oW?z~[P_A/>|QH >a O>&<ڪI(mY,.mKKvL$?t!BX 4Zh%Ts2=msޝ?3{gٙ;3w9j Lb@!(^㙞+wGG MNMn~Wd4 @TC}Zӻ=~VA$ꙆԬIu~2U*'6oՃn_V!Jo#Y.5@ΩGFF'$hipÚ' 1?u0ډ5;[}'ӽ-q0A~oRG'~2c %-1+@@1ж(kcMk0q=aW\ dI,ذ)î+M52^5> 2.q E,Bs}I;???L0s~%V>q _F-<"rs['u@fvC?ld5 )2;@2gLNI ne,P]M\_a @ƥ" uAϡ>ofo~ ^qn>eTx26a3N w&H k? =+?"2-u2ʨ\7Yc>.j[V>~%|1Cٶs-l;}}d`] Cyb~;]{Ͷ+lk*Gv\#54~gS0&jjNu&lX6oienQv;s OK:9~r-@lKۿrϛ)r˜AJ˜H,ueO^cFᗳ rd`>dI"`hD :ؔUNɝ?rgd< CbG鞔DYq]Мr?h boqA3>>4|k.@ftpﯿ(6HKkb"NFnR[}䉡eh+J#r# {h~3ݎi['dS|˜AEP <12< 5 3s/TN2WaSHqЇo8;99 Bo笏E ͧT;a8Ɏ魍/ Ix˾cB #2,OR;̣лvAPT P1è.qXx2䉁^{tfz2,O j>;t2M_Vl޴l2[HG?0_i@`x`baڧ)g?ߧO94pi|5ׄcrmKfd? 坡^8>,:Z/YcYm(od c:!<:"˜AP2/ dah6 $YDx22=j:t2e}gd4>l8ևYӾ6d@0fmb(Lum++o;W9xՋ2b._r(!|[9>X_ΓMCu]3x OE| A;9w>{,~`d['wl A/3eH2“A^?0fF O cOkxBG)>tm,_hLk*Ӕd^[}P#?mbcQ ,^VR.1/?Fiq*znb?uG꯭JėM=b}2;Jl+5:Zמ"54΄,2-囥CcvԍQs@*#HJ( Rwi?j-G3Hg~2q,ғ)?A2s#?ݙ&2 ub4BPfߩ<ub  QQ!zJ20Ka >f2*1Ͼ^c|{/9yY:D1o:CTYw9v헖n ֍q_6ɾU}B&mv#ѷ˪.5Eծ@| _2+~J˂1; ;ۦ7n?+։es'd ݟHQ'Ac)!jb(p@B0 ,oG, X@F:p2ҙ_`̂& Qjm'*MrrTwc_\[_:Xv *$q$cvkip֎Rk4U49MD53{a @5T->{!B}(2,'Cz2BT4ry z" #ӓX@F0 l6̩9X Ca1NG+92d۪9k iFhҀhKH-B  2)h5̻dn׃dvL <AcMB/Y @&  ~-$Sd2KɹICz2룃H/U!#;iMw.\TU/B'ZoR=qNLb_cL׏VslMfo9I+m6CF7.j}!:J d/+T<}\9o1R?-wrA2ٝ%%|RchM!ԗA1Ƀ:1ochD`Z;dz%/' #@V}GS`aӁ͠dvbIĺLs2n%v[uqvaiy{ 18ǩy=%q3LcD@F*po>ޢ]#zS2Nm}XOOB x) L5r4fLc`@ub  ´/Nic:1 d}2֧f1w^)d 4𐌼T&Ǻ[twjܞz+t-V9?؎`os3#˘<2ť+p>/޸g~׈DUzp2 rz,%5D4f ԉ Ӟ!Em++KN)mK2od8|n~ߚ(ӝ!i-3gv~4QCn3I| !2U5a;4?Gx=#1y/+ \x@V3p㷓C+N>Ĉ12d򣾌;Tžs?4K{U1`H⟘hG/=yn ]di[؂! v[RC0 m&H'9v:mv>yNo3?sd1D4PȬd'؁|w8{*0fs' 2_qmo)ddO2GzhJXd.)> d f hB|/)diRQ}xL:{=9{ys,u-)OrmFYuEu>˄FBܚV+jo"XW>1o<B!eϽG/ןp훯֯[_Nce_P0<!fpsV`e u x4{ H!cAg;F!92:#% 27)9vuXh9!SUE^mҶֳjSKA-}eK #s,o:G8Bfy$E !S~NO>UͽdADWL1'`}*'8qa˜Q8D ÓxB(薎OfJWb%d w. [r )<:w o\c#)RqZ{ypvl?Sim~2@^n.df)Ȣ2/_/^Guy F0f2.1t+G98^@ʒ/C!d>Hszex 9A!` |sBO} uFKH-s3AX۪!dlu|zWekSZL 'C'q^-d233jq߭u4 ~;Qn{~:KOj22a wO7'1!e(dE%ppgÓ8(-ØGȁ W<_"  ђ 0˛=c]Z;: aa8TTm_DLUklE#}Oh8B>ɁBƕ">;_}ʾ \xeL-Ke|b8$zЋ1&1 z0Q^ ^4!1it[[XkvBKOZ1AnK$]1PbԆ Q0}wK7>gL7߼7/ofw~hբ+W #]o #ȄkOZ7{1XZ/|" q!דe$V|8d躶s2E$h1S3*6 3U]>3DZ:-7ާ{LA\1! h4FO)nA7f+OL֒BxbX?٧^xzcDzޙ@96Yڛ2>L`!ferfԩr;a{C28\K1}b"doiLe5^| }dJ'Qm::pEQ3IQk@2Iz%VX!4LT&>1ēm*B:Jmp d2#]=Sy5r Pn2@lPgħUoWN+υ,udDTƖ-h>&B?WHʽc^ >&Dw ޤ 2^@(˭)J=vH<ُ݁O=' q]:zio<9 U]~idҥ+=}4D]mm@ S3>H@$2TeݘqEQ:[];"ܓ Ș!;Lt @̝mQ*@Mot] d,DIb2.rxscSo[wh X7RQiY󡺺ߏt6EpVUxY~2@F/}F$ǾbN4ا|qc͈(6EE0txXd:@&:@c,c_ZJ'bCi 6gJW]#巿VMc?7T siayt_+:w]L( qh٦;_uE\:?6}y`ܹ"q3r bgtcw37fn˩( '_).kGW'd2RX?J ̿[LUǟM}Qb}iKL4&&Mhr .؅R.&S"h [,CE1QSh5R 9_a3'̞=;=ٝ91$̶ Ofq$x9` >}ݗ):7-oiL2B#Ff%E9ƩJs6M 9Y?:1,d>¼@Ȥ)Y.Ø/_(8a@1C/SC .E 99*B$_c!d!dXa!(ς0flF!?B4MRh%m=B&YZ*#62Ӗw4Ls4 C2Y'&g>~K.dt Ust?8a`SL b0:1.20f)2n2NLb{|h^ك^;4JZR(zYͮ );H,-t}*P:f_j$ӱ),1gZ)y͉rl.xZd!o)@d"WlD`}iu]ח4) “u'c 50<+f}niF` oȌ FXx͖Qiŵ9vFg<2\g,h ˓,x(fN'҅'4۶g>kԵ dDW1odDUhC %'c!2^Q;: d\$d&o'4*Y2=ǪG+c L2* &B& ZJ,“ٶK&d9UmU@oG2 :O O!{Ԏ O{+>BW8( Ϩ++>Hl,d{s}Æ䈉}ӱSj3-ipZcʫw7ͯkc%?ol2* 4T0rFy싐Qϫ5relY%ygUpsRLHҤ|t/F>iHCm|2:<|~cԩ;3LA Bv @=#׉qN*~_AK;ʆ[ǔI c! k6{!TQAɘ5IK t~-Aj9*PxJIr ǒGRLTN`5ɾ5qҕoU7,߳|lx惐-alcא@k  Mܧ( d\J/3505ՙzBƭBFNOVNBџ={-ᙔCyroOi$^,-SP?7?''d !E.Kt;OH…ڛ֓IE͐$Cm%lӶلN0<\sJBFMQ!]$tolY`EBq_3 3nc ]R"a>Deq`ɘBiaKBs)+2n2@u9 zRAgcn`YE2%dڛFmXrLm?d!SxTrG1!5IA|1â9-[iusb?5G] odR ‘n2 P2ʄ 2 h]ͪJgʩW dJWȌ'TN,dϳbvpiwX̳`"%bJț>1︸Ţ0 $r!gkκ"d85?_&s|23}ITf2[?4e7?_.2@ 1Hc!{@sb/ 2jޙTfDzh! dJH`tb /++!\OW ե{+f7%!Ҥ2'!?mveDLJ}h}ץװ4$N~/Emm~m*"{ƦQ"Ul캽lrӒ2;hoѧayCmץk?IJdds|23-d4fb}w+QTf&zG>.B$D?`@fids`{_BTxzo:0UTr3 3]Lz@=tX~`e:~RBIr$#iIqXbbŅoO_W &MSp Hu$ :-:K-#)1ӽ!, ;ڄnTY_| &3+C<0fISJԧ1i!#8Cd?e%%~HfB-Kl[3 㒑S]&>UqqxʙsB$BK_OgF4L 2QK_CFG#ddґc{> y7Hr]3O{ nRe02+=vJOK djֿ;'@^!8%L~̯^%5/ZL@\t ?NAq <կQ볅LGUsbۄOS>"v2Zu_Wny()$<{>b &BBfηCֈ%`$hZgJ}@w'bK;2 BU{Ej@b"`"$ͤ1xG,^V 4۝V"X.m(RknK#->lƒ/1W1A}`<ǓLe?r̙nOgf7wkϭ[~,dɢbeQ#uY ysR8 LӓA̞ #"HLn?C8}޾v.dF "H°|zKnC{9;+$̑#dSk9,*ko|CTe+H^3,b:$b"t݄ "QJHc,S)Hc6`r0O!GBċ."DPSD~f1>ͭ,}?j?EXeݩQ^//Sҡ-k_7>̨?%!ӕ /޸;y@ϧO>AJ2Lx~pBױ<1Hc4f OҘU 'V %#Ci|z/duTKe d4ZJ>}j-Yhl Yq]1(Z Q)cH3 ֙=);}\>T$gB/13Rf!gqხe_oT!KGx~oCTҗqYBӀ t"aY;큪*!B'<3o֮I4d̆#uT7UK_.# dfII"de2r)ӱqCf(S@%ar]B^1ۚ.};ٛh{_P]>{ir+i}5ֽ) -)NN>󙏯}t82fKv*i4~߼1k4c!]~>E3ʄo3w!=a1 y!r RI4UӘ??2JQu2T7^zw|zP euz}F!g( $co{C/OgRCg{!!^t~ysoBƋ04[$dB JCh`-ғw2Fjued< !:Y$ u*21Q7!Axd|c_J_)]p(˚ G|w87-BFOD{K>IȐ k_=א Bf^ GĂ%F(F s۩%D`~JF& d@e;ғp_dNz2;J!RR)'dTzZ#dhB(V0 !B3k*닣/f0 *ev꾥K^"/Ef'x{LO=TlQCҲ{y}|ya ^~>ԑƌ1/dhuKA{A% -qH)ˤ*}/1ø} 8 QBI%r`>[9B"ғ!U@ BF/̰L7U.dh;? Y9w!#- DT40"㖠ZWשs)I0|-x_gQŲ䮭%1#1sr})$}"N!Ñ&\#M- F0K2,d%|lcQEq?Y*ЧSlPs$ ^ǷN[8=I\QQz!d r=OXy '@ey៓B7>MQ*R2cl+q=n%dbSWm#,EB$htDw2q!8fz!3j,2V3 ]ߥ|1o} ҘAr ]'D ){_w!rEHu;=K2早j!s3%B*d>-'ҚڃB\) 0i[R^6FO.:rH/;i=ivsDBfs9%GĦOL_dUeXĮclZ;ANV3;떛{cy7MŢS7KZa!5}EE/{؈[)Misný9M{o~s~?ԗ͂cۊGI_kUI(R>'ݏ= uOS!dubŷ!C" Aȯ#s̅L ύEp$UJ(4ܶ!!…XÏiKy_*!2~52W3v[kN|J/# |be dcTؗh6BHODì+!)CrBO^'&>~ppLNĈeR=\i/dtG2΅ i'M3_X2OHmmv.d͞ \9fc]Vr}3 4/&!#s9C3rh^i:0ʅy_A6^d7Rě@p@5ғyN/,dH}XX! vBgcgBF"4;S #ۥmCr&d ?NA_]$f Wb5>ҘA89GDP8w!=VLF dA q@z2O^#'bN6{C5@*t r̋Bwm:x{{mBrŤmfMj_5a!c#f&h[)~lc&~ہg _R}iO ƾ4fbׯ8G׵`Bmu$8tK(w|M2 А8ғ?UoŬF/ IV[LTZI1b!O'}-@3fw%JW"=~o~2% GL{ozM>f]>Cc刘H }o2O"8s76V GA ! } 4f2B@HǑL0ٱL4Z[α+0@^~/?:81BFuoG2b[0OVI(2ڗMRzEBVPtY1;zR]Jiq)1]7Hu;P OҘAbcI(jЪi̎t@%y֚ГoiulY2 _q(Υ*8>n2eL!]d $ qɹ*DL 1n/};g6 =ϛI㗎[N]3gkO#-BȸRȂCW!CcZ?)s-XكRi"fܐƌ 7 $`UI"!k`+y2Bh=uBEIH -DE LX0u%qK&)CrF@B"iLߩ1<} yB>'ɖ^K 1I_Ҙo@R >XdS@|~Z6Z{?NuI65KcƯ<Qjǀ:!#xZoB|Pԗ 2BFȖJ2$@=gN-wA@h )jx>:BF!` V8!FjxxֿkUy.ĎʊJ}KMz1 &m'JJtPsD $x㵻ʢL؅"AA5lr}'}>'i>| B SOvY6~NL.?#f2b7tX2"n?!cΰa%%]Us-drZ6s:X~Cp}L&m2+"fixfUʇ{ "d\Y2h7IPS d@D(CO+!(urr7ƖX*fM2"fDЄ?>d5M į;B&J2u|"c~`/@BA44,˻^rح0SG<Z9AȰBz^QZQX2͚BF0FG3ȥk 2&^i~$Ёfcsn3a %-d}BTbgeNMA;ĘAEh}{H AVx_fhf=!Ӻo_}ɗ?8 UE-}B xIɘ7WgJ:uDʈ oS6E &`q3>b̘2'R3"eD d8y}Wj`q3'.ȶi)BfxY!guO/0"L)RFL?KZȸ?C 7)-ixj /`okv#d5>\K,W1w~sJzwcOvt9qׁ;\k !iɹxi:7n30DȄz{E o:H?vA08r;h ]OWr2{CD޷Ap.M/ُS!3.&XnWĘB "d\Y26Ęz)Dq!g*0h ;2!RVAبl[ jbhd߰]K:P1f]yyw$"db},{42:EĈdj6}01SzQUrL.!cn">3`&vPӣ}ŘAA ?cyEL#d `L{bbƩ]ٿ܏_FLMͳO3U R݀G[b̀l2MI9Ri ?DʈApl2&t 0{'3B>b̎HB 'V@{hܗ1 ùGSUG 0TOfap ;d_ (#usc؞ɽË/B6^9枘! ^B͠dS{BV]Szc)bE ^hlr8DA o@HW d8 9 {-aeas\b5Ir}v#dׇ]ABȔ#dƓU0Ne ~_my27i! S_1jJMC!=*dV,776Z>/ēq!s+?F2BF$ȘJǩF&6}1;R9ᖾD l )!5 tʘ|U Ko^ؑ!z4o2BFbD蜫~5;}2zL:5}!cNx2*YL1f׏k=R2/z{ړK) d8JginJuJNO#!c}+c~=xML18Il|4kjpnB{g"ZQ FH]%څ`t"Xx*h3'w(XXΞ),liRƔ!A6tǨ{3Q>ۙ߼}r2?!Ƭt&o[ T"AJ7|^'v[ ߘR _k,d: #yH5C{+2x-bn-Bfpv%eS۝f|^yLE+O[ZͲ{"ƬhX2՞L#c̍ӭVQ!"zIYvʑz5TVYMMC+91T3L#n/0B ƓܕFՖ'$dǓy2:g~NgW1üOS2Ы99=TTL}_fLĤ?\ݩ7I#0dLt< >FO݀R^SST5N)OGȬ\QLE/~2Ę  eͳ7& M.iϒ1"#I3}%2}HdYvu?5M1 ``O!A2ēY^u!zϣ/D ;2"aLC!cXZL*d!L>9!Z4+d!CbIs RgKX<1BF $C2fq)$ܛc >p2PPxj +Z\՝%F~O}bp d +l~2"fmXet)dl|*Bf!l4Ar21VdL{{$ntQ! !OMۀ`K"B,t07)6ze⇒wd^Hro?_&IW=| k !CY oOǒfW%2yX9_$^4{oDKc駢ǕFXO{<};7j  fG2b#dŘP2'd윯~1v Eucw 0OGcV'F EJn^4n/|id+ݽ)"sTLw&_șWv -DNC1kΟ5(~;B[+m bea& WM2%]w%Z HA8ً\ɜ0VAo0#_?__9g^d"& |ckc* }o#dثix,'b0KRǨP9k{Oϭ) ƌ&M 23.$,41{^ɠ|':Bf&+6F2ɞx2Bdyjcce"e39E(q 9O2Ɂ9djSј⥟3܊2!<)'$c `$<=xam!&Y ɰXL# iVo W)~"dbOLHV$d<Ř8z?dBn˨0+ WߥĘ/d~t3_"ddE5Gsnu=iDwΣٷ1ɘn` 9OD=T

(x3$! @B322Qڣ0 !3=GPe'v9_>GJ{!Nj2]܀Y>}_"d=GikLԋ,Z1z2_ۄ@ԹWyW OHy!>|QnEw_3 s d- 4ae^eB鍮̣18g@2,v?_"ze72&y4cʘB`&@<]c]8^ DAܻv NIAdPD3TDl͢ 4wp89t(t{<Ñk|,5ߗ՟e~AJ8!#fkdYI 2LȔ"[&='ℌ d`񋀐IEJG},\*Q GSSj YēA MN~>1A1_1 d*26%BѶ yV >T XZ Vܞ+蹹>{-++tk׉q452,\0y{M)+:zB&{B&ȘO߃/@TpfD}o41֘%]ub̖2"X -)QGœtǑRYq*js6} O0PBFOD!dV35]̟nzoPA}27 {!dDLJlλS!Ê H9j B2ᅬ#z,Ę-'+d4Bf ZӪOz}ƘH{ R DPXpDXqB4{MϛMz}ݣoie5 F*>1BȰtMJ!OS؉{L 5-O{1sڀaWdW d&Bf^h E`VcFMkAM!PO2xzG#d'Dx[]Ӫǘ1+M^[d' 2}]12,&r K'gه{"c0k߸ueż(5dė?y{&=zִEȀPK!ӘQ/+0%yPyIi2|@'B goNYO/w2Z"dB`|csnqy' ƬX!#ecy:H-/!Cw?Դ^5% !;L0xBGtx!,}be!dm".54k7z/{]NbwƪQDQ^BtV [Y[N!2IBImLt (>/2ޟ́qCH2I-rg{sϹ!S/c_4KD;Һf#dHƥo B/7(E(Z:*cDb1C>!s<ΩDOn횉~M#d$L/jfo_7A`2 cN>*@|O 2/MDW-OwZ*Y\!d$S zE ˗13!j3!Lds'\Nh%dHԽ^cdN![ȎO 2qM~{d"uLq zY0.c2BHrGtq^|}s{D@#:w)}-E3 }.d2S aO<@)Qz?$bb !S.PiR;BRFr&7Q4Bb'9ZC9Bfx/d-v$dPx< s²c)O2~*b^)q͇)?㷉ʗ4EsZk D_B˒qzc+Y֬HtL:ٹڎ"5 vLP:qHd9Ü"d(L)O>(,C2|͇)>,z?,f/e,cfBJY|<;zyt"u ڏia$wC*_F3 VO,!sD_f2fOLik>LH1M /_!ŗ1B氚.%]%dZwބ ɾP !4O @B&VxiE^db(kz^4g0zv͔_&M2}Txay̩h΅/и?2 I)1C[ Ѓ's2fr˓5;d;As=u ʘg?^N"Sz<+d6 5-ek bDf]LCQBeqiXX*d"It肄 e2@UdP /$d tS~H<|Jz BxvquR&h$e$gJ1 D/rИRiF,y5 +&15_! xē,dE,vXzHƔw#jC-mFe_/q@B8% F\NH^0*V3 &`UO7 4|}b+"d2фLFg_f -2@4cs2$)tx1C?ȸ d<&f4d8Um'FM&!d2L"7mW7A1f]lJ5F~=hG1?l~/W !,O @h!c'f}7|DIg62xBƮRQQs͸; | "~T2'iWzybN ! \Ps<}b!AdzI%lR,,bхlP|W#h Xua147_IY3;s/CicLR'A;a_֖2zC?2S刎1wj?_%-ƌ 7!3ILqA&[r91Wؘd i6Ѧhv[4_P@)dG1czj?O ŔA0'(WIc1*nN 3L1M,ҿI|6̗i! rYm!ȤK_k?/yPo`cFL0'(VI`N2CړW2bz2MF\gۓWvacA)!d DC_6~ǯ˞w8l,Έ)A¢@d*Aɗ l~NǞ AFLOˋ ϯܾbeI:m̾?]YuˤLw"Lݾ?~ccBOLg/3_ CdL 2=ٰ16?Xdjep2Qym̢cϹ٥ u9pq?Xd*p:`c]h$d{2 s=g|dWŀ S\L_M>B qe jP.ICNAf"||Kh15_ m[b`"ž #D"+T$b,tubB<@f.H_'A/k˔)ȤejPFwc= i1#1!ɖd䕊 d^Yؘ;:_+S |eB HA&-20ůuKAfKsbd{S{h}db#|eVdRN6fh})r=:&~w*qEA-|ۄ#X mARFH^<`DM*H-lxAdVg=C,27?ޣ6!&=12ABDx21fL`/{CYoܘx#1fR쉑{2vp Ïx2+L.2b0<4vcvj(drٸ !1!rKM 0 29ؖO%l:>B H5mp%~FY)81B-Qr!7:E,C"!(S@#tx2@<6S'DCDuBZrd#Ƭ2!g <0A3%ڇϩ/Cu-h-dC4˿F#zeMAՠ_Dv/Rv ,}qBL2{ ! a4&퉩^!j#ԉs 2_W]!1vt31f+YP'(!W|yjOLD{{bjc?oHd2_t2A^1fg)Ƭ\Ȉ1!cF^ tBU+{bܳw6 DA z@"$ ׀@B Db,tL{ogw= {*Km1!ȸ^!o 5uA3ؘ0SWiP? C1LM9Nd {2`f72 !&,\~Xd0Ԕr4!ʄ8SOi!`ClLB/2TLǺ)͠u A&/'/v _̼0jZ}ߧMa_Η;(ȴ2|1A^.N=Kpd\Gpfc8wNM+V |B)'ȴCe y5ϙ ce "sb.{G!3Yȉƌ lhBwӫ$l 2(3T'eÐkac.9`0 sAD'cZh `bn`*WM2zbzb<d8zJ /`l-`m+;1iPbɁ {~3a7`Pd EГJ@&~,`$wKc@՘E@Sne@c a6^M2;DZ%`;COA>X'Cve^ LK("MW@cFcV3.{أJ0c1buzbBQ6;o И8j,Bc_1C^ G s\|0ʱL zbZ[cfq^2eTAcW(eИ8 7fÍczbP1՘YKWL/yp1ͮyQBcf_ex!a]άoFO4ГV;er^L>wW1 U~ d.׫X f)=g ӪW,`6ok6Oe4fK_Aǁ<ﰏ7c>d<ݜГxV=4fL+Lˀƌ}7-u#@O'dQ( /&E@P ]B*W&\@G KJ)X5S _"O!/sf8HOzg?Q?0h2|BaL4sˠB YθaRb*LƩX e01_M9L^ܡWn &4feQ@cW(cfTxN`g/@cAݡ' dZ3z2z%22*2 21+2}bF!>/Kzb ih4f0 i(/TB1C~=4fΠ'2  =jx{1zOBb2e*,p6HIOt$W мz}И}%1ˠ@W`pP֧'dWV zȴhѵ211/q=q{b"!zCl6. fz==je|Ҙ&?miz2=1@LWE ;^4ڄڢ @c+=e8a~n/@ozbC2 /8, dj ~ׇ;{w@`! ^J.gHm+bRZAHطW|!wߛ@Q˜% 2f쉁 3GF+Q)z{خ+ĘdehS^=pK|F|x2Vi0DhLK3ap&_FTdO O 2m[{Ƙ}=ܖTޘT~N1KId:$>ē؂L_C &d_zEA& 3Q,c L옟#89c/c=1Qzd'^5H 2/"¬~xO3L2b̊Ęd$(Z]iޞXhAmXۀݽ:|.~q陫 s~4\V lP[_#F' ž(WԀ34*5_zE|Yf~_oDxɘB_#+PFXKʞpx{ذ'd,kBZ$YLKvM6o9&L/#Ƭ?cD% B<;gn& K<Ɂs?>31fR⏽;Fi >F"HžB2)b›ݯzof?0@wOD]&zGGC&oѫ}e&MhA>I: 08d{'_āW }<2c.zEC&3ѠIcfW C1c;'avUVlV'&2c,"L?^!= e=0w[Ҕ=෴?nt~GҔ2ē銜۞F恌5K(zm(z1vMCeuH<PK' `E=W~yQkQ1-e$?`O ?Ā 1f >n(z{}L65p =bĘ0NrĀ d`C FCFѫAٝ30@[bĘeGӱІFd@ +4,2^ bnN?1z\Ӻc!Ę1+~ Xphr* -0ѐQj.2c{fR"vR4b̲Ę9[V#_Ct,hȌ;Fi |[AS<6.eS K:[of!7P^b̾!;W| f,`^g8qwaBLt RBy/S_@ ^Ā ~^cFq1quˀ3ezbbן8'b0?lZ[]m_f3@Yu=1`C ̻ zx{xAXr`E2bŘ:Sy/!ē +T/w+ b Ę13lēDаrt[ơWbY fv[7b8=,32XG/^n/ēA=1Wx S:1e"ϳTY+7:_@ }{bb X2 ^y"lz2YJ@22@scWĘL9ءWˀ33bt8{/c =1$œ b{DL~sش.ݽiA2\ pn( *Р@ 08,{}k0O3_9cf Ȕ@O + L;y?M2f`Ɂ1-Hnɘ5ȘS7ȓ Wc *ldd̰X挙䖌 `;XCy2p'711oi`3=Sv,ȓp'ǐÝȘq;~31y/1+1[(7x2XE+T-m*9H/Kg e=SɏcKwb7"cFLv.Y&O e_/O@ƌuyeƾ/ dwb@za<;1`3jdܗ!3_o _q ye13dJy2+tȌO*9἗1Ku_FƬ]@ӏ2%Q^A ,` LװP!gS)c7fdȘk~2{w0DA"H+ppK`rt*|3EH0x–gdE A 5f}zwtDC1cݙ dӱ0 {bP#As9čh CzK՘%څ{3cAm=1``—{uvY:0wGe@EL2!?{b1C YM2A)Pcƺ@~eB{bԓ0Pc@~>} ?D@@x5f5fθW Pc&#!z2˝P"QG665*ܗw 5fdfԓ2mԘU>Nj~32]J N '@&e.3@~ٻ( M(@ءi@oZkkAb'IČ_ēD:cv޿Xr`1f2IDYZœ@ MĘ=2Ό:3/C%b d%6| י`f/bhcM7=1`cĘ_'>QZ$,Pˀ3ޗo'n_'@˴rZnb28c%~M?V(^ia2ƘĘ% dOr z9)kĘm1fn" ,9` _ēD1f0Kp=2t'ckT@F OƍGi *+4B۩] )\@*A MW`g\ۀ |8 8S9[9/jlˠ vbd1nWٗɳV:$-2 3m5f`_feGjp Pn#I Sl7֘=Ԩ1ScCdPO-Pc75f hԘҾrd^A,7fԘ}l^Z\0V]9 T:z5fK1K@gm 2 d˰t d`g@;135fT1KQZ}3Ok2p1Qc&N 1Ha_fZW f*BQc32N j'{w0F|!Cs/R >N7gv[o;$'9Z1[| @=Ψ^Lʵ*5fݨ . 32ԓR_f_ a$I@}&epz2ˀ 3_gPa_F@wbRQ}XBYޯ5e@@;1ԘQƬnKR}ϧ;2L @;11lؗi>LPԘQl9dPOj̰/c_vVcm$/#@=Ψ^L&J5f՘} ]y}QO22T;Ԙ5O1L 5f g~/#bQ(.Zك;!eH@]E[ğ98xgwb ./j^,f fZ?gPa_F@;1`_5fO1K@Yؗ5f1JYݖ#} z2л*5f d*՘3/#g.\=ؗ5fH 4jΌ~/#`r'&ˀ,L22TٗU `r_FjT1K@̾ 1؉Ԙ[Qe_&gҾp^ڗ5f_3/S_=+Ԙ3Ԙfԓ5f fFؗԾ35fO%b pB Ex {=`QC_QfaB {̺/#@=Pm_֩1eMj@F=ؗ~1TcL3PcL2؉@&Y@vePcVƬ)*"i>̾ U /ƌjzA@}ʾ 1Kjga_!}Wc&Hj'Pc}2%7O/ט5fRc6@@=@,}Z2=՘ƌ3d} z2o2NY]w,nRce2'0l721ScV2NQcM5fdhZc&|'&g4aV) v%c"LH(d@R]A /2fHIuؗI̾ 2f2vbڗ1CƬR2eae_2f2vbd̸}ZE3?òþ2f98L@ N $_vڗq'͘03}}.ldcF jedF/ɘzy2]Y3s`_A~p#Oؗ.dL4S9ؗaN3?މAf/2f$@}AN 辌2fvr/q3@]A Of'~ٻc( Û d/bgҸ @Y`@ EbHxwy_x?Ș;˴uٝV9|u}3*drq'1þ}qݟ_22f@e󾌃 <b,џD}*e1#cmed~#<e1+c|iS!cj8ď|YؗAL,c&Y2eݜ1s؉/Cyy0S%cVန}h1s/#cFrqDrȾL2fӽ}h1sȓى1þ}N[8+ȘQ!c㌃ Oeč16/edy23f9̌_&*TʘecF >ۻ@ %`/v9:p8vd"QO۾ j1}Ym 5fȷ/sP"ꋜYIENDB`vagrant-1.4.3/website/docs/source/index.html.erb000066400000000000000000000000001226132634600216120ustar00rootroot00000000000000vagrant-1.4.3/website/docs/source/javascripts/000077500000000000000000000000001226132634600214115ustar00rootroot00000000000000vagrant-1.4.3/website/docs/source/javascripts/backstretch.js000066400000000000000000000077151226132634600242560ustar00rootroot00000000000000/*! Backstretch - v2.0.1 - 2012-10-01 * http://srobbin.com/jquery-plugins/backstretch/ * Copyright (c) 2012 Scott Robbin; Licensed MIT */ (function(e,t,n){"use strict";e.fn.backstretch=function(r,s){return(r===n||r.length===0)&&e.error("No images were supplied for Backstretch"),e(t).scrollTop()===0&&t.scrollTo(0,0),this.each(function(){var t=e(this),n=t.data("backstretch");n&&(s=e.extend(n.options,s),n.destroy(!0)),n=new i(this,r,s),t.data("backstretch",n)})},e.backstretch=function(t,n){return e("body").backstretch(t,n).data("backstretch")},e.expr[":"].backstretch=function(t){return e(t).data("backstretch")!==n},e.fn.backstretch.defaults={centeredX:!0,centeredY:!0,duration:5e3,fade:0};var r={wrap:{left:0,top:0,overflow:"hidden",margin:0,padding:0,height:"100%",width:"100%",zIndex:-999999},img:{position:"absolute",display:"none",margin:0,padding:0,border:"none",width:"auto",height:"auto",maxWidth:"none",zIndex:-999999}},i=function(n,i,o){this.options=e.extend({},e.fn.backstretch.defaults,o||{}),this.images=e.isArray(i)?i:[i],e.each(this.images,function(){e("")[0].src=this}),this.isBody=n===document.body,this.$container=e(n),this.$wrap=e('

').css(r.wrap).appendTo(this.$container),this.$root=this.isBody?s?e(t):e(document):this.$container;if(!this.isBody){var u=this.$container.css("position"),a=this.$container.css("zIndex");this.$container.css({position:u==="static"?"relative":u,zIndex:a==="auto"?0:a,background:"none"}),this.$wrap.css({zIndex:-999998})}this.$wrap.css({position:this.isBody&&s?"fixed":"absolute"}),this.index=0,this.show(this.index),e(t).on("resize.backstretch",e.proxy(this.resize,this)).on("orientationchange.backstretch",e.proxy(function(){this.isBody&&t.pageYOffset===0&&(t.scrollTo(0,1),this.resize())},this))};i.prototype={resize:function(){try{var e={left:0,top:0},n=this.isBody?this.$root.width():this.$root.innerWidth(),r=n,i=this.isBody?t.innerHeight?t.innerHeight:this.$root.height():this.$root.innerHeight(),s=r/this.$img.data("ratio"),o;s>=i?(o=(s-i)/2,this.options.centeredY&&(e.top="-"+o+"px")):(s=i,r=s*this.$img.data("ratio"),o=(r-n)/2,this.options.centeredX&&(e.left="-"+o+"px")),this.$wrap.css({width:n,height:i}).find("img:not(.deleteable)").css({width:r,height:s}).css(e)}catch(u){}return this},show:function(t){if(Math.abs(t)>this.images.length-1)return;this.index=t;var n=this,i=n.$wrap.find("img").addClass("deleteable"),s=e.Event("backstretch.show",{relatedTarget:n.$container[0]});return clearInterval(n.interval),n.$img=e("").css(r.img).bind("load",function(t){var r=this.width||e(t.target).width(),o=this.height||e(t.target).height();e(this).data("ratio",r/o),n.resize(),e(this).fadeIn(n.options.speed||n.options.fade,function(){i.remove(),n.paused||n.cycle(),n.$container.trigger(s)})}).appendTo(n.$wrap),n.$img.attr("src",n.images[t]),n},next:function(){return this.show(this.index1&&(clearInterval(this.interval),this.interval=setInterval(e.proxy(function(){this.paused||this.next()},this),this.options.duration)),this},destroy:function(n){e(t).off("resize.backstretch orientationchange.backstretch"),clearInterval(this.interval),n||this.$wrap.remove(),this.$container.removeData("backstretch")}};var s=function(){var e=navigator.userAgent,n=navigator.platform,r=e.match(/AppleWebKit\/([0-9]+)/),i=!!r&&r[1],s=e.match(/Fennec\/([0-9]+)/),o=!!s&&s[1],u=e.match(/Opera Mobi\/([0-9]+)/),a=!!u&&u[1],f=e.match(/MSIE ([0-9]+)/),l=!!f&&f[1];return!((n.indexOf("iPhone")>-1||n.indexOf("iPad")>-1||n.indexOf("iPod")>-1)&&i&&i<534||t.operamini&&{}.toString.call(t.operamini)==="[object OperaMini]"||u&&a<7458||e.indexOf("Android")>-1&&i&&i<533||o&&o<6||"palmGetResource"in t&&i&&i<534||e.indexOf("MeeGo")>-1&&e.indexOf("NokiaBrowser/8.5.0")>-1||l&&l<=6)}()})(jQuery,window);vagrant-1.4.3/website/docs/source/javascripts/bootstrap.min.js000077500000000000000000000624411226132634600245600ustar00rootroot00000000000000/** * Bootstrap.js by @fat & @mdo * plugins: bootstrap-transition.js, bootstrap-modal.js, bootstrap-dropdown.js, bootstrap-scrollspy.js, bootstrap-tab.js, bootstrap-tooltip.js, bootstrap-popover.js, bootstrap-affix.js, bootstrap-alert.js, bootstrap-button.js, bootstrap-collapse.js, bootstrap-carousel.js, bootstrap-typeahead.js * Copyright 2012 Twitter, Inc. * http://www.apache.org/licenses/LICENSE-2.0.txt */ !function(a){a(function(){a.support.transition=function(){var a=function(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"},c;for(c in b)if(a.style[c]!==undefined)return b[c]}();return a&&{end:a}}()})}(window.jQuery),!function(a){var b=function(b,c){this.options=c,this.$element=a(b).delegate('[data-dismiss="modal"]',"click.dismiss.modal",a.proxy(this.hide,this)),this.options.remote&&this.$element.find(".modal-body").load(this.options.remote)};b.prototype={constructor:b,toggle:function(){return this[this.isShown?"hide":"show"]()},show:function(){var b=this,c=a.Event("show");this.$element.trigger(c);if(this.isShown||c.isDefaultPrevented())return;this.isShown=!0,this.escape(),this.backdrop(function(){var c=a.support.transition&&b.$element.hasClass("fade");b.$element.parent().length||b.$element.appendTo(document.body),b.$element.show(),c&&b.$element[0].offsetWidth,b.$element.addClass("in").attr("aria-hidden",!1),b.enforceFocus(),c?b.$element.one(a.support.transition.end,function(){b.$element.focus().trigger("shown")}):b.$element.focus().trigger("shown")})},hide:function(b){b&&b.preventDefault();var c=this;b=a.Event("hide"),this.$element.trigger(b);if(!this.isShown||b.isDefaultPrevented())return;this.isShown=!1,this.escape(),a(document).off("focusin.modal"),this.$element.removeClass("in").attr("aria-hidden",!0),a.support.transition&&this.$element.hasClass("fade")?this.hideWithTransition():this.hideModal()},enforceFocus:function(){var b=this;a(document).on("focusin.modal",function(a){b.$element[0]!==a.target&&!b.$element.has(a.target).length&&b.$element.focus()})},escape:function(){var a=this;this.isShown&&this.options.keyboard?this.$element.on("keyup.dismiss.modal",function(b){b.which==27&&a.hide()}):this.isShown||this.$element.off("keyup.dismiss.modal")},hideWithTransition:function(){var b=this,c=setTimeout(function(){b.$element.off(a.support.transition.end),b.hideModal()},500);this.$element.one(a.support.transition.end,function(){clearTimeout(c),b.hideModal()})},hideModal:function(a){this.$element.hide().trigger("hidden"),this.backdrop()},removeBackdrop:function(){this.$backdrop.remove(),this.$backdrop=null},backdrop:function(b){var c=this,d=this.$element.hasClass("fade")?"fade":"";if(this.isShown&&this.options.backdrop){var e=a.support.transition&&d;this.$backdrop=a('