From 4207796c02f8efeb34a0f12ea9695c6a37552313 Mon Sep 17 00:00:00 2001 From: "R.I.Pienaar" Date: Sat, 3 Nov 2018 13:43:51 +0100 Subject: [PATCH] (#1) create a minimal gem --- .circleci/config.yml | 15 + .gitignore | 6 +- .travis.yml | 19 - CODE_OF_CONDUCT.md | 74 + CONTRIBUTING.md | 48 +- Gemfile | 47 +- COPYING => LICENSE | 2 +- MAINTAINERS | 27 - NOTICE | 6 + README.md | 110 +- Rakefile | 152 +- acceptance/.gitignore | 3 - acceptance/Gemfile | 29 - acceptance/README.md | 112 - acceptance/Rakefile | 407 -- acceptance/config/aio/options.rb | 18 - acceptance/config/nodes/centos-5-x86_64.yaml | 20 - acceptance/config/nodes/centos-6-x86_64.yaml | 20 - acceptance/config/nodes/debian-6-x86_64.yaml | 20 - acceptance/config/nodes/debian-7-x86_64.yaml | 20 - acceptance/config/nodes/debian-8-x86_64.yaml | 20 - acceptance/config/nodes/fedora-20-x86_64.yaml | 20 - acceptance/config/nodes/fedora-21-x86_64.yaml | 20 - acceptance/config/nodes/redhat-5-x86_64.yaml | 20 - acceptance/config/nodes/redhat-6-x86_64.yaml | 20 - acceptance/config/nodes/redhat-7-x86_64.yaml | 20 - .../config/nodes/ubuntu-1204-x86_64.yaml | 20 - .../config/nodes/ubuntu-1404-x86_64.yaml | 20 - .../config/nodes/ubuntu-1410-x86_64.yaml | 20 - .../config/nodes/win2003r2x64-rubyx64.yaml | 21 - .../config/nodes/win2003r2x64-rubyx86.yaml | 21 - .../config/nodes/win2003r2x86-rubyx86.yaml | 21 - .../config/nodes/win2003x64-rubyx64.yaml | 21 - .../config/nodes/win2003x64-rubyx86.yaml | 21 - .../config/nodes/win2003x86-rubyx86.yaml | 21 - acceptance/config/nodes/win2008-rubyx64.yaml | 21 - acceptance/config/nodes/win2008-rubyx86.yaml | 21 - .../config/nodes/win2008r2-rubyx64.yaml | 21 - .../config/nodes/win2008r2-rubyx86.yaml | 21 - acceptance/config/nodes/win2012-rubyx64.yaml | 21 - acceptance/config/nodes/win2012-rubyx86.yaml | 21 - .../config/nodes/win2012r2-rubyx64.yaml | 21 - .../config/nodes/win2012r2-rubyx86.yaml | 21 - acceptance/files/activemq.keystore | Bin 3914 -> 0 bytes acceptance/files/activemq.truststore | Bin 1459 -> 0 bytes acceptance/files/activemq.xml | 182 - acceptance/files/ca_crt.pem | 32 - acceptance/files/client.cfg | 24 - acceptance/files/client.crt | 32 - acceptance/files/client.key | 51 - acceptance/files/server.crt | 32 - acceptance/files/server.key | 51 - acceptance/files/windows-client.cfg | 24 - acceptance/lib/acceptance_spec_helper.rb | 8 - acceptance/lib/helper.rb | 1 - .../lib/puppet/acceptance/common_utils.rb | 144 - acceptance/lib/puppet/acceptance/git_utils.rb | 19 - .../lib/puppet/acceptance/install_utils.rb | 226 - acceptance/setup/aio/pre-suite/010_Install.rb | 79 - .../aio/pre-suite/015_PackageHostsPresets.rb | 1 - .../045_EnsureMasterStartedOnPassenger.rb | 3 - .../aio/pre-suite/050_Install-activemq.rb | 122 - .../060_Install-mcollective-daemon.rb | 78 - .../070_Install_puppet-agent-plugin.rb | 69 - .../common/pre-suite/025_StopFirewall.rb | 9 - .../common/pre-suite/040_ValidateSignCert.rb | 6 - .../common/pre-suite/070_InstallCACerts.rb | 93 - .../pre-suite/110_SetPEPuppetService.rb | 1 - acceptance/ssl/ca/ca_crl.pem | 16 - acceptance/ssl/ca/ca_crt.pem | 32 - acceptance/ssl/ca/ca_key.pem | 51 - acceptance/ssl/ca/ca_pub.pem | 14 - acceptance/ssl/ca/inventory.txt | 5 - acceptance/ssl/ca/private/ca.pass | 1 - acceptance/ssl/ca/serial | 1 - acceptance/ssl/ca/signed/activemq.pem | 31 - .../ssl/ca/signed/mcollective-client.pem | 32 - .../ssl/ca/signed/mcollective-server.pem | 32 - acceptance/ssl/ca/signed/socks.local.pem | 32 - acceptance/ssl/certs/activemq.pem | 31 - acceptance/ssl/certs/ca.pem | 32 - acceptance/ssl/certs/mcollective-client.pem | 32 - acceptance/ssl/certs/mcollective-server.pem | 32 - acceptance/ssl/certs/socks.local.pem | 32 - acceptance/ssl/crl.pem | 16 - acceptance/ssl/private_keys/activemq.pem | 51 - .../ssl/private_keys/mcollective-client.pem | 51 - .../ssl/private_keys/mcollective-server.pem | 51 - acceptance/ssl/private_keys/socks.local.pem | 51 - acceptance/ssl/public_keys/activemq.pem | 14 - .../ssl/public_keys/mcollective-client.pem | 14 - .../ssl/public_keys/mcollective-server.pem | 14 - acceptance/ssl/public_keys/socks.local.pem | 14 - acceptance/tests/mco_ping.rb | 12 - acceptance/tests/mco_puppet_batch_count.rb | 12 - acceptance/tests/mco_puppet_count.rb | 12 - acceptance/tests/mco_puppet_exec.rb | 93 - acceptance/tests/mco_puppet_powershell.rb | 99 - acceptance/tests/mco_puppet_runonce.rb | 88 - acceptance/tests/mco_reconnect.rb | 36 - bin/mcollectived | 80 - ext/Makefile | 43 - ext/action_helpers/perl/.gitignore | 3 - ext/action_helpers/perl/Makefile.PL | 9 - .../perl/lib/MCollective/Action.pm | 158 - ext/action_helpers/perl/t/basic.t | 30 - ext/action_helpers/php/README.markdown | 38 - ext/action_helpers/php/mcollective_action.php | 65 - .../python/kwilczynski/README.markdown | 46 - ext/action_helpers/python/kwilczynski/echo.py | 16 - .../python/kwilczynski/mcollective_action.py | 112 - .../python/romke/README.markdown | 38 - .../python/romke/mcollectiveah.py | 69 - ext/action_helpers/python/romke/test.py | 50 - ext/activemq/apache-activemq.spec | 206 - ext/activemq/examples/multi-broker/README | 12 - .../multi-broker/broker1-activemq.xml | 241 - .../multi-broker/broker2-activemq.xml | 175 - .../multi-broker/broker3-activemq.xml | 175 - ext/activemq/examples/single-broker/README | 5 - .../examples/single-broker/activemq.xml | 137 - ext/activemq/wlcg-patch.tgz | Bin 9890 -> 0 bytes ext/aio/README.md | 5 - ext/aio/common/client.cfg.dist | 24 - ext/aio/common/server.cfg.dist | 27 - ext/aio/debian/mcollective.default | 2 - ext/aio/debian/mcollective.init | 92 - ext/aio/osx/mcollective.plist | 30 - ext/aio/redhat/mcollective-systemd.logrotate | 8 - ext/aio/redhat/mcollective-sysv.logrotate | 8 - ext/aio/redhat/mcollective.init | 142 - ext/aio/redhat/mcollective.service | 25 - ext/aio/redhat/mcollective.sysconfig | 10 - ext/aio/solaris/smf/mcollective.xml | 44 - ext/aio/suse/mcollective.init | 121 - ext/bash/mco_completion.sh | 57 - ext/build_defaults.yaml | 21 - ext/debian/changelog.erb | 5 - ext/debian/compat | 1 - ext/debian/control | 42 - ext/debian/copyright | 29 - ext/debian/mcollective-client.install | 2 - ext/debian/mcollective-common.install | 2 - ext/debian/mcollective-doc.install | 1 - ext/debian/mcollective.dirs | 2 - ext/debian/mcollective.init | 92 - ext/debian/mcollective.install | 3 - ext/debian/patches/pluginsdir.patch | 24 - ext/debian/patches/series | 1 - ext/debian/rules | 15 - ext/debian/source/format | 1 - ext/help-templates/README | 1 - ext/help-templates/rpc-help-markdown.erb | 49 - ext/mc-irb | 252 - ext/mc-rpc-restserver.rb | 34 - ext/openbsd/README | 4 - ext/openbsd/port-files/mcollective/Makefile | 28 - ext/openbsd/port-files/mcollective/distinfo | 5 - .../patches/patch-etc_server_cfg_dist | 9 - .../mcollective/patches/patch-ext_Makefile | 67 - ext/openbsd/port-files/mcollective/pkg/DESCR | 2 - .../port-files/mcollective/pkg/MESSAGE | 3 - ext/openbsd/port-files/mcollective/pkg/PLIST | 432 -- .../mcollective/pkg/mcollectived.rc | 10 - ext/osx/README | 15 - ext/osx/bldmacpkg | 146 - ext/packaging.rake | 36 - ext/perl/mc-find-hosts.pl | 80 - ext/project_data.yaml | 41 - ext/redhat/mcollective.init | 139 - ext/redhat/mcollective.service | 25 - ext/redhat/mcollective.spec.erb | 173 - ext/solaris/README | 33 - ext/solaris/build | 68 - ext/solaris/cswmcollectived.xml | 100 - ext/solaris/depend | 3 - ext/solaris/mcollective.init | 96 - ext/solaris/pkginfo | 6 - ext/solaris/postinstall | 6 - ext/solaris/postremove | 3 - ext/solaris/preremove | 2 - ext/solaris/prototype.head | 4 - ext/solaris/setversion | 9 - ext/solaris11/Makefile | 46 - ext/solaris11/README.md | 103 - ext/stompclient | 156 - ext/vim/_.snippets | 10 - ext/vim/mcollective_ddl.snippets | 89 - ext/windows/README.md | 33 - ext/windows/daemon.bat | 6 - ext/windows/environment.bat | 16 - ext/windows/mco.bat | 9 - ext/windows/register_service.bat | 7 - ext/windows/service_manager.rb | 101 - ext/windows/unregister_service.bat | 7 - ext/zsh/_mco | 94 - install.rb | 326 -- lib/mcollective.rb | 6 +- lib/mcollective/agent/discovery.rb | 37 - lib/mcollective/agent/rpcutil.ddl | 220 - lib/mcollective/agent/rpcutil.rb | 108 - lib/mcollective/audit/logfile.rb | 26 - lib/mcollective/config.rb | 8 +- lib/mcollective/connector/activemq.ddl | 9 - lib/mcollective/connector/activemq.rb | 609 --- lib/mcollective/connector/rabbitmq.ddl | 9 - lib/mcollective/connector/rabbitmq.rb | 517 --- lib/mcollective/registration.rb | 16 - lib/mcollective/registration/agentlist.rb | 10 - lib/mcollective/runner.rb | 311 -- lib/mcollective/security/aes_security.rb | 398 -- lib/mcollective/security/psk.rb | 117 - lib/mcollective/security/ssl.rb | 332 -- lib/mcollective/unix_daemon.rb | 70 - lib/mcollective/vendor.rb | 41 - lib/mcollective/vendor/load_systemu.rb | 1 - lib/mcollective/vendor/require_vendored.rb | 1 - lib/mcollective/vendor/systemu/BSDL | 23 - lib/mcollective/vendor/systemu/LICENSE | 57 - lib/mcollective/vendor/systemu/README | 170 - lib/mcollective/vendor/systemu/README.erb | 37 - lib/mcollective/vendor/systemu/Rakefile | 394 -- lib/mcollective/vendor/systemu/lib/systemu.rb | 379 -- lib/mcollective/vendor/systemu/samples/a.rb | 11 - lib/mcollective/vendor/systemu/samples/b.rb | 12 - lib/mcollective/vendor/systemu/samples/c.rb | 10 - lib/mcollective/vendor/systemu/samples/d.rb | 11 - lib/mcollective/vendor/systemu/samples/e.rb | 9 - lib/mcollective/vendor/systemu/samples/f.rb | 18 - .../vendor/systemu/systemu.gemspec | 45 - .../vendor/systemu/test/systemu_test.rb | 78 - .../vendor/systemu/test/testing.rb | 195 - lib/mcollective/windows_daemon.rb | 45 - mcollective.init | 122 - spec/unit/mcollective/agent/rpcutil_spec.rb | 176 - spec/unit/mcollective/audit/logfile_spec.rb | 44 - .../mcollective/connector/activemq_spec.rb | 833 ---- .../mcollective/connector/rabbitmq_spec.rb | 867 ---- spec/unit/mcollective/monkey_patches_spec.rb | 6 +- .../mcollective/registration/base_spec.rb | 123 - spec/unit/mcollective/runner_spec.rb | 360 -- .../mcollective/security/aes_security_spec.rb | 211 - spec/unit/mcollective/security/psk_spec.rb | 161 - spec/unit/mcollective/security/ssl_spec.rb | 90 - spec/unit/mcollective/unix_daemon_spec.rb | 85 - spec/unit/mcollective/vendor_spec.rb | 34 - spec/unit/mcollective/windows_daemon_spec.rb | 58 - website/_includes/main_menu.html | 19 - website/blueprint/ie.css | 35 - .../blueprint/plugins/buttons/icons/cross.png | Bin 655 -> 0 bytes .../blueprint/plugins/buttons/icons/key.png | Bin 455 -> 0 bytes .../blueprint/plugins/buttons/icons/tick.png | Bin 537 -> 0 bytes website/blueprint/plugins/buttons/readme.txt | 32 - website/blueprint/plugins/buttons/screen.css | 97 - .../blueprint/plugins/fancy-type/readme.txt | 14 - .../blueprint/plugins/fancy-type/screen.css | 71 - .../plugins/link-icons/icons/doc.png | Bin 777 -> 0 bytes .../plugins/link-icons/icons/email.png | Bin 641 -> 0 bytes .../plugins/link-icons/icons/external.png | Bin 46848 -> 0 bytes .../plugins/link-icons/icons/feed.png | Bin 691 -> 0 bytes .../blueprint/plugins/link-icons/icons/im.png | Bin 741 -> 0 bytes .../plugins/link-icons/icons/pdf.png | Bin 591 -> 0 bytes .../plugins/link-icons/icons/visited.png | Bin 46990 -> 0 bytes .../plugins/link-icons/icons/xls.png | Bin 663 -> 0 bytes .../blueprint/plugins/link-icons/readme.txt | 18 - .../blueprint/plugins/link-icons/screen.css | 40 - website/blueprint/plugins/rtl/readme.txt | 10 - website/blueprint/plugins/rtl/screen.css | 110 - website/blueprint/print.css | 29 - website/blueprint/screen.css | 258 -- website/blueprint/src/forms.css | 65 - website/blueprint/src/grid.css | 280 -- website/blueprint/src/grid.png | Bin 195 -> 0 bytes website/blueprint/src/ie.css | 76 - website/blueprint/src/print.css | 85 - website/blueprint/src/reset.css | 45 - website/blueprint/src/typography.css | 106 - website/changelog.md | 882 ---- website/configure/client.md | 611 --- website/configure/server.md | 800 ---- website/deploy/demo.md | 26 - website/deploy/index.md | 87 - website/deploy/install.md | 190 - website/deploy/middleware/activemq.md | 700 --- .../deploy/middleware/activemq_keystores.md | 273 -- website/deploy/middleware/index.md | 76 - website/deploy/plugins.md | 369 -- website/deploy/standard.md | 544 --- website/ec2demo.md | 11 - website/images/activemq-multi-locations.png | Bin 77356 -> 0 bytes website/images/client.gif | Bin 92800 -> 0 bytes website/images/deployment.gif | Bin 64360 -> 0 bytes website/images/mcollective-aaa.png | Bin 69828 -> 0 bytes website/images/mcollective/li.png | Bin 149 -> 0 bytes website/images/message-flow-diagram.png | Bin 91289 -> 0 bytes website/images/middleware-magic.gif | Bin 76782 -> 0 bytes website/images/middleware.gif | Bin 87874 -> 0 bytes website/images/server.gif | Bin 83946 -> 0 bytes website/images/subcollectives-collectives.png | Bin 99788 -> 0 bytes website/images/subcollectives-impact.png | Bin 57834 -> 0 bytes .../subcollectives-multiple-middleware.png | Bin 48589 -> 0 bytes website/index.md | 94 - website/overview_components.md | 153 - .../agent_file_manager.markdown | 8 - .../agent_iptables_junk_filter.markdown | 7 - .../plugin_directory/agent_metadata.markdown | 8 - .../agent_registration_mongodb.markdown | 87 - .../agent_registration_monitor.markdown | 34 - website/plugin_directory/apt.markdown | 66 - .../authorization_action_policy.markdown | 7 - .../plugin_directory/central_rpc_log.markdown | 88 - .../discovery_assisted_ssh.markdown | 22 - website/plugin_directory/facter.markdown | 7 - .../plugin_directory/facter_via_yaml.markdown | 95 - website/plugin_directory/index.markdown | 68 - .../logstash_rpc_audit_logs.markdown | 8 - website/plugin_directory/net_test.markdown | 7 - website/plugin_directory/none.markdown | 23 - website/plugin_directory/nrpe_agent.markdown | 7 - website/plugin_directory/ohai.markdown | 52 - website/plugin_directory/package.markdown | 7 - website/plugin_directory/packages.markdown | 63 - .../process_management.markdown | 102 - .../plugin_directory/puppet_agent.markdown | 9 - website/plugin_directory/puppet_ca.markdown | 94 - .../puppet_commander.markdown | 50 - .../registration_metadata.markdown | 30 - .../resources_data_plugin.markdown | 7 - website/plugin_directory/services.markdown | 7 - .../plugin_directory/spamassassin.markdown | 105 - website/plugin_directory/stomp_util.markdown | 79 - website/plugin_directory/sysctl_data.markdown | 21 - .../reference/basic/basic_agent_and_client.md | 262 -- website/reference/basic/basic_cli_usage.md | 531 --- website/reference/basic/configuration.md | 136 - website/reference/basic/daemon.md | 158 - website/reference/basic/gettingstarted.md | 35 - .../reference/basic/gettingstarted_debian.md | 259 -- .../reference/basic/gettingstarted_redhat.md | 255 - website/reference/basic/messageflow.md | 28 - website/reference/basic/messageformat.md | 160 - website/reference/basic/subcollectives.md | 201 - website/reference/development/ec2_demo.md | 104 - website/reference/development/releasetasks.md | 47 - website/reference/index.md | 55 - .../integration/activemq_clusters.md | 56 - .../integration/activemq_security.md | 38 - website/reference/integration/activemq_ssl.md | 224 - website/reference/integration/puppet.md | 74 - website/reference/plugins/aggregate.md | 367 -- website/reference/plugins/application.md | 308 -- .../reference/plugins/connector_activemq.md | 319 -- .../reference/plugins/connector_rabbitmq.md | 344 -- website/reference/plugins/connector_stomp.md | 90 - website/reference/plugins/data.md | 271 -- website/reference/plugins/ddl.md | 274 -- website/reference/plugins/discovery.md | 158 - website/reference/plugins/facts.md | 82 - website/reference/plugins/registration.md | 68 - website/reference/plugins/rpcutil.md | 116 - website/reference/plugins/security_aes.md | 283 -- website/reference/plugins/security_ssl.md | 125 - website/reference/plugins/validator.md | 127 - website/reference/ui/filters.md | 92 - website/reference/ui/nodereports.md | 173 - website/releasenotes.md | 4119 ----------------- website/screencasts.md | 108 - website/security.md | 192 - website/simplerpc/agents.md | 521 --- website/simplerpc/auditing.md | 69 - website/simplerpc/authorization.md | 61 - website/simplerpc/clients.md | 572 --- website/simplerpc/index.md | 102 - website/simplerpc/messageformat.md | 92 - website/terminology.md | 101 - 375 files changed, 192 insertions(+), 35827 deletions(-) create mode 100644 .circleci/config.yml delete mode 100644 .travis.yml create mode 100644 CODE_OF_CONDUCT.md rename COPYING => LICENSE (99%) delete mode 100644 MAINTAINERS create mode 100644 NOTICE delete mode 100644 acceptance/.gitignore delete mode 100644 acceptance/Gemfile delete mode 100644 acceptance/README.md delete mode 100644 acceptance/Rakefile delete mode 100644 acceptance/config/aio/options.rb delete mode 100644 acceptance/config/nodes/centos-5-x86_64.yaml delete mode 100644 acceptance/config/nodes/centos-6-x86_64.yaml delete mode 100644 acceptance/config/nodes/debian-6-x86_64.yaml delete mode 100644 acceptance/config/nodes/debian-7-x86_64.yaml delete mode 100644 acceptance/config/nodes/debian-8-x86_64.yaml delete mode 100644 acceptance/config/nodes/fedora-20-x86_64.yaml delete mode 100644 acceptance/config/nodes/fedora-21-x86_64.yaml delete mode 100644 acceptance/config/nodes/redhat-5-x86_64.yaml delete mode 100644 acceptance/config/nodes/redhat-6-x86_64.yaml delete mode 100644 acceptance/config/nodes/redhat-7-x86_64.yaml delete mode 100644 acceptance/config/nodes/ubuntu-1204-x86_64.yaml delete mode 100644 acceptance/config/nodes/ubuntu-1404-x86_64.yaml delete mode 100644 acceptance/config/nodes/ubuntu-1410-x86_64.yaml delete mode 100644 acceptance/config/nodes/win2003r2x64-rubyx64.yaml delete mode 100644 acceptance/config/nodes/win2003r2x64-rubyx86.yaml delete mode 100644 acceptance/config/nodes/win2003r2x86-rubyx86.yaml delete mode 100644 acceptance/config/nodes/win2003x64-rubyx64.yaml delete mode 100644 acceptance/config/nodes/win2003x64-rubyx86.yaml delete mode 100644 acceptance/config/nodes/win2003x86-rubyx86.yaml delete mode 100644 acceptance/config/nodes/win2008-rubyx64.yaml delete mode 100644 acceptance/config/nodes/win2008-rubyx86.yaml delete mode 100644 acceptance/config/nodes/win2008r2-rubyx64.yaml delete mode 100644 acceptance/config/nodes/win2008r2-rubyx86.yaml delete mode 100644 acceptance/config/nodes/win2012-rubyx64.yaml delete mode 100644 acceptance/config/nodes/win2012-rubyx86.yaml delete mode 100644 acceptance/config/nodes/win2012r2-rubyx64.yaml delete mode 100644 acceptance/config/nodes/win2012r2-rubyx86.yaml delete mode 100644 acceptance/files/activemq.keystore delete mode 100644 acceptance/files/activemq.truststore delete mode 100644 acceptance/files/activemq.xml delete mode 100644 acceptance/files/ca_crt.pem delete mode 100644 acceptance/files/client.cfg delete mode 100644 acceptance/files/client.crt delete mode 100644 acceptance/files/client.key delete mode 100644 acceptance/files/server.crt delete mode 100644 acceptance/files/server.key delete mode 100644 acceptance/files/windows-client.cfg delete mode 100644 acceptance/lib/acceptance_spec_helper.rb delete mode 100644 acceptance/lib/helper.rb delete mode 100644 acceptance/lib/puppet/acceptance/common_utils.rb delete mode 100644 acceptance/lib/puppet/acceptance/git_utils.rb delete mode 100644 acceptance/lib/puppet/acceptance/install_utils.rb delete mode 100644 acceptance/setup/aio/pre-suite/010_Install.rb delete mode 100644 acceptance/setup/aio/pre-suite/015_PackageHostsPresets.rb delete mode 100644 acceptance/setup/aio/pre-suite/045_EnsureMasterStartedOnPassenger.rb delete mode 100644 acceptance/setup/aio/pre-suite/050_Install-activemq.rb delete mode 100644 acceptance/setup/aio/pre-suite/060_Install-mcollective-daemon.rb delete mode 100644 acceptance/setup/aio/pre-suite/070_Install_puppet-agent-plugin.rb delete mode 100644 acceptance/setup/common/pre-suite/025_StopFirewall.rb delete mode 100644 acceptance/setup/common/pre-suite/040_ValidateSignCert.rb delete mode 100644 acceptance/setup/common/pre-suite/070_InstallCACerts.rb delete mode 100644 acceptance/setup/common/pre-suite/110_SetPEPuppetService.rb delete mode 100644 acceptance/ssl/ca/ca_crl.pem delete mode 100644 acceptance/ssl/ca/ca_crt.pem delete mode 100644 acceptance/ssl/ca/ca_key.pem delete mode 100644 acceptance/ssl/ca/ca_pub.pem delete mode 100644 acceptance/ssl/ca/inventory.txt delete mode 100644 acceptance/ssl/ca/private/ca.pass delete mode 100644 acceptance/ssl/ca/serial delete mode 100644 acceptance/ssl/ca/signed/activemq.pem delete mode 100644 acceptance/ssl/ca/signed/mcollective-client.pem delete mode 100644 acceptance/ssl/ca/signed/mcollective-server.pem delete mode 100644 acceptance/ssl/ca/signed/socks.local.pem delete mode 100644 acceptance/ssl/certs/activemq.pem delete mode 100644 acceptance/ssl/certs/ca.pem delete mode 100644 acceptance/ssl/certs/mcollective-client.pem delete mode 100644 acceptance/ssl/certs/mcollective-server.pem delete mode 100644 acceptance/ssl/certs/socks.local.pem delete mode 100644 acceptance/ssl/crl.pem delete mode 100644 acceptance/ssl/private_keys/activemq.pem delete mode 100644 acceptance/ssl/private_keys/mcollective-client.pem delete mode 100644 acceptance/ssl/private_keys/mcollective-server.pem delete mode 100644 acceptance/ssl/private_keys/socks.local.pem delete mode 100644 acceptance/ssl/public_keys/activemq.pem delete mode 100644 acceptance/ssl/public_keys/mcollective-client.pem delete mode 100644 acceptance/ssl/public_keys/mcollective-server.pem delete mode 100644 acceptance/ssl/public_keys/socks.local.pem delete mode 100644 acceptance/tests/mco_ping.rb delete mode 100644 acceptance/tests/mco_puppet_batch_count.rb delete mode 100644 acceptance/tests/mco_puppet_count.rb delete mode 100644 acceptance/tests/mco_puppet_exec.rb delete mode 100644 acceptance/tests/mco_puppet_powershell.rb delete mode 100644 acceptance/tests/mco_puppet_runonce.rb delete mode 100644 acceptance/tests/mco_reconnect.rb delete mode 100755 bin/mcollectived delete mode 100644 ext/Makefile delete mode 100644 ext/action_helpers/perl/.gitignore delete mode 100644 ext/action_helpers/perl/Makefile.PL delete mode 100644 ext/action_helpers/perl/lib/MCollective/Action.pm delete mode 100644 ext/action_helpers/perl/t/basic.t delete mode 100644 ext/action_helpers/php/README.markdown delete mode 100644 ext/action_helpers/php/mcollective_action.php delete mode 100644 ext/action_helpers/python/kwilczynski/README.markdown delete mode 100644 ext/action_helpers/python/kwilczynski/echo.py delete mode 100644 ext/action_helpers/python/kwilczynski/mcollective_action.py delete mode 100644 ext/action_helpers/python/romke/README.markdown delete mode 100644 ext/action_helpers/python/romke/mcollectiveah.py delete mode 100644 ext/action_helpers/python/romke/test.py delete mode 100644 ext/activemq/apache-activemq.spec delete mode 100644 ext/activemq/examples/multi-broker/README delete mode 100755 ext/activemq/examples/multi-broker/broker1-activemq.xml delete mode 100755 ext/activemq/examples/multi-broker/broker2-activemq.xml delete mode 100755 ext/activemq/examples/multi-broker/broker3-activemq.xml delete mode 100644 ext/activemq/examples/single-broker/README delete mode 100644 ext/activemq/examples/single-broker/activemq.xml delete mode 100644 ext/activemq/wlcg-patch.tgz delete mode 100644 ext/aio/README.md delete mode 100644 ext/aio/common/client.cfg.dist delete mode 100644 ext/aio/common/server.cfg.dist delete mode 100644 ext/aio/debian/mcollective.default delete mode 100755 ext/aio/debian/mcollective.init delete mode 100644 ext/aio/osx/mcollective.plist delete mode 100644 ext/aio/redhat/mcollective-systemd.logrotate delete mode 100644 ext/aio/redhat/mcollective-sysv.logrotate delete mode 100755 ext/aio/redhat/mcollective.init delete mode 100644 ext/aio/redhat/mcollective.service delete mode 100644 ext/aio/redhat/mcollective.sysconfig delete mode 100644 ext/aio/solaris/smf/mcollective.xml delete mode 100755 ext/aio/suse/mcollective.init delete mode 100644 ext/bash/mco_completion.sh delete mode 100644 ext/build_defaults.yaml delete mode 100644 ext/debian/changelog.erb delete mode 100644 ext/debian/compat delete mode 100644 ext/debian/control delete mode 100644 ext/debian/copyright delete mode 100644 ext/debian/mcollective-client.install delete mode 100644 ext/debian/mcollective-common.install delete mode 100644 ext/debian/mcollective-doc.install delete mode 100644 ext/debian/mcollective.dirs delete mode 100755 ext/debian/mcollective.init delete mode 100644 ext/debian/mcollective.install delete mode 100755 ext/debian/patches/pluginsdir.patch delete mode 100644 ext/debian/patches/series delete mode 100755 ext/debian/rules delete mode 100644 ext/debian/source/format delete mode 100644 ext/help-templates/README delete mode 100644 ext/help-templates/rpc-help-markdown.erb delete mode 100755 ext/mc-irb delete mode 100755 ext/mc-rpc-restserver.rb delete mode 100644 ext/openbsd/README delete mode 100644 ext/openbsd/port-files/mcollective/Makefile delete mode 100644 ext/openbsd/port-files/mcollective/distinfo delete mode 100644 ext/openbsd/port-files/mcollective/patches/patch-etc_server_cfg_dist delete mode 100644 ext/openbsd/port-files/mcollective/patches/patch-ext_Makefile delete mode 100644 ext/openbsd/port-files/mcollective/pkg/DESCR delete mode 100644 ext/openbsd/port-files/mcollective/pkg/MESSAGE delete mode 100644 ext/openbsd/port-files/mcollective/pkg/PLIST delete mode 100644 ext/openbsd/port-files/mcollective/pkg/mcollectived.rc delete mode 100644 ext/osx/README delete mode 100644 ext/osx/bldmacpkg delete mode 100644 ext/packaging.rake delete mode 100644 ext/perl/mc-find-hosts.pl delete mode 100644 ext/project_data.yaml delete mode 100755 ext/redhat/mcollective.init delete mode 100644 ext/redhat/mcollective.service delete mode 100644 ext/redhat/mcollective.spec.erb delete mode 100644 ext/solaris/README delete mode 100755 ext/solaris/build delete mode 100644 ext/solaris/cswmcollectived.xml delete mode 100644 ext/solaris/depend delete mode 100755 ext/solaris/mcollective.init delete mode 100644 ext/solaris/pkginfo delete mode 100644 ext/solaris/postinstall delete mode 100644 ext/solaris/postremove delete mode 100644 ext/solaris/preremove delete mode 100644 ext/solaris/prototype.head delete mode 100755 ext/solaris/setversion delete mode 100644 ext/solaris11/Makefile delete mode 100644 ext/solaris11/README.md delete mode 100755 ext/stompclient delete mode 100644 ext/vim/_.snippets delete mode 100644 ext/vim/mcollective_ddl.snippets delete mode 100644 ext/windows/README.md delete mode 100644 ext/windows/daemon.bat delete mode 100644 ext/windows/environment.bat delete mode 100644 ext/windows/mco.bat delete mode 100644 ext/windows/register_service.bat delete mode 100644 ext/windows/service_manager.rb delete mode 100644 ext/windows/unregister_service.bat delete mode 100644 ext/zsh/_mco delete mode 100755 install.rb delete mode 100644 lib/mcollective/agent/discovery.rb delete mode 100644 lib/mcollective/agent/rpcutil.ddl delete mode 100644 lib/mcollective/agent/rpcutil.rb delete mode 100644 lib/mcollective/audit/logfile.rb delete mode 100644 lib/mcollective/connector/activemq.ddl delete mode 100644 lib/mcollective/connector/activemq.rb delete mode 100644 lib/mcollective/connector/rabbitmq.ddl delete mode 100644 lib/mcollective/connector/rabbitmq.rb delete mode 100644 lib/mcollective/registration.rb delete mode 100644 lib/mcollective/registration/agentlist.rb delete mode 100644 lib/mcollective/runner.rb delete mode 100644 lib/mcollective/security/aes_security.rb delete mode 100644 lib/mcollective/security/psk.rb delete mode 100644 lib/mcollective/security/ssl.rb delete mode 100644 lib/mcollective/unix_daemon.rb delete mode 100644 lib/mcollective/vendor.rb delete mode 100644 lib/mcollective/vendor/load_systemu.rb delete mode 100644 lib/mcollective/vendor/require_vendored.rb delete mode 100755 lib/mcollective/vendor/systemu/BSDL delete mode 100755 lib/mcollective/vendor/systemu/LICENSE delete mode 100755 lib/mcollective/vendor/systemu/README delete mode 100755 lib/mcollective/vendor/systemu/README.erb delete mode 100755 lib/mcollective/vendor/systemu/Rakefile delete mode 100755 lib/mcollective/vendor/systemu/lib/systemu.rb delete mode 100755 lib/mcollective/vendor/systemu/samples/a.rb delete mode 100755 lib/mcollective/vendor/systemu/samples/b.rb delete mode 100755 lib/mcollective/vendor/systemu/samples/c.rb delete mode 100755 lib/mcollective/vendor/systemu/samples/d.rb delete mode 100755 lib/mcollective/vendor/systemu/samples/e.rb delete mode 100755 lib/mcollective/vendor/systemu/samples/f.rb delete mode 100755 lib/mcollective/vendor/systemu/systemu.gemspec delete mode 100755 lib/mcollective/vendor/systemu/test/systemu_test.rb delete mode 100755 lib/mcollective/vendor/systemu/test/testing.rb delete mode 100644 lib/mcollective/windows_daemon.rb delete mode 100755 mcollective.init delete mode 100644 spec/unit/mcollective/agent/rpcutil_spec.rb delete mode 100644 spec/unit/mcollective/audit/logfile_spec.rb delete mode 100644 spec/unit/mcollective/connector/activemq_spec.rb delete mode 100644 spec/unit/mcollective/connector/rabbitmq_spec.rb delete mode 100755 spec/unit/mcollective/registration/base_spec.rb delete mode 100644 spec/unit/mcollective/runner_spec.rb delete mode 100644 spec/unit/mcollective/security/aes_security_spec.rb delete mode 100755 spec/unit/mcollective/security/psk_spec.rb delete mode 100644 spec/unit/mcollective/security/ssl_spec.rb delete mode 100755 spec/unit/mcollective/unix_daemon_spec.rb delete mode 100755 spec/unit/mcollective/vendor_spec.rb delete mode 100755 spec/unit/mcollective/windows_daemon_spec.rb delete mode 100644 website/_includes/main_menu.html delete mode 100644 website/blueprint/ie.css delete mode 100755 website/blueprint/plugins/buttons/icons/cross.png delete mode 100755 website/blueprint/plugins/buttons/icons/key.png delete mode 100755 website/blueprint/plugins/buttons/icons/tick.png delete mode 100644 website/blueprint/plugins/buttons/readme.txt delete mode 100644 website/blueprint/plugins/buttons/screen.css delete mode 100644 website/blueprint/plugins/fancy-type/readme.txt delete mode 100644 website/blueprint/plugins/fancy-type/screen.css delete mode 100644 website/blueprint/plugins/link-icons/icons/doc.png delete mode 100644 website/blueprint/plugins/link-icons/icons/email.png delete mode 100644 website/blueprint/plugins/link-icons/icons/external.png delete mode 100644 website/blueprint/plugins/link-icons/icons/feed.png delete mode 100644 website/blueprint/plugins/link-icons/icons/im.png delete mode 100644 website/blueprint/plugins/link-icons/icons/pdf.png delete mode 100644 website/blueprint/plugins/link-icons/icons/visited.png delete mode 100644 website/blueprint/plugins/link-icons/icons/xls.png delete mode 100644 website/blueprint/plugins/link-icons/readme.txt delete mode 100644 website/blueprint/plugins/link-icons/screen.css delete mode 100644 website/blueprint/plugins/rtl/readme.txt delete mode 100644 website/blueprint/plugins/rtl/screen.css delete mode 100644 website/blueprint/print.css delete mode 100644 website/blueprint/screen.css delete mode 100644 website/blueprint/src/forms.css delete mode 100755 website/blueprint/src/grid.css delete mode 100644 website/blueprint/src/grid.png delete mode 100644 website/blueprint/src/ie.css delete mode 100755 website/blueprint/src/print.css delete mode 100755 website/blueprint/src/reset.css delete mode 100644 website/blueprint/src/typography.css delete mode 100644 website/changelog.md delete mode 100644 website/configure/client.md delete mode 100644 website/configure/server.md delete mode 100644 website/deploy/demo.md delete mode 100644 website/deploy/index.md delete mode 100644 website/deploy/install.md delete mode 100644 website/deploy/middleware/activemq.md delete mode 100644 website/deploy/middleware/activemq_keystores.md delete mode 100644 website/deploy/middleware/index.md delete mode 100644 website/deploy/plugins.md delete mode 100644 website/deploy/standard.md delete mode 100644 website/ec2demo.md delete mode 100644 website/images/activemq-multi-locations.png delete mode 100644 website/images/client.gif delete mode 100644 website/images/deployment.gif delete mode 100644 website/images/mcollective-aaa.png delete mode 100644 website/images/mcollective/li.png delete mode 100644 website/images/message-flow-diagram.png delete mode 100644 website/images/middleware-magic.gif delete mode 100644 website/images/middleware.gif delete mode 100644 website/images/server.gif delete mode 100644 website/images/subcollectives-collectives.png delete mode 100644 website/images/subcollectives-impact.png delete mode 100644 website/images/subcollectives-multiple-middleware.png delete mode 100644 website/index.md delete mode 100644 website/overview_components.md delete mode 100644 website/plugin_directory/agent_file_manager.markdown delete mode 100644 website/plugin_directory/agent_iptables_junk_filter.markdown delete mode 100644 website/plugin_directory/agent_metadata.markdown delete mode 100644 website/plugin_directory/agent_registration_mongodb.markdown delete mode 100644 website/plugin_directory/agent_registration_monitor.markdown delete mode 100644 website/plugin_directory/apt.markdown delete mode 100644 website/plugin_directory/authorization_action_policy.markdown delete mode 100644 website/plugin_directory/central_rpc_log.markdown delete mode 100644 website/plugin_directory/discovery_assisted_ssh.markdown delete mode 100644 website/plugin_directory/facter.markdown delete mode 100644 website/plugin_directory/facter_via_yaml.markdown delete mode 100644 website/plugin_directory/index.markdown delete mode 100644 website/plugin_directory/logstash_rpc_audit_logs.markdown delete mode 100644 website/plugin_directory/net_test.markdown delete mode 100644 website/plugin_directory/none.markdown delete mode 100644 website/plugin_directory/nrpe_agent.markdown delete mode 100644 website/plugin_directory/ohai.markdown delete mode 100644 website/plugin_directory/package.markdown delete mode 100644 website/plugin_directory/packages.markdown delete mode 100644 website/plugin_directory/process_management.markdown delete mode 100644 website/plugin_directory/puppet_agent.markdown delete mode 100644 website/plugin_directory/puppet_ca.markdown delete mode 100644 website/plugin_directory/puppet_commander.markdown delete mode 100644 website/plugin_directory/registration_metadata.markdown delete mode 100644 website/plugin_directory/resources_data_plugin.markdown delete mode 100644 website/plugin_directory/services.markdown delete mode 100644 website/plugin_directory/spamassassin.markdown delete mode 100644 website/plugin_directory/stomp_util.markdown delete mode 100644 website/plugin_directory/sysctl_data.markdown delete mode 100644 website/reference/basic/basic_agent_and_client.md delete mode 100644 website/reference/basic/basic_cli_usage.md delete mode 100644 website/reference/basic/configuration.md delete mode 100644 website/reference/basic/daemon.md delete mode 100644 website/reference/basic/gettingstarted.md delete mode 100644 website/reference/basic/gettingstarted_debian.md delete mode 100644 website/reference/basic/gettingstarted_redhat.md delete mode 100644 website/reference/basic/messageflow.md delete mode 100644 website/reference/basic/messageformat.md delete mode 100644 website/reference/basic/subcollectives.md delete mode 100644 website/reference/development/ec2_demo.md delete mode 100644 website/reference/development/releasetasks.md delete mode 100644 website/reference/index.md delete mode 100644 website/reference/integration/activemq_clusters.md delete mode 100644 website/reference/integration/activemq_security.md delete mode 100644 website/reference/integration/activemq_ssl.md delete mode 100644 website/reference/integration/puppet.md delete mode 100644 website/reference/plugins/aggregate.md delete mode 100644 website/reference/plugins/application.md delete mode 100644 website/reference/plugins/connector_activemq.md delete mode 100644 website/reference/plugins/connector_rabbitmq.md delete mode 100644 website/reference/plugins/connector_stomp.md delete mode 100644 website/reference/plugins/data.md delete mode 100644 website/reference/plugins/ddl.md delete mode 100644 website/reference/plugins/discovery.md delete mode 100644 website/reference/plugins/facts.md delete mode 100644 website/reference/plugins/registration.md delete mode 100644 website/reference/plugins/rpcutil.md delete mode 100644 website/reference/plugins/security_aes.md delete mode 100644 website/reference/plugins/security_ssl.md delete mode 100644 website/reference/plugins/validator.md delete mode 100644 website/reference/ui/filters.md delete mode 100644 website/reference/ui/nodereports.md delete mode 100644 website/releasenotes.md delete mode 100644 website/screencasts.md delete mode 100644 website/security.md delete mode 100644 website/simplerpc/agents.md delete mode 100644 website/simplerpc/auditing.md delete mode 100644 website/simplerpc/authorization.md delete mode 100644 website/simplerpc/clients.md delete mode 100644 website/simplerpc/index.md delete mode 100644 website/simplerpc/messageformat.md delete mode 100644 website/terminology.md diff --git a/.circleci/config.yml b/.circleci/config.yml new file mode 100644 index 00000000..40675fa1 --- /dev/null +++ b/.circleci/config.yml @@ -0,0 +1,15 @@ +version: 2 +jobs: + rspec_and_rubocop: + docker: + - image: circleci/ruby:2.4-node + steps: + - checkout + - run: bundle install + - run: bundle exec rake test + +workflows: + version: 2 + test: + jobs: + - rspec_and_rubocop diff --git a/.gitignore b/.gitignore index e2f988a6..68d1e079 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,3 @@ -doc build etc/*cfg etc/*yaml @@ -7,9 +6,6 @@ etc/ssl/clients/*pem plugins/mcollective/security/none.rb tags pkg -.localeapp Gemfile.lock .bundle -ext/packaging -.idea -Gemfile.local +.ruby-version diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 26b1cc16..00000000 --- a/.travis.yml +++ /dev/null @@ -1,19 +0,0 @@ -sudo: false -language: ruby -script: "bundle exec rake $CHECK" -notifications: - email: false -rvm: - - 2.4.1 - - 2.1.9 - - 2.0.0 -env: - - "CHECK=test" - - "CHECK=rubocop" - -matrix: - exclude: - - rvm: 2.1.9 - env: "CHECK=rubocop" - - rvm: 2.0.0 - env: "CHECK=rubocop" diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..228a9866 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,74 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +In the interest of fostering an open and welcoming environment, we as +contributors and maintainers pledge to making participation in our project and +our community a harassment-free experience for everyone, regardless of age, body +size, disability, ethnicity, gender identity and expression, level of experience, +nationality, personal appearance, race, religion, or sexual identity and +orientation. + +## Our Standards + +Examples of behavior that contributes to creating a positive environment +include: + +* Using welcoming and inclusive language +* Being respectful of differing viewpoints and experiences +* Gracefully accepting constructive criticism +* Focusing on what is best for the community +* Showing empathy towards other community members + +Examples of unacceptable behavior by participants include: + +* The use of sexualized language or imagery and unwelcome sexual attention or +advances +* Trolling, insulting/derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or electronic + address, without explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Our Responsibilities + +Project maintainers are responsible for clarifying the standards of acceptable +behavior and are expected to take appropriate and fair corrective action in +response to any instances of unacceptable behavior. + +Project maintainers have the right and responsibility to remove, edit, or +reject comments, commits, code, wiki edits, issues, and other contributions +that are not aligned to this Code of Conduct, or to ban temporarily or +permanently any contributor for other behaviors that they deem inappropriate, +threatening, offensive, or harmful. + +## Scope + +This Code of Conduct applies both within project spaces and in public spaces +when an individual is representing the project or its community. Examples of +representing a project or community include using an official project e-mail +address, posting via an official social media account, or acting as an appointed +representative at an online or offline event. Representation of a project may be +further defined and clarified by project maintainers. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported by contacting the project team at rip@devco.net. All +complaints will be reviewed and investigated and will result in a response that +is deemed necessary and appropriate to the circumstances. The project team is +obligated to maintain confidentiality with regard to the reporter of an incident. +Further details of specific enforcement policies may be posted separately. + +Project maintainers who do not follow or enforce the Code of Conduct in good +faith may face temporary or permanent repercussions as determined by other +members of the project's leadership. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, +available at [http://contributor-covenant.org/version/1/4][version] + +[homepage]: http://contributor-covenant.org +[version]: http://contributor-covenant.org/version/1/4/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 7547324c..bacda571 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,9 +1,45 @@ # How to contribute -Third-party patches are essential for keeping Puppet open-source projects -great. We want to keep it as easy as possible to contribute changes that -allow you to get the most out of our projects. There are a few guidelines -that we need contributors to follow so that we can have a chance of keeping on -top of things. For more info, see our canonical guide to contributing: +This project relies on your contributions for it's continued success. We want to make the process as easy as possible but a few guidelines are needed. -[https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md) +First please note that participation is bound by our [Code Of Conduct](CODE_OF_CONDUCT.md). + +# Issues + +We do not have strong guidelines on what you put in an issue, we'll gladly work with you to gather the information we require. We do ask that you try to put as much detail as possible to help everyone use their time in a meaningful way. + +# PRs + +Almost every PR will need an issue first. You do not need issues for trivial updates like typos, most documentation updates etc. But if you're doing a PR that adjusts behavior we will need a issue to go with it. + +Once you have an issue note the number and once you are ready to send your PR please squash your commits then make a single commit with something like the following format: + +``` +(#123) Add a super awesome feature + +This adds a new super awesome feature that everyone would love, +it can be used for such and such. + +To deliver this feature we had to extend, this that and the other +with new behaviours + +Backwards compatibility is kept +``` + +Please see [How to Write a Git Commit Message](https://chris.beams.io/posts/git-commit/) for excellent coverage of the topic of commit messages. We don't require things to be as detailed or strict as that but it's a great resource to keep in mind. + +Short updates obviously do not need all this, very often I am happy with just a 1 line commit message that matches the first line above. + +For trivial updates without issues and where context is not needed: + +``` +(misc) Fix typo in README.md +``` + +# Finding others in the community + +Usually we like to keep discussions on issues where they relate but of course we are very happy to chat in a more real time manner, especially useful if you want to find out our thoughts on your proposed changes ahead of spending your valuable time on them: + + * [Mailing List](https://groups.google.com/forum/#!forum/choria-users) + * [#choria on the Puppet Slack](http://slack.puppet.com/) + * #mcollective on Freenode diff --git a/Gemfile b/Gemfile index 682deea0..697c0518 100755 --- a/Gemfile +++ b/Gemfile @@ -1,37 +1,10 @@ -source ENV['GEM_SOURCE'] || 'https://rubygems.org' - -gem 'stomp', '>= 1.4.4' - -if RUBY_VERSION =~ /^1\.8/ - gem 'systemu', '2.6.4' - gem 'json', '~> 1.8.3' -else - gem 'systemu' -end - -if RUBY_VERSION =~ /^2\.4/ - gem 'json', '~> 2.1.0' -end - -group :dev do - gem 'rake', '~> 10.4' - if RUBY_VERSION =~ /^2\.[0-9]\.[0-9]/ - gem 'rubocop', '~> 0.48.1', :platforms => [:ruby] - end -end - -group :test do - if RUBY_VERSION =~ /^1\.8/ - gem 'rdoc', '~> 4.2.2' - else - gem 'rdoc' - end - gem 'yarjuf', "~> 1.0" - gem 'rspec', '~> 2.11.0' - gem 'mocha', '~> 0.10.0' - gem 'mcollective-test' -end - -if File.exists? "#{__FILE__}.local" - eval(File.read("#{__FILE__}.local"), binding) -end +source ENV["GEM_SOURCE"] || "https://rubygems.org" + +gem "json", "~> 2.1.0" +gem "systemu", "~> 2.6.4" +gem "rdoc" +gem "yarjuf", "~> 1.0" +gem "rspec", "~> 2.11.0" +gem "mocha", "~> 0.10.0" +gem "mcollective-test" +gem "rake", "< 11.0" diff --git a/COPYING b/LICENSE similarity index 99% rename from COPYING rename to LICENSE index 375d1c86..d6456956 100644 --- a/COPYING +++ b/LICENSE @@ -187,7 +187,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2010, 2011 Puppet Labs + Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/MAINTAINERS b/MAINTAINERS deleted file mode 100644 index bb9dbbeb..00000000 --- a/MAINTAINERS +++ /dev/null @@ -1,27 +0,0 @@ -{ - "version": 1, - "file_format": "This MAINTAINERS file format is described at http://pup.pt/maintainers", - "issues": "https://tickets.puppet.com/browse/MCO", - "internal_list": "https://groups.google.com/a/puppet.com/forum/?hl=en#!forum/discuss-orchestrator-maintainers", - "people": [ - { - "github": "MikaelSmith", - "email": "michael.smith@puppet.com", - "name": "Michael Smith" - }, - { - "github": "mruzicka", - "name": "Michal Růžička" - }, - { - "github": "parisiale", - "email": "alessandro@puppet.com", - "name": "Alessandro Parisi" - }, - { - "github": "johnduarte", - "email": "john.duarte@puppet.com", - "name": "John Duarte" - } - ] -} diff --git a/NOTICE b/NOTICE new file mode 100644 index 00000000..442e1975 --- /dev/null +++ b/NOTICE @@ -0,0 +1,6 @@ +Choria Orchestrator + +Copyright 2018 R.I.Pienaar + +This product includes software developed at +The Choria Project (https://choria.io). diff --git a/README.md b/README.md index 62eca19e..eaa35a75 100644 --- a/README.md +++ b/README.md @@ -1,105 +1,11 @@ -# The Marionette Collective +# Choria MCollective RPC Ruby Support Libraries -The Marionette Collective aka. mcollective is a framework to build server orchestration or parallel job execution systems. +These are stripped down versions of the legacy MCollective libraries that +are used to facilitate hosting Ruby based MCollective Agents within the +Golang daemon as well as interact with a Choria network from the Ruby API +and `mco` CLI -For documentation please see https://docs.puppet.com/mcollective +It's not possible to run the old MCollective daemon using this code or the +resulting gem. -## Developing - -The documentation above details how MCollective works and many of its extension points. - -### Spec Tests - -To run spec tests -``` -bundle install -bundle exec rake test -``` - -### Acceptance Tests - -To run acceptance tests, see [this][acceptance]. - -### Development Environment (MacOS) - -Setup ActiveMQ using acceptance config: -``` -brew install activemq -cp acceptance/files/activemq.* /usr/local/opt/activemq/libexec/conf -activemq start -``` - -ActiveMQ can later by stopped with `activemq stop`. ActiveMQ logs are located at -`/usr/local/opt/activemq/libexec/data/activemq.log`. - -Setup MCollective with acceptance config: -``` -mkdir -p ~/.puppetlabs/etc/mcollective/ssl-clients -cp acceptance/files/client.* ~/.puppetlabs/etc/mcollective -cp acceptance/files/server.* ~/.puppetlabs/etc/mcollective -cp acceptance/files/ca_crt.pem ~/.puppetlabs/etc/mcollective -cp acceptance/files/client.crt ~/.puppetlabs/etc/mcollective/ssl-clients/client.pem -ln -s ~/.puppetlabs/etc/mcollective/client.cfg ~/.mcollective -``` - -Modify `client.cfg` to work on the local machine: -* Change the `ssl_server_public`, `ssl_client_private`, `ssl_client_public` -paths to point to `~/.puppetlabs/etc/mcollective/{server.crt,client.key,client.pem}`. -* Change the `activemq.pool.1.ssl.{ca,cert,key}` paths to -`~/.puppetlabs/etc/mcollective/{ca_crt.pem,client.crt,client.key}`. -Note that `~` needs to be expanded to the full path. Also, that `client.pem` doesn't -point to an actual file is intentional (I don't fully understand why). - -Create `server.cfg`, updating ``: -``` -main_collective = mcollective -collectives = mcollective -logger_type = console -loglevel = info -daemonize = 0 - -securityprovider = ssl -plugin.ssl_server_private = /Users//.puppetlabs/etc/mcollective/server.key -plugin.ssl_server_public = /Users//.puppetlabs/etc/mcollective/server.crt -plugin.ssl_client_cert_dir = /Users//.puppetlabs/etc/mcollective/ssl-clients - -connector = activemq -plugin.activemq.pool.size = 1 -plugin.activemq.pool.1.host = activemq -plugin.activemq.pool.1.port = 61613 -plugin.activemq.pool.1.user = mcollective -plugin.activemq.pool.1.password = marionette -plugin.activemq.pool.1.ssl = true -plugin.activemq.pool.1.ssl.ca = /Users//.puppetlabs/etc/mcollective/ca_crt.pem -plugin.activemq.pool.1.ssl.cert = /Users//.puppetlabs/etc/mcollective/server.crt -plugin.activemq.pool.1.ssl.key = /Users//.puppetlabs/etc/mcollective/server.key -``` - -The configuration above uses `activemq` as the name of the ActiveMQ broker. MCollective -will enforce that the SSL certificate presented by the server matches the name it's trying -to connect to. To use the configuration above, traffic to `activemq` must be redirected to -the local host. On most machines, that can be accomplished with -``` -sudo echo "127.0.0.1   activemq" >> /etc/hosts -``` - -From the root of this repository, test the setup by running a server -``` -RUBYLIB=lib bundle exec bin/mcollectived --config ~/.puppetlabs/etc/mcollective/server.cfg -``` -and client -``` -RUBYLIB=lib bundle exec bin/mco ping -``` - -Note that it may be useful to change the `loglevel` in `client.cfg` to debug issues with -`mco ping`. - -To enable specific plugins, you may need to set `libdir` in `server.cfg` and add plugin-specific configuration. - -## Contributing - -Please refer to [this][contributing] document. - -[contributing]: CONTRIBUTING.md -[acceptance]: acceptance/README.md +For further information please see [https://choria.io](https://choria.io/) diff --git a/Rakefile b/Rakefile index 802a853c..6f6be9b8 100644 --- a/Rakefile +++ b/Rakefile @@ -1,131 +1,33 @@ -RAKE_ROOT = File.expand_path(File.dirname(__FILE__)) - -# Allow override of RELEASE using BUILD_NUMBER -ENV["RELEASE"] = ENV["BUILD_NUMBER"] if ENV["BUILD_NUMBER"] - -begin - load File.join(RAKE_ROOT, 'ext', 'packaging.rake') -rescue LoadError -end - -def announce(msg='') - STDERR.puts "================" - STDERR.puts msg - STDERR.puts "================" -end - -def safe_system *args - raise RuntimeError, "Failed: #{args.join(' ')}" unless system(*args) -end - -def load_tools - unless File.directory?(File.join(RAKE_ROOT, 'ext', 'packaging')) - Rake::Task["package:bootstrap"].invoke - begin - load File.join(RAKE_ROOT, 'ext', 'packaging.rake') - rescue LoadError - STDERR.puts "Could not load packaging tools. exiting" - exit 1 - end - end -end - -def move_artifacts - mv("pkg", "build") -end - -desc "Cleanup" -task :clean do - rm_rf "build" - rm_rf "doc" -end - -desc "Create the .debs" -task :deb => :clean do - load_tools - announce("Building debian packages for #{@build.project}-#{@build.version}-#{@build.release}") - Rake::Task["package:deb"].invoke - - if ENV['SIGNED'] == '1' - deb_flag = "-k#{ENV['SIGNWITH']}" if ENV['SIGNWITH'] - safe_system %{/usr/bin/debsign #{deb_flag} pkg/deb/*.changes} - end - move_artifacts -end - -desc "Build documentation" -task :doc => :clean do - load_tools - Rake::Task["package:doc"].invoke -end - -desc "Build a gem" -task :gem => :clean do - load_tools - Rake::Task["gem"].reenable - Rake::Task["package:gem"].invoke -end - -desc "Create a tarball for this release" -task :package => :clean do - load_tools - announce "Creating #{@build.project}-#{@build.version}.tar.gz" - Rake::Task["package:tar"].invoke - move_artifacts -end - -desc "Creates a RPM" -task :rpm => :clean do - load_tools - announce("Building RPM for #{@build.project}-#{@build.version}-#{@build.release}") - Rake::Task["package:rpm"].invoke - Rake::Task["package:srpm"].invoke - if ENV['SIGNED'] == '1' - safe_system %{/usr/bin/rpm --sign pkg/**/*.rpm} - end - move_artifacts +require "rubygems" +require "rubygems/package_task" + +PROJ_VERSION = "0.0.1" + +spec = Gem::Specification.new do |s| + s.name = "choria-mcorpc-support" + s.version = PROJ_VERSION + s.license = "Apache-2.0" + s.author = "R.I.Pienaar" + s.email = "rip@devco.net" + s.homepage = "https://choria.io/" + s.summary = "Support libraries the Choria Server" + s.description = "Libraries enabling Ruby support for the Choria Orchestration Server" + s.files = FileList["{lib,bin}/**/*"].to_a + s.require_path = "lib" + s.bindir = "bin" + s.test_files = FileList["spec/**/*"].to_a + s.executables = ["mco"] + s.add_dependency "systemu", "~> 2.6", ">= 2.6.4" + s.add_dependency "json", "~> 2.1", ">= 2.1.0" +end + +Gem::PackageTask.new(spec) do |pkg| + pkg.need_tar = false + pkg.need_zip = false + pkg.package_dir = "build" end desc "Run spec tests" task :test do sh "cd spec && rake" end - -desc "Run spec tests" -task :test => :spec - -namespace :ci do - desc "Run the specs with CI options" - task :spec do - ENV["LOG_SPEC_ORDER"] = "true" - sh %{rspec -r yarjuf -f JUnit -o result.xml -fp spec} - end -end - -desc "Creates the website as a tarball" -task :website => :clean do - FileUtils.mkdir_p("build/marionette-collective.org/html") - - Dir.chdir("website") do - safe_system("jekyll ../build/marionette-collective.org/html") - end - - unless File.exist?("build/marionette-collective.org/html/index.html") - raise "Failed to build website" - end - - Dir.chdir("build") do - safe_system("tar -cvzf marionette-collective-org-#{Time.now.to_i}.tgz marionette-collective.org") - end -end - -desc 'run static analysis with rubocop' -task(:rubocop) do - if RUBY_VERSION !~ /1.8/ - require 'rubocop' - cli = RuboCop::CLI.new - exit cli.run(%w(-D -f s)) - else - puts "Rubocop is disabled in ruby 1.8" - end -end diff --git a/acceptance/.gitignore b/acceptance/.gitignore deleted file mode 100644 index 4b73f7c8..00000000 --- a/acceptance/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -/log/ -/junit/ -/.vagrant diff --git a/acceptance/Gemfile b/acceptance/Gemfile deleted file mode 100644 index 91707eea..00000000 --- a/acceptance/Gemfile +++ /dev/null @@ -1,29 +0,0 @@ -# Specifies a gem mirror; duplicated in acceptance setup -# to ensure a similar environment on acceptance hosts. -source ENV['GEM_SOURCE'] || 'https://rubygems.org' - -def location_for(place, fake_version = nil) - if place =~ /^(git:[^#]*)#(.*)/ - [fake_version, { :git => $1, :branch => $2, :require => false }].compact - elsif place =~ /^file:\/\/(.*)/ - ['>= 0', { :path => File.expand_path($1), :require => false }] - else - [place, { :require => false }] - end -end - -gem "beaker", *location_for(ENV['BEAKER_VERSION'] || "~> 3.6") -gem "beaker-hostgenerator", *location_for(ENV['BEAKER_HOSTGENERATOR_VERSION'] || "~> 0.8") -gem "beaker-abs", *location_for(ENV['BEAKER_ABS_VERSION'] || "~> 0.2") -gem "rake" -gem "httparty", :require => false -gem 'uuidtools', :require => false - -group(:test) do - gem "rspec", "~> 2.14.0", :require => false - gem "mocha", "~> 0.10.5", :require => false -end - -if File.exists? "#{__FILE__}.local" - eval(File.read("#{__FILE__}.local"), binding) -end diff --git a/acceptance/README.md b/acceptance/README.md deleted file mode 100644 index e0b5f97d..00000000 --- a/acceptance/README.md +++ /dev/null @@ -1,112 +0,0 @@ -beaker tests for validation of MCollective in puppet-agent - -# WARNING - -![There be Dragons](http://upload.wikimedia.org/wikipedia/commons/thumb/b/bc/Chinese_black_dragon.svg/235px-Chinese_black_dragon.svg.png) - -**WARNING:** Under NO circumstances should you use **any** of the -certificate files located in the /acceptance directory or any of -its subdirectories in a production system. The private keys are -publicly available and **will** result in an insecure environment - -# Files directory - -/files contains pre-generated certificates and configuration files -that are used by the acceptance test pre-suites in order to quickly -facilitate a running environment on the system under test. The -certificates in the /files directory are for testing purposes only -and are publicly available. - -These files were generated using the command outlined below, in the -*SSL setup* section. - -# SSL setup - -/ssl is a puppet master's ssl directory. Selected files from this -have been copied into the files/ directory, either directly as .pem -files, or combined into java truststores. - -Commands used to set it up: - - puppet master --ssldir=`pwd`/ssl - puppet cert --ssldir=`pwd`/ssl generate activemq - puppet cert --ssldir=`pwd`/ssl generate mcollective-client - puppet cert --ssldir=`pwd`/ssl generate mcollective-server - - keytool -storepasswd -storepass notsecret -import -alias 'puppet ca' -file ssl/ca/ca_crt.pem -keystore files/activemq.truststore - cat ssl/private_keys/activemq.pem ssl/certs/activemq.pem > activemq.combined.pem - openssl pkcs12 -password pass:notsecret -export -in activemq.combined.pem -out activemq.p12 -name activemq.example.com - keytool -importkeystore -destkeystore files/activemq.keystore -deststorepass notsecret -srckeystore activemq.p12 -srcstoretype PKCS12 -srcstorepass notsecret -alias activemq.example.com - rm activemq.combined.pem activemq.p12 - - cp ssl/ca/ca_crt.pem files/ca_crt.pem - cp ssl/certs/mcollective-server.pem files/server.crt - cp ssl/private_keys/mcollective-server.pem files/server.key - cp ssl/certs/mcollective-client.pem files/client.crt - cp ssl/private_keys/mcollective-client.pem files/client.key - -# Running with Rake - -The rake task `ci:test:aio` will provision, install, and execute the tests -for you. It requires the environment variable for the sha of the puppet-agent -version that should be installed during testing. - -The minimal invocation of this task would be: -``` -bundle exec rake ci:test:aio SHA=1.8.2 -``` - -Typically, this task would be invoked against a development build with a -specific host target in mind. Given that we would like to test the MCO -functionality in the latest nightly build of puppet-agent on an Ubuntu 16.04 -x86_64 instance, the command would be: -``` -bundle exec rake ci:test:aio SHA=nightly TEST_TARGET=ubuntu1604-64mco_master.a -``` - -## Environment variables -The following environment variables are used in conjunction with the -`ci:test:aio` rake task: - -SHA _required_ - : Build identifier of puppet-agent version to be installed, release tag - or full SHA, (e.g. `nightly`, `1.8.2`, - `aa3068e6859a695167a4b7ac06584b4d4ace525f`). - -SUITE_VERSION - : If the SHA used is a development build, then this variable must be - specified, (e.g. 1.8.2.62.gaa3068e). - -TEST_TARGET - : Beaker-hostgenerator string used to dynamically create a Beaker hosts - file. The `mco_master` role must be part of this string. If left - unspecified, this will default to `windows2012r2-64mco_master.a`. - -MASTER_TEST_TARGET - : Beaker-hostgenerator string used to dynamically create a Beaker hosts - file. If unspecified, this will default to `redhat7-64ma`. - -BEAKER_HOSTS - : Path to an existing Beaker hosts file to be used. - -ACTIVEMQ_SOURCE - : A url from which to download pre-built ActiveMQ binaries. Together with - ACTIVEMQ_VERSION, specifies where to get binaries. The default uses an - internal Puppet mirror, externally you should use http://apache.osuosl.org. - Note that osuosl only hosts the latest releases. Setting this will attempt - to fetch a package from $ACTIVEMQ_SOURCE/activemq/$ACTIVEMQ_VERSION/apache-activemq-$ACTIVEMQ_VERSION-bin.tar.gz - (or .zip on Windows). - -ACTIVEMQ_VERSION - : The version of ActiveMQ requested. - -JDK_SOURCE - : A url from which to download pre-built JDK binaries for Windows (other - platforms use the package manager for the OS). Together with - JDK_VERSION_FULL, specifies where to get binaries. The default uses an - internal Puppet mirror, externally you should use https://download.oracle.com/otn-pub/java/jdk - -JDK_VERSION_FULL - : The full version of JDK requested. From Oracle, this should include the - build number (e.g. `8u111-b14`). See the [Oracle JDK downoad page](http://www.oracle.com/technetwork/java/javase/downloads/jdk8-downloads-2133151.html) - for details. diff --git a/acceptance/Rakefile b/acceptance/Rakefile deleted file mode 100644 index b3aec2c7..00000000 --- a/acceptance/Rakefile +++ /dev/null @@ -1,407 +0,0 @@ -require 'rake/clean' -require 'pp' -require 'yaml' -require 'securerandom' -require 'fileutils' -require 'beaker-hostgenerator' - -$LOAD_PATH << File.expand_path(File.join(File.dirname(__FILE__), 'lib')) -require 'puppet/acceptance/git_utils' -extend Puppet::Acceptance::GitUtils - -ONE_DAY_IN_SECS = 24 * 60 * 60 -REPO_CONFIGS_DIR = "repo-configs" -CLEAN.include('*.tar', REPO_CONFIGS_DIR, 'merged_options.rb') - -# If elsewhere we're depending on internal network resources -# then assume we can depend on them here -EPEL_MIRROR = ENV['BEAKER_EPEL_MIRROR'] - -# Default test target if none specified -DEFAULT_MASTER_TEST_TARGET = 'redhat7-64m' -DEFAULT_TEST_TARGETS = "#{DEFAULT_MASTER_TEST_TARGET}a-windows2012r2-64mco_master.a" -module HarnessOptions - - DEFAULTS = { - :type => 'git', - :helper => ['lib/helper.rb'], - :tests => ['tests'], - :log_level => 'debug', - :color => false, - :root_keys => true, - :ssh => { - :keys => ["id_rsa_acceptance", "#{ENV['HOME']}/.ssh/id_rsa-acceptance"], - }, - :xml => true, - :timesync => false, - :repo_proxy => true, - :add_el_extras => true, - :preserve_hosts => 'onfail', - :forge_host => 'forge-aio01-petest.puppetlabs.com', - :'master-start-curl-retries' => 30, - } - - class Aggregator - attr_reader :mode - - def initialize(mode) - @mode = mode - end - - def get_options(file_path) - puts file_path - if File.exists? file_path - options = eval(File.read(file_path), binding) - else - puts "No options file found at #{File.expand_path(file_path)}" - end - options || {} - end - - def get_mode_options - get_options("./config/#{mode}/options.rb") - end - - def get_local_options - get_options("./local_options.rb") - end - - def final_options(intermediary_options = {}) - mode_options = get_mode_options - local_overrides = get_local_options - final_options = DEFAULTS.merge(mode_options) - final_options.merge!(intermediary_options) - final_options.merge!(local_overrides) - return final_options - end - end - - def self.options(mode, options) - final_options = Aggregator.new(mode).final_options(options) - final_options - end -end - -def beaker_test(mode = :packages, options = {}) - delete_options = options.delete(:__delete_options__) || [] - final_options = HarnessOptions.options(mode, options) - preserve_config = final_options.delete(:__preserve_config__) - - if mode == :git - # Build up project git urls based on git server and fork env variables or defaults - final_options[:install].map! do |install| - raise(ArgumentError, "Missing Git URL within options hash. Install URL is nil.") if install.nil? - if md = /^(\w+)#(\w+)$/.match(install) - project, project_sha = md.captures - "#{build_giturl(project)}##{project_sha}" - elsif md = /^(\w+)$/.match(install) - project = md[1] - "#{build_giturl(project)}##{sha}" - else - install - end - end - end - - delete_options.each do |delete_me| - final_options.delete(delete_me) - end - - options_file = 'merged_options.rb' - File.open(options_file, 'w') do |merged| - merged.puts <<-EOS -# Copy this file to local_options.rb and adjust as needed if you wish to run -# with some local overrides. -EOS - merged.puts(final_options.pretty_inspect) - end - - tests = ENV['TESTS'] || ENV['TEST'] - tests_opt = "--tests=#{tests}" if tests - - target = ENV['TEST_TARGET'] - if config and File.exists?(config) - config_opt = "--hosts=#{config}" - else - if agent_target = ENV['TEST_TARGET'] - master_target = ENV['MASTER_TEST_TARGET'] || DEFAULT_MASTER_TEST_TARGET - targets = "#{master_target}-#{agent_target}" - else - targets = DEFAULT_TEST_TARGETS - end - - if !targets.include? 'mco_master' - targets = targets.gsub( /([^\d]+)$/, 'mco_master.\1') - end - - cli = BeakerHostGenerator::CLI.new([targets, '--disable-default-role', '--osinfo-version', '1']) - ENV['BEAKER_HOSTS'] = "tmp/#{targets}-#{SecureRandom.uuid}.yaml" - FileUtils.mkdir_p('tmp') - File.open(config, 'w') do |fh| - fh.print(cli.execute) - end - config_opt = "--hosts=#{config}" - end - - overriding_options = ENV['OPTIONS'] - - args = ["--options-file", options_file, config_opt, tests_opt, overriding_options].compact - - begin - sh("beaker", *args) - ensure - preserve_configuration(final_options, options_file) if preserve_config - end -end - -def preserve_configuration(final_options, options_file) - if (hosts_file = config || final_options[:hosts_file]) && hosts_file !~ /preserved_config/ - cp(hosts_file, "log/latest/config.yml") - generate_config_for_latest_hosts - end - mv(options_file, "log/latest") -end - -def generate_config_for_latest_hosts - preserved_config_hash = { 'HOSTS' => {} } - - puts "\nPreserving configuration so that any preserved nodes can be tested again locally..." - - config_hash = YAML.load_file('log/latest/config.yml') - if !config_hash || !config_hash.include?('HOSTS') - puts "Warning: No HOSTS configuration found in log/latest/config.yml" - return - else - nodes = config_hash['HOSTS'].map do |node_label,hash| - { - :node_label => node_label, - :roles => hash['roles'], - :platform => hash['platform'] - } - end - - pre_suite_log = File.read('log/latest/pre_suite-run.log') - nodes.each do |node_info| - host_regex = /^([\w.]+) \(#{node_info[:node_label]}\)/ - if matched = host_regex.match(pre_suite_log) - hostname = matched[1] - fqdn = (hostname =~ /\./) ? - hostname : - "#{hostname}.delivery.puppetlabs.net" - elsif /^#{node_info[:node_label]} /.match(pre_suite_log) - fqdn = "#{node_info[:node_label]}" - puts "* Couldn't find any log lines for #{host_regex}, assuming #{fqdn} is the fqdn" - end - if fqdn - preserved_config_hash['HOSTS'][fqdn] = { - 'roles' => node_info[:roles], - 'platform' => node_info[:platform], - } - else - puts "* Couldn't match #{node_info[:node_label]} in pre_suite-run.log" - end - end - pp preserved_config_hash - - File.open('log/latest/preserved_config.yaml', 'w') do |config_file| - YAML.dump(preserved_config_hash, config_file) - end - end -rescue Errno::ENOENT => e - puts "Warning: Couldn't generate preserved_config.yaml #{e}" -end - -def list_preserved_configurations(secs_ago = ONE_DAY_IN_SECS) - preserved = {} - Dir.glob('log/*_*').each do |dir| - preserved_config_path = "#{dir}/preserved_config.yaml" - yesterday = Time.now - secs_ago.to_i - if preserved_config = File.exists?(preserved_config_path) - directory = File.new(dir) - if directory.ctime > yesterday - hosts = [] - preserved_config = YAML.load_file(preserved_config_path).to_hash - preserved_config['HOSTS'].each do |hostname,values| - hosts << "#{hostname}: #{values['platform']}, #{values['roles']}" - end - preserved[hosts] = directory.to_path - end - end - end - preserved.map { |k,v| [v,k] }.sort { |a,b| a[0] <=> b[0] }.reverse -end - -def list_preserved_hosts(secs_ago = ONE_DAY_IN_SECS) - hosts = Set.new - Dir.glob('log/**/pre*suite*run.log').each do |log| - yesterday = Time.now - secs_ago.to_i - File.open(log, 'r') do |file| - if file.ctime > yesterday - file.each_line do |line| - matchdata = /^(\w+)(?:\.[\w.]+)? \(.*?\) \d\d:\d\d:\d\d\$/.match(line.encode!('UTF-8', 'UTF-8', :invalid => :replace)) - hosts.add(matchdata[1]) if matchdata - end - end - end - end - hosts -end - -def release_hosts(hosts = nil, secs_ago = ONE_DAY_IN_SECS) - secs_ago ||= ONE_DAY_IN_SECS - hosts ||= list_preserved_hosts(secs_ago) - - hosts.each do |h| - hostname = h.split('.').first - puts "Releaseing '#{hostname}'" - puts `curl -X DELETE --url http://vcloud.delivery.puppetlabs.net/vm/#{hostname}` - end -end - -def print_preserved(preserved) - preserved.each_with_index do |entry,i| - puts "##{i}: #{entry[0]}" - entry[1].each { |h| puts " #{h}" } - end -end - -def beaker_run_type - type = ENV['TYPE'] || :packages - type = type.to_sym -end - -def sha - ENV['SHA'] -end - -def config - ENV['BEAKER_HOSTS'] -end - -namespace :ci do - - task :check_env do - raise(USAGE) unless sha - end - - namespace :test do - USAGE = <<-EOS -Requires commit SHA to be put under test as environment variable: SHA=''. -Also must set BEAKER_HOSTS=config/nodes/foo.yaml or include it in an options.rb for Beaker, -or specify TEST_TARGET in a form beaker-hostgenerator accepts, e.g. `ubuntu1604-64a`. -The TEST_TARGET must include the `mco_master` role, e.g. `ubuntu1604-64mco_master.a`. -You may override the default master test target by specifying MASTER_TEST_TARGET. -You may set TESTS=path/to/test,and/more/tests. -You may set additional Beaker OPTIONS='--more --options' -If testing from git checkouts, you may optionally set the github fork to checkout from using PUPPET_FORK='some-other-puppet-fork' (you may change the HIERA_FORK and FACTER_FORK as well if you wish). -You may also optionally set the git server to checkout repos from using GIT_SERVER='some.git.mirror'. -Or you may set PUPPET_GIT_SERVER='my.host.with.git.daemon', specifically, if you have set up a `git daemon` to pull local commits from. (You will need to allow the git daemon to serve the repo (see `git help daemon` and the docs/acceptance_tests.md for more details)). -If there is a Beaker options hash in a ./local_options.rb, it will be included. Commandline options set through the above environment variables will override settings in this file. -EOS - - desc <<-EOS -Run the acceptance tests through Beaker and install packages on the configuration targets. -#{USAGE} -EOS - task :packages => 'ci:check_env' do - beaker_test - end - - desc <<-EOS -Run the acceptance tests through Beaker and install packages as part of the AIO puppet-agent installation. -#{USAGE} -EOS - task :aio => 'ci:check_env' do - beaker_test(:aio) - end - - desc <<-EOS -Run the acceptance tests through Beaker and install packages as part of the AIO puppet-agent installation, testing against puppet-master-passenger. -#{USAGE} -EOS - task :passenger => 'ci:check_env' do - beaker_test(:passenger) - end - - desc <<-EOS -Run the acceptance tests through Beaker and install from git on the configuration targets. -#{USAGE} -EOS - task :git => 'ci:check_env' do - beaker_test(:git) - end - end - - desc "Capture the master and agent hostname from the latest log and construct a preserved_config.yaml for re-running against preserved hosts without provisioning." - task :extract_preserved_config do - generate_config_for_latest_hosts - end - - desc <<-EOS -Run an acceptance test for a given node configuration and preserve the hosts. -Defaults to a packages run, but you can set it to 'git' with TYPE='git'. -#{USAGE} - EOS - task :test_and_preserve_hosts => 'ci:check_env' do - beaker_test(beaker_run_type, :preserve_hosts => 'always', :__preserve_config__ => true) - end - - desc "List acceptance runs from the past day which had hosts preserved." - task :list_preserved do - preserved = list_preserved_configurations - print_preserved(preserved) - end - - desc <<-EOS -Shutdown and destroy any hosts that we have preserved for testing. These should be reaped daily by scripts, but this will free up resources immediately. -Specify a list of comma separated HOST_NAMES if you have a set of dynamic vcloud host names you want to purge outside of what can be grepped from the logs. -You can go back through the last SECS_AGO logs. Default is one day ago in secs. - EOS - task :release_hosts do - host_names = ENV['HOST_NAMES'].split(',') if ENV['HOST_NAMES'] - secs_ago = ENV['SECS_AGO'] - release_hosts(host_names, secs_ago) - end - - task :destroy_preserved_hosts => 'ci:release_hosts' do - puts "Note: we are now releasing hosts back to the vcloud pooling api rather than destroying them directly. The rake task for this is ci:release_hosts" - end - - desc <<-EOS -Rerun an acceptance test using the last captured preserved_config.yaml to skip provisioning. -Or specify a CONFIG_NUMBER from `rake ci:list_preserved`. -Defaults to a packages run, but you can set it to 'git' with TYPE='git'. - EOS - task :test_against_preserved_hosts do - config_number = (ENV['CONFIG_NUMBER'] || 0).to_i - preserved = list_preserved_configurations - print_preserved(preserved) - config_path = preserved[config_number][0] - - puts "Using ##{config_number}: #{config_path}" - - options = { - :hosts_file => "#{config_path}/preserved_config.yaml", - :no_provision => true, - :preserve_hosts => 'always', - } - run_type = beaker_run_type - if run_type == :packages - options.merge!(:pre_suite => [ - 'setup/packages/pre-suite/015_PackageHostsPresets.rb', - 'setup/packages/pre-suite/045_EnsureMasterStartedOnPassenger.rb', - ]) - else - options.merge!(:__delete_options__ => [:pre_suite]) - end - beaker_test(beaker_run_type, options) - end -end - -task :default do - sh('rake -T') -end - -task :spec do - sh('rspec lib') -end diff --git a/acceptance/config/aio/options.rb b/acceptance/config/aio/options.rb deleted file mode 100644 index 04093e3e..00000000 --- a/acceptance/config/aio/options.rb +++ /dev/null @@ -1,18 +0,0 @@ -{ - :type => 'aio', - :is_puppetserver => true, - :puppetservice => 'puppetserver', - :'puppetserver-confdir' => '/etc/puppetlabs/puppetserver/conf.d', - :pre_suite => [ - 'setup/aio/pre-suite/010_Install.rb', - 'setup/aio/pre-suite/015_PackageHostsPresets.rb', - 'setup/common/pre-suite/025_StopFirewall.rb', - 'setup/common/pre-suite/040_ValidateSignCert.rb', - 'setup/aio/pre-suite/045_EnsureMasterStartedOnPassenger.rb', - 'setup/common/pre-suite/070_InstallCACerts.rb', - 'setup/aio/pre-suite/050_Install-activemq.rb', - 'setup/aio/pre-suite/060_Install-mcollective-daemon.rb', - 'setup/aio/pre-suite/070_Install_puppet-agent-plugin.rb', - ], - :plugins => [ 'mcollective-puppet-agent' ], -} diff --git a/acceptance/config/nodes/centos-5-x86_64.yaml b/acceptance/config/nodes/centos-5-x86_64.yaml deleted file mode 100644 index aff8a5b1..00000000 --- a/acceptance/config/nodes/centos-5-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: centos-5-x86_64 - hypervisor: vcloud - template: centos-5-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/centos-6-x86_64.yaml b/acceptance/config/nodes/centos-6-x86_64.yaml deleted file mode 100644 index 3c455298..00000000 --- a/acceptance/config/nodes/centos-6-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: centos-6-x86_64 - hypervisor: vcloud - template: centos-6-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/debian-6-x86_64.yaml b/acceptance/config/nodes/debian-6-x86_64.yaml deleted file mode 100644 index f8626227..00000000 --- a/acceptance/config/nodes/debian-6-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: debian-squeeze-x86_64 - hypervisor: vcloud - template: debian-6-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/debian-7-x86_64.yaml b/acceptance/config/nodes/debian-7-x86_64.yaml deleted file mode 100644 index fcbb638b..00000000 --- a/acceptance/config/nodes/debian-7-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: debian-wheezy-x86_64 - hypervisor: vcloud - template: debian-7-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/debian-8-x86_64.yaml b/acceptance/config/nodes/debian-8-x86_64.yaml deleted file mode 100644 index 7195ae41..00000000 --- a/acceptance/config/nodes/debian-8-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: debian-jessie-x86_64 - hypervisor: vcloud - template: debian-8-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/fedora-20-x86_64.yaml b/acceptance/config/nodes/fedora-20-x86_64.yaml deleted file mode 100644 index ff33c65f..00000000 --- a/acceptance/config/nodes/fedora-20-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: fedora-20-x86_64 - hypervisor: vcloud - template: fedora-20-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/fedora-21-x86_64.yaml b/acceptance/config/nodes/fedora-21-x86_64.yaml deleted file mode 100644 index ba96472a..00000000 --- a/acceptance/config/nodes/fedora-21-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: fedora-21-x86_64 - hypervisor: vcloud - template: fedora-21-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/redhat-5-x86_64.yaml b/acceptance/config/nodes/redhat-5-x86_64.yaml deleted file mode 100644 index fa32c493..00000000 --- a/acceptance/config/nodes/redhat-5-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: el-5-x86_64 - hypervisor: vcloud - template: redhat-5-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/redhat-6-x86_64.yaml b/acceptance/config/nodes/redhat-6-x86_64.yaml deleted file mode 100644 index d055031e..00000000 --- a/acceptance/config/nodes/redhat-6-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: el-6-x86_64 - hypervisor: vcloud - template: redhat-6-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/redhat-7-x86_64.yaml b/acceptance/config/nodes/redhat-7-x86_64.yaml deleted file mode 100644 index 16131e9d..00000000 --- a/acceptance/config/nodes/redhat-7-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/ubuntu-1204-x86_64.yaml b/acceptance/config/nodes/ubuntu-1204-x86_64.yaml deleted file mode 100644 index 33be4092..00000000 --- a/acceptance/config/nodes/ubuntu-1204-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: ubuntu-precise-x86_64 - hypervisor: vcloud - template: ubuntu-1204-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/ubuntu-1404-x86_64.yaml b/acceptance/config/nodes/ubuntu-1404-x86_64.yaml deleted file mode 100644 index 653c6ace..00000000 --- a/acceptance/config/nodes/ubuntu-1404-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: ubuntu-trusty-x86_64 - hypervisor: vcloud - template: ubuntu-1404-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/ubuntu-1410-x86_64.yaml b/acceptance/config/nodes/ubuntu-1410-x86_64.yaml deleted file mode 100644 index 6ce9dd53..00000000 --- a/acceptance/config/nodes/ubuntu-1410-x86_64.yaml +++ /dev/null @@ -1,20 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent: - roles: - - agent - - mco_master - platform: ubuntu-utopic-x86_64 - hypervisor: vcloud - template: ubuntu-1410-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2003r2x64-rubyx64.yaml b/acceptance/config/nodes/win2003r2x64-rubyx64.yaml deleted file mode 100644 index c14b57b1..00000000 --- a/acceptance/config/nodes/win2003r2x64-rubyx64.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2003r2-x86_64-rubyx64: - roles: - - agent - - mco_master - platform: windows-2003r2-64 - ruby_arch: x64 - hypervisor: vcloud - template: win-2003r2-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2003r2x64-rubyx86.yaml b/acceptance/config/nodes/win2003r2x64-rubyx86.yaml deleted file mode 100644 index 457a1687..00000000 --- a/acceptance/config/nodes/win2003r2x64-rubyx86.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2003r2-x86_64-rubyx86: - roles: - - agent - - mco_master - platform: windows-2003r2-64 - ruby_arch: x86 - hypervisor: vcloud - template: win-2003r2-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2003r2x86-rubyx86.yaml b/acceptance/config/nodes/win2003r2x86-rubyx86.yaml deleted file mode 100644 index 0c097b62..00000000 --- a/acceptance/config/nodes/win2003r2x86-rubyx86.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2003r2-i386: - roles: - - agent - - mco_master - platform: windows-2003r2-32 - ruby_arch: x86 - hypervisor: vcloud - template: win-2003r2-i386 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2003x64-rubyx64.yaml b/acceptance/config/nodes/win2003x64-rubyx64.yaml deleted file mode 100644 index ab33298e..00000000 --- a/acceptance/config/nodes/win2003x64-rubyx64.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2003-x86_64-rubyx64: - roles: - - agent - - mco_master - platform: windows-2003-64 - ruby_arch: x64 - hypervisor: vcloud - template: win-2003-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2003x64-rubyx86.yaml b/acceptance/config/nodes/win2003x64-rubyx86.yaml deleted file mode 100644 index 2a257935..00000000 --- a/acceptance/config/nodes/win2003x64-rubyx86.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2003-x86_64-rubyx86: - roles: - - agent - - mco_master - platform: windows-2003-64 - ruby_arch: x86 - hypervisor: vcloud - template: win-2003-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2003x86-rubyx86.yaml b/acceptance/config/nodes/win2003x86-rubyx86.yaml deleted file mode 100644 index dbfcd1b3..00000000 --- a/acceptance/config/nodes/win2003x86-rubyx86.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2003-i386: - roles: - - agent - - mco_master - platform: windows-2003-32 - ruby_arch: x86 - hypervisor: vcloud - template: win-2003-i386 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2008-rubyx64.yaml b/acceptance/config/nodes/win2008-rubyx64.yaml deleted file mode 100644 index 7f9ed438..00000000 --- a/acceptance/config/nodes/win2008-rubyx64.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2008-x86_64-rubyx64: - roles: - - agent - - mco_master - platform: windows-2008-64 - ruby_arch: x64 - hypervisor: vcloud - template: win-2008-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2008-rubyx86.yaml b/acceptance/config/nodes/win2008-rubyx86.yaml deleted file mode 100644 index 348aa2fe..00000000 --- a/acceptance/config/nodes/win2008-rubyx86.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2008-x86_64-rubyx86: - roles: - - agent - - mco_master - platform: windows-2008-64 - ruby_arch: x86 - hypervisor: vcloud - template: win-2008-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2008r2-rubyx64.yaml b/acceptance/config/nodes/win2008r2-rubyx64.yaml deleted file mode 100644 index 392db143..00000000 --- a/acceptance/config/nodes/win2008r2-rubyx64.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2008r2-x86_64-rubyx64: - roles: - - agent - - mco_master - platform: windows-2008r2-64 - ruby_arch: x64 - hypervisor: vcloud - template: win-2008r2-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2008r2-rubyx86.yaml b/acceptance/config/nodes/win2008r2-rubyx86.yaml deleted file mode 100644 index 43097008..00000000 --- a/acceptance/config/nodes/win2008r2-rubyx86.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2008r2-x86_64-rubyx86: - roles: - - agent - - mco_master - platform: windows-2008r2-64 - ruby_arch: x86 - hypervisor: vcloud - template: win-2008r2-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2012-rubyx64.yaml b/acceptance/config/nodes/win2012-rubyx64.yaml deleted file mode 100644 index 62ad1a95..00000000 --- a/acceptance/config/nodes/win2012-rubyx64.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2012-x86_64-rubyx64: - roles: - - agent - - mco_master - platform: windows-2012-64 - ruby_arch: x64 - hypervisor: vcloud - template: win-2012-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2012-rubyx86.yaml b/acceptance/config/nodes/win2012-rubyx86.yaml deleted file mode 100644 index 41e239a4..00000000 --- a/acceptance/config/nodes/win2012-rubyx86.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2012-x86_64-rubyx86: - roles: - - agent - - mco_master - platform: windows-2012-64 - ruby_arch: x86 - hypervisor: vcloud - template: win-2012-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2012r2-rubyx64.yaml b/acceptance/config/nodes/win2012r2-rubyx64.yaml deleted file mode 100644 index a0e78a7f..00000000 --- a/acceptance/config/nodes/win2012r2-rubyx64.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2012r2-x86_64-rubyx64: - roles: - - agent - - mco_master - platform: windows-2012r2-64 - ruby_arch: x64 - hypervisor: vcloud - template: win-2012r2-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/config/nodes/win2012r2-rubyx86.yaml b/acceptance/config/nodes/win2012r2-rubyx86.yaml deleted file mode 100644 index 9f5810a4..00000000 --- a/acceptance/config/nodes/win2012r2-rubyx86.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -HOSTS: - master: - roles: - - master - platform: el-7-x86_64 - hypervisor: vcloud - template: redhat-7-x86_64 - agent-2012r2-x86_64-rubyx86: - roles: - - agent - - mco_master - platform: windows-2012r2-64 - ruby_arch: x86 - hypervisor: vcloud - template: win-2012r2-x86_64 -CONFIG: - datastore: instance0 - resourcepool: delivery/Quality Assurance/FOSS/Dynamic - folder: Delivery/Quality Assurance/FOSS/Dynamic - pooling_api: http://vmpooler.delivery.puppetlabs.net/ diff --git a/acceptance/files/activemq.keystore b/acceptance/files/activemq.keystore deleted file mode 100644 index 7872c98633c6dbbd45ad13c17160ac4f0c2c6b3f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3914 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 - - - - - - - file:${activemq.conf}/credentials.properties - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/acceptance/files/ca_crt.pem b/acceptance/files/ca_crt.pem deleted file mode 100644 index 2e084dce..00000000 --- a/acceptance/files/ca_crt.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFbTCCA1WgAwIBAgIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyMFoXDTIwMDMwMTExNTYy -MFowITEfMB0GA1UEAwwWUHVwcGV0IENBOiBzb2Nrcy5sb2NhbDCCAiIwDQYJKoZI -hvcNAQEBBQADggIPADCCAgoCggIBAL0qDiSB8u/6dxihgLSycKXGMeH+xzASvIWk -tsK6oPZ8vwBeEoz4A1iw6Lf7Xz9G9qUx0NlmLryN7HLFmidQ4aTwgYE/+/Q/xwa1 -FJaFl9yy49vU8/rfZ6m6/xNfBMP3MzDjqtlDYouo1A7h+VTN9sXMmltI9Qu8zwvT -0UYRy9J8wAMri7y0yVdiYZ4IjhzhwGaB4yrCZkFN6fsuoW3u2Yb1BtewffqXoav2 -R5G6m/tcgMzNkyJ9cG+GUM1bk5lEE8KOf360mHC8RsOu/7ZWLJmIUx5JKB+5xhGX -1nNd71mNB36f6JJ5ZlNSdVa4Yw5bv2HPyqk3BXbxfZlov/8ECfyEsW4LIANgTQbm -9Eep6wZE8inogDiGB5hMH1VgrbLjiNriScF3yeMVfdyzRdSp/OjyuE1CmOcBToIu -Bse7RDfpuJNDQs9ycqaa1ncFVA0sO6e06FszlznaioejELjCULT6qxVNgwTLTPqH -e1vTZe+iCtoVAIw0RFUhHltAZE4hUXAm+trgAy1FIpeG90TTtfHQEwpR9IgdzDaI -b2Oc4WeOG7pYxBRFz9NeESyBuKxXmCVfbprbtunpP/F0N4TgUc2zxdSZFN03GKL+ -jKYYDUcFq4Iune/uxdGudcUHfyx0XaRFmRAjtqwFoFzuebnuJuXKcCuh7x5OTB8i -4sF64VQJAgMBAAGjga8wgawwNQYJYIZIAYb4QgENBChQdXBwZXQgUnVieS9PcGVu -U1NMIEludGVybmFsIENlcnRpZmljYXRlMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB -Af8EBTADAQH/MB0GA1UdDgQWBBRqr/VzStqL/9HXVt6Qy5P7nhV46zAzBgNVHSME -LDAqoSWkIzAhMR8wHQYDVQQDDBZQdXBwZXQgQ0E6IHNvY2tzLmxvY2FsggEBMA0G -CSqGSIb3DQEBCwUAA4ICAQAWW4sT+13YO3E76jLgjhIjOmmQCSGJiJAHe8gxZxrZ -xbsybkImc+bR0DIJXMQzWpm183Wx0K3YKVD50zfoS5hTThU8OLuMHvXfmMwNy7Hi -0vwOaREyrfIzuCRhY3PUz7HXlncdmzAT/1Q9n07d7VCmsFvHtksSJUVcTAMsL9xv -D6fzO2or1ZtpJbckww6NmoOw+ofdn4vutn06do5SZbbPDfzEfdyPbeWFUXqFlCGk -/IO8RWjMt6XRBfc6z9HFqj4HI2n5t8gSVN8MTUo1vmzaR0rErpO+JGw8xm2XVvBW -jHwG8onTGErHYk+04M+woL8Q294SglpfvLONuJTBaKGaCtVnQvhDMvITJ7rnrBz3 -r1x1Fx635ofY+FGpVMX0EGLA79gaya6zeyBIlvtUcTN89UfQ0sCEClme9Oe48Scy -bSJKsJNsNqEljCWQ8sqlc4zGXGk2tysAHWBEPoPcfPeIyWKNx0KQ5dz8zf+adV+3 -JIZC70qF3fEnyhpv0Z6B7VxRR/EZUVAyLIkucQUNVcMR7uTuAWv67CciCS9EOfit -eYNnpfGRXYoiEgaSnv6oOCtgDSXd6nJgtfe9m4K7200KNPFJIWREFm21UCseHsRc -AC+KbxNdwopBY8IFS2PLJQPQfBnf4q5uPZagBg4r0mHH5TgiESpa15fI25fjxAHB -Zw== ------END CERTIFICATE----- diff --git a/acceptance/files/client.cfg b/acceptance/files/client.cfg deleted file mode 100644 index 086717ea..00000000 --- a/acceptance/files/client.cfg +++ /dev/null @@ -1,24 +0,0 @@ -main_collective = mcollective -collectives = mcollective -libdir = /opt/puppetlabs/mcollective/plugins -logger_type = console -loglevel = warn - -# Plugins -securityprovider = ssl -plugin.ssl_server_public = /etc/puppetlabs/mcollective/server.crt -plugin.ssl_client_private = /etc/puppetlabs/mcollective/client.key -plugin.ssl_client_public = /etc/puppetlabs/mcollective/client.pem - -connector = activemq -plugin.activemq.pool.size = 1 -plugin.activemq.pool.1.host = activemq -plugin.activemq.pool.1.port = 61613 -plugin.activemq.pool.1.user = mcollective -plugin.activemq.pool.1.password = marionette -plugin.activemq.pool.1.ssl = true -plugin.activemq.pool.1.ssl.ca = /etc/puppetlabs/mcollective/ca_crt.pem -plugin.activemq.pool.1.ssl.cert = /etc/puppetlabs/mcollective/client.crt -plugin.activemq.pool.1.ssl.key = /etc/puppetlabs/mcollective/client.key - -connection_timeout = 3 diff --git a/acceptance/files/client.crt b/acceptance/files/client.crt deleted file mode 100644 index ec630c8c..00000000 --- a/acceptance/files/client.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIBBDANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyOVoXDTIwMDMwMTExNTYy -OVowHTEbMBkGA1UEAwwSbWNvbGxlY3RpdmUtY2xpZW50MIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAopc/qeMD7V4sQSeLjI71Na9gabTXC5dxDV5T/qoM -ZOGVInuMUVEjWWYM/Pjsj+/rEqjcN+0N8sZHpY9sMkbqB97I9c0A/zdVskF8EGVe -7vS/ytUIUtKwONffD0/FEPNpIDUc++rSj5PPgJvn35ObhB7Q2MpXLx6iamD1gnyF -fmrssnv6/f5esjV/n61YaskLd1zQD6t7Zghdkzjy6pFz/AfGqWdzq4r2D5ANnsTO -WAH1qWyphv8Jbwf7vE+H8x7jySmwrxmHu/PDiqZMz28ZRTVjWBWY67hI7Z/p1otQ -kzoFFf7XLNfNuyajcsG1YCaGXwbBHdb5aa+leLKLzPd1Xy1X0/h4MEG63xFzkrml -B7PQZHbzVHdi1lI4tI6erUyb3RjsIT2EE8ED732iDIPZduDg5I1yOUKa7R8wEYVE -RyHv7V6Hbo2ydJu/oIMwJZ0oGlzK1T1xpEsg8t7wgcLKUY7n/+zG23dLOh49/ubm -+ecC3QbN1z3pMc9pOceTwkWCi6vGf0Otd1TrraHAlK22dzkfQqFK2mKMegoQZXSW -HPd5JAdSy4qnXCJPxGJaStQGDii5SndFWiuduKlQpSE6WSgMdiBwncAFmdYsnlNM -gY0wHL/ufjWhsVdePVrEagNux6PdLdmolWawoHxtNh4UuwyByiyJU8gF7A6f/DoA -H8cCAwEAAaOBujCBtzA1BglghkgBhvhCAQ0EKFB1cHBldCBSdWJ5L09wZW5TU0wg -SW50ZXJuYWwgQ2VydGlmaWNhdGUwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQW -MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQV -Bygt19XIerwYMgLimQ1LAQjwATAfBgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7 -nhV46zANBgkqhkiG9w0BAQsFAAOCAgEANw3dHIu3J71utQB8eiCwfhNKxBhM8Yj3 -i+eeAvyo28ZN3knyCbXRQmaegVRbeRLHi3YC+9yG0MFTBeOasxVPK508WosaaQJA -yOuP/++IYbPiqB/xZArAbe5lA/bmp7xekS/hB9PA0hp6eS2tj13GQBOfJ+tNDEFf -su8H1w+OIA626LfR1PwaqkKCTQBHEtX+Cv8KyvDeYLt0cP2tMi4MQbqdqO093AHO -4o8ZdyBOJUwzjYzt+4Y3M5Lh7i/WXh4XxnH59F0RfhVmztf2cSrjRKUvop+IDP7b -b1bx4vtnorFSFqcngb9jg6Om21+x1Akhew26ZsvlazSWwAxCrlPOSCPy/taW680K -gA02sbAonkanLe44E3UE5puugWz8ImDp28gOGt1PWiCyNxtJcJ3j3OTw/L5jur8Q -OEZNhTrZ5j7WdZrzEFMiu6K7C2c0RL2Gz1TWd4MocGTe/vs+0ebbh6D1w9EGci8V -+FBIavrOc9ZmI03wGIHLnHD+QgxLeTvQOnFENr0k9ah320b8UHdpbbvr3pRf8an5 -Q8GdVahd5gTY1CbkZGeaIlY8OWUG7s9mvGGDEswSsLIah0UjTNDpzexw1ibr+LDZ -tSH7GPdtwSniix47ywtv/qzVa8erCpJQ6rucUThR7o4jn3BcZJm0ZDGzZhWhpxjt -cCoGKqzr1xk= ------END CERTIFICATE----- diff --git a/acceptance/files/client.key b/acceptance/files/client.key deleted file mode 100644 index 00e37304..00000000 --- a/acceptance/files/client.key +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -MIIJKQIBAAKCAgEAopc/qeMD7V4sQSeLjI71Na9gabTXC5dxDV5T/qoMZOGVInuM -UVEjWWYM/Pjsj+/rEqjcN+0N8sZHpY9sMkbqB97I9c0A/zdVskF8EGVe7vS/ytUI -UtKwONffD0/FEPNpIDUc++rSj5PPgJvn35ObhB7Q2MpXLx6iamD1gnyFfmrssnv6 -/f5esjV/n61YaskLd1zQD6t7Zghdkzjy6pFz/AfGqWdzq4r2D5ANnsTOWAH1qWyp -hv8Jbwf7vE+H8x7jySmwrxmHu/PDiqZMz28ZRTVjWBWY67hI7Z/p1otQkzoFFf7X -LNfNuyajcsG1YCaGXwbBHdb5aa+leLKLzPd1Xy1X0/h4MEG63xFzkrmlB7PQZHbz -VHdi1lI4tI6erUyb3RjsIT2EE8ED732iDIPZduDg5I1yOUKa7R8wEYVERyHv7V6H -bo2ydJu/oIMwJZ0oGlzK1T1xpEsg8t7wgcLKUY7n/+zG23dLOh49/ubm+ecC3QbN -1z3pMc9pOceTwkWCi6vGf0Otd1TrraHAlK22dzkfQqFK2mKMegoQZXSWHPd5JAdS -y4qnXCJPxGJaStQGDii5SndFWiuduKlQpSE6WSgMdiBwncAFmdYsnlNMgY0wHL/u -fjWhsVdePVrEagNux6PdLdmolWawoHxtNh4UuwyByiyJU8gF7A6f/DoAH8cCAwEA -AQKCAgEAnMXqBQ14Q/CCC3DuBeHyXol0FXjhwbOMtq81nmCpArzg6Bbo/Z+WziSw -cVwz/bYAnEVl0icpQ+YcP29DjFcEYgJXE2tQgsYAQ5kQ315fY1lrdVBkbjfo42aD -Fmh8RBPwUbXk9KM/1GFZu1CZC+NwGLz984tm9XA8ewZytBcNQRAomxEgurgC5yLe -pECo+I5SGA3OMM3QdlmqoMJQuAz4IRP+Ymn2Bno/OFJWT7jEneeF0I+OBzTzC7RO -7wxgsfQe/2DLlIqxbnFlItOnTHx7iVgpKk3o3aEb8MQ43T4Hu24+aS8Im5yaKtLe -11Aga026UvZYT0VxyI4sp2zBkCkrackFA0GxprFK2QohopsW05iJVzqUCHyIEPyc -YRjtw2xpe4yqK2Kiiv9qgWLjoDrO6AjdJ5gcBfAmZokysqSZsfdVl63uCl6du54I -OQ4vIIVC7/VdrgiMUhCu5sDNbBq0K+MKZC93KSHrif8a/bCKnjpWbPoQXYAfkxXc -PdQe6DDivdKj2qRjUfZEmrT6jIz7lrVjHu8rpB3Z2rBxqBj0fMEiNGU2lrrZ/yX7 -UhoECfOYKMw5LDe0BltOkgjm4gizM2KJkAPthujAJuFw77bwF5IrEMxYl0biywd7 -+LUA6Ga2ckLieNbGJ406mBDg7mO3brHVK6Oa5R2GQpqgJBnOIAECggEBAMzZ4svY -5NeVyu3IgSXsv2Ou/lNQM3Aa3C6Q0kvqK4W1K9mlzgD7UM8fbLVzacezM14OKuPp -3DR227sKhOcnKF4B61q3SvGx9Vh5CXL6aX5Jmkbv0iiKwu+gY1pwbpiKRP/L9iAK -yfZBT7r4qE993W+5KRxSA6bTbdNgkxHd6IijiWsQ3b7dCs9mRlHMvAGGTouY4POV -oy6pik5vjIHytE3GRPFuQo94Q0yRkQm29Wfv0pKm9Bjc5ItjYKWr6njfRxQL6P2T -IkYRoYgYVAbDcRcHx/3PcPJt6xwdUV61HTkF5lnh16IZ4z2/f/emeUaLP1+hfuQS -5lyDKTnVs7d5kgECggEBAMswE/RfWYh5PF8QOxs/yl+ia7ro9Vaubrp/rGAB8ny6 -pgI5ntwoYuBN95APxP9vNIn27Kcm8w9zvCgesgVJdjN73eWVo6vqF+UL9zIDDVpC -2bILe30nEoB6JViqixxgwXARinTkalVMwoOHuabmowqJStfsjCBAoUCU98vLUgJP -5SjyYHCyQfY++XijG+QgMtmw8x8hKEGJ+JqNqLZfAJtQMg4q0IjoA/71GfLASpla -mSqHsG4obURcQSr8Bww4Ntbbwa5+ai1G13fSsHxOq0Ve/ksDEWm6dYX+QWD5sY1j -TT0QUqWffZxoNZIPXpwTNljS0nVjPP+8lBf2l3utoccCggEANMGQZQswtEzBfEd8 -6zVzfMqeePpYpPBl8CAf6KkgGEk2R9EpopsWjqD7MCfPAD0oZ3qilRuvOFu77wmg -fJ9bCAraf/xgcqpwEx7ozhGrhXiTIN22c/Z4iZn2vF3kPaiuaGowssPUi3T6gZ50 -SjSSCKnY6pA8nIQq7psahSlvL3FefJJVaUgn9o3SYlKlwIbER7NRi6nn4qaLFfQo -bXcB9F2xd7P7tFM2kcuTXLsfrGrZAie5CYYp3bP0OfZyZjqqZYFDubpgw0bbIN+T -FELVJyc73CPGZMjmGdF7GTbvlbXtQwykqfycx1RAMplO9ln6Mji3Iymy5fRpc5mT -tRa8AQKCAQEAyajJ5TvNDJnmxSLCxuSk4g29hiA3fvRYiVi1qAPGuuw0Xvj5JeAf -Yid/kMdV6X5hUpxze+I4Uhm3oMn2PTEP00EYlgfSDYmkdXtOt6A33GGE1iR3R1tE -Dacs8bcxodSVp7iBBkq9DjPEye4m3/L/1jE0yuDGoiwC3qn61ZTRq+HHur/z32XL -+a2+w3B++gbK8Suh4D90SLe6uQnnbDkVzQ/m3hC4L2i2rpBsVHVslI9KfN92x53v -9GzrcNH53LLeGpi4vYpRruYka5P7/SPG8G67S4+b9KdOhNI36RtokL8BpZ1RqXeF -n3n90RzX9WdMBRQYNNFtdROgl7fx2JoXeQKCAQAuwhhzlDBTm7x3J8OGkPVYHCFE -rrkSooAbQUH7EK3Ns5ZeDrDNQ/193zELtCBnJ2PSlpcNIry9jI0NkdBniBzvQ8Kn -CkSmw73HRVfg+BIP5CPj2L8jDdbiZlu4Y7cn1u5Q5uz/A8Q8/7VxwaC/hCzFJ3GP -kSbpvlAxpoQ5QofK0EW/c/5EXYG1soITKvv6+Z4b/yZg91bPFSVTRFdHLVHSs2Q7 -cz+QquxFfkS2jhouXtgxQNA5fy45EHjZgnUf3nFWx8eohAEZd4a+KkDmFW3foDS6 -Yo3ydTc1R68U0ssdwbLW28OJuXIfUHWMavLY1OOJ5OBYWL1ML9uuJFmCy8/H ------END RSA PRIVATE KEY----- diff --git a/acceptance/files/server.crt b/acceptance/files/server.crt deleted file mode 100644 index 7b26714e..00000000 --- a/acceptance/files/server.crt +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIBBTANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjEyMTgzNVoXDTIwMDMwMTEyMTgz -NVowHTEbMBkGA1UEAwwSbWNvbGxlY3RpdmUtc2VydmVyMIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAmb8vabpZIw3WHUXqV6HkOjmKXz5IgjhXBg5/XA6A -TGrvFlo5mj6W2JpE7CxI+DhHYZ4+Z5NLkzIAYoSXuV/0umuPpXOAH5ma5JYudzKd -5RV7RiffSj5LfzBnGZwn3dVVhaZiR6vUM4cmP8CqUqfoxvpweh9nGDbkEKoHdC4p -ENerwvuheAnPGflonDZFxaWEqXn5oXxSiuf88XZ0tgn1mMBn/JF0rXtCKYFfOAr7 -Evw1eUak+4wEHym26q+BCogKpeW+lT0C/v/TH5XG63ycyhmqiWjTI2vPab6BC7t2 -Bmgd5Grcr7cofvt4QYwIsIwL6ZZWahyLynHMBs85AAm1bfPdnMeia5WP+J98euAJ -7MK8TuX4sEjHt/yAiXT75aD0rsViP9BkKstBudMALokywnMHLa0/KdJCwXP5JH/Z -D8v4LJvpfGxDodC+tyUCsr82Wn67AAculvMZDjY6SD9LtaNrtiNZeNj2PXYBYWWs -CQ4aR2LzelDPDZ3TUu0BqMjNQ07zz/Daol6DASuF1TLUv6YW2tLZ5nggt1rkARx6 -m2BTEpa1Jl6j8KkE2l+7KR6EaUCwz3bqlvAweqY/8mnHrwhXkeeQ6Bta53o5YjXD -WFXTmZD/iSlT8hbnWmoww/EgRjsQyXZ9dS3OelsAPYBTKTwXsKIGdVgj4t8Xd9CK -gicCAwEAAaOBujCBtzA1BglghkgBhvhCAQ0EKFB1cHBldCBSdWJ5L09wZW5TU0wg -SW50ZXJuYWwgQ2VydGlmaWNhdGUwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQW -MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRm -iqXTvKMtefbQHDRT3F9MVe9xtjAfBgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7 -nhV46zANBgkqhkiG9w0BAQsFAAOCAgEAWSNWAshqJtEeV1vfdlU581ppNmodUs7Q -6Ypdx0eot6S9lvTl69GFYonuOvwf8U6chs5Hp051oMB8M1PWzD7CZHj+wMwRKqvG -PWymZUsZQHFpb01tnABLL62fyqxEnaVPqUyRwNQMsXoD6OiW495kY+o17i2k6oYF -pE2ofyREQd40i3wGk7u0MlpWvSfdpcUgzVTTlruOuHh+E2HTf53PH3AuFDgqd8Ee -+2JCO2hcAT7JXfmlxzE6XtbalnaorBnJD09vRR/MshppN6l/11+yMDg3CpfkARBJ -OqSVLd8PD3BZm4jUWd3w7tBMs1WUYrtMtUKVGc2Ct4QyxCpi1bKKZRcrnROo3lLH -ZZGEYo+19KpCff/kOoBiyqkim8SN9cdy5nzEllGsIj+72mJuqRhkh58tlrTBUDl1 -8Sc1rRLZ+T6k2A/UWybkPMVFw+e1DFOtK8QvjwXPiZyNTDmf05uesgp8sJ81iebv -1llZu24x5gVobMHEeXGmKGGs6vquwTrs0/mAy4ujHhkEXZPRkrdv1uBt0sG969/5 -0Bnk+Lq0xxGDbgTt+8TOpV++cE3dU6K3Fb7JCJT8S6dzd/78+T+m13maW6WKdiZc -QAzFNkiw4D0qvdCoL8bu45P58tPFGdJtRbIQ83Ik1Ie6M8nXxCcq0qIttw83Od+H -qDxqCwAZL8E= ------END CERTIFICATE----- diff --git a/acceptance/files/server.key b/acceptance/files/server.key deleted file mode 100644 index 98bee06d..00000000 --- a/acceptance/files/server.key +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/acceptance/files/windows-client.cfg b/acceptance/files/windows-client.cfg deleted file mode 100644 index d0abb42d..00000000 --- a/acceptance/files/windows-client.cfg +++ /dev/null @@ -1,24 +0,0 @@ -main_collective = mcollective -collectives = mcollective -libdir = C:\ProgramData\PuppetLabs\mcollective\etc\plugins -logger_type = console -loglevel = warn - -# Plugins -securityprovider = ssl -plugin.ssl_server_public = C:\ProgramData\PuppetLabs\mcollective\etc\server.crt -plugin.ssl_client_private = C:\ProgramData\PuppetLabs\mcollective\etc\client.key -plugin.ssl_client_public = C:\ProgramData\PuppetLabs\mcollective\etc\client.pem - -connector = activemq -plugin.activemq.pool.size = 1 -plugin.activemq.pool.1.host = activemq -plugin.activemq.pool.1.port = 61613 -plugin.activemq.pool.1.user = mcollective -plugin.activemq.pool.1.password = marionette -plugin.activemq.pool.1.ssl = true -plugin.activemq.pool.1.ssl.ca = C:\ProgramData\PuppetLabs\mcollective\etc\ca_crt.pem -plugin.activemq.pool.1.ssl.cert = C:\ProgramData\PuppetLabs\mcollective\etc\client.crt -plugin.activemq.pool.1.ssl.key = C:\ProgramData\PuppetLabs\mcollective\etc\client.key - -connection_timeout = 3 diff --git a/acceptance/lib/acceptance_spec_helper.rb b/acceptance/lib/acceptance_spec_helper.rb deleted file mode 100644 index d279a042..00000000 --- a/acceptance/lib/acceptance_spec_helper.rb +++ /dev/null @@ -1,8 +0,0 @@ -require 'fileutils' - -dir = File.expand_path(File.dirname(__FILE__)) -$LOAD_PATH.unshift dir - -RSpec.configure do |config| - config.mock_with :mocha -end diff --git a/acceptance/lib/helper.rb b/acceptance/lib/helper.rb deleted file mode 100644 index c2b5df87..00000000 --- a/acceptance/lib/helper.rb +++ /dev/null @@ -1 +0,0 @@ -$LOAD_PATH << File.expand_path(File.dirname(__FILE__)) diff --git a/acceptance/lib/puppet/acceptance/common_utils.rb b/acceptance/lib/puppet/acceptance/common_utils.rb deleted file mode 100644 index be437c23..00000000 --- a/acceptance/lib/puppet/acceptance/common_utils.rb +++ /dev/null @@ -1,144 +0,0 @@ -module Puppet - module Acceptance - module CronUtils - def clean(agent, o={}) - o = {:user => 'tstuser'}.merge(o) - run_cron_on(agent, :remove, o[:user]) - apply_manifest_on(agent, %[user { '%s': ensure => absent, managehome => false }] % o[:user]) - end - - def setup(agent, o={}) - o = {:user => 'tstuser'}.merge(o) - apply_manifest_on(agent, %[user { '%s': ensure => present, managehome => false }] % o[:user]) - apply_manifest_on(agent, %[case $operatingsystem { - centos, redhat: {$cron = 'cronie'} - solaris: { $cron = 'core-os' } - default: {$cron ='cron'} } - package {'cron': name=> $cron, ensure=>present, }]) - end - end - - module CAUtils - - def initialize_ssl - hostname = on(master, 'facter hostname').stdout.strip - fqdn = on(master, 'facter fqdn').stdout.strip - - if master.use_service_scripts? - step "Ensure puppet is stopped" - # Passenger, in particular, must be shutdown for the cert setup steps to work, - # but any running puppet master will interfere with webrick starting up and - # potentially ignore the puppet.conf changes. - on(master, puppet('resource', 'service', master['puppetservice'], "ensure=stopped")) - end - - step "Clear SSL on all hosts" - hosts.each do |host| - ssldir = on(host, puppet('agent --configprint ssldir')).stdout.chomp - on(host, "rm -rf '#{ssldir}'") - end - - step "Master: Start Puppet Master" do - master_opts = { - :main => { - :dns_alt_names => "puppet,#{hostname},#{fqdn}", - }, - :__service_args__ => { - # apache2 service scripts can't restart if we've removed the ssl dir - :bypass_service_script => true, - }, - } - with_puppet_running_on(master, master_opts) do - - hosts.each do |host| - next if host['roles'].include? 'master' - - step "Agents: Run agent --test first time to gen CSR" - on host, puppet("agent --test --server #{master}"), :acceptable_exit_codes => [1] - end - - # Sign all waiting certs - step "Master: sign all certs" - on master, puppet("cert --sign --all"), :acceptable_exit_codes => [0,24] - - step "Agents: Run agent --test second time to obtain signed cert" - on agents, puppet("agent --test --server #{master}"), :acceptable_exit_codes => [0,2] - end - end - end - - def clean_cert(host, cn, check = true) - if host == master && master[:is_puppetserver] - on master, puppet_resource("service", master['puppetservice'], "ensure=stopped") - end - - on(host, puppet('cert', 'clean', cn), :acceptable_exit_codes => check ? [0] : [0, 24]) - if check - assert_match(/remov.*Certificate.*#{cn}/i, stdout, "Should see a log message that certificate request was removed.") - on(host, puppet('cert', 'list', '--all')) - assert_no_match(/#{cn}/, stdout, "Should not see certificate in list anymore.") - end - end - - def clear_agent_ssl - return if master.is_pe? - step "All: Clear agent only ssl settings (do not clear master)" - hosts.each do |host| - next if host == master - ssldir = on(host, puppet('agent --configprint ssldir')).stdout.chomp - on( host, host_command("rm -rf '#{ssldir}'") ) - end - end - - def reset_agent_ssl(resign = true) - return if master.is_pe? - clear_agent_ssl - - hostname = master.execute('facter hostname') - fqdn = master.execute('facter fqdn') - - step "Clear old agent certificates from master" do - agents.each do |agent| - next if agent == master && agent.is_using_passenger? - agent_cn = on(agent, puppet('agent --configprint certname')).stdout.chomp - clean_cert(master, agent_cn, false) if agent_cn - end - end - - if resign - step "Master: Ensure the master is listening and autosigning" - with_puppet_running_on(master, - :master => { - :dns_alt_names => "puppet,#{hostname},#{fqdn}", - :autosign => true, - } - ) do - - agents.each do |agent| - next if agent == master && agent.is_using_passenger? - step "Agents: Run agent --test once to obtain auto-signed cert" do - on agent, puppet('agent', "--test --server #{master}"), :acceptable_exit_codes => [0,2] - end - end - end - end - end - end - - module CommandUtils - def ruby_command(host) - "env PATH=\"#{host['privatebindir']}:${PATH}\" ruby" - end - module_function :ruby_command - - def gem_command(host) - if host['platform'] =~ /windows/ - "env PATH=\"#{host['privatebindir']}:${PATH}\" cmd /c gem" - else - "env PATH=\"#{host['privatebindir']}:${PATH}\" gem" - end - end - module_function :gem_command - end - end -end diff --git a/acceptance/lib/puppet/acceptance/git_utils.rb b/acceptance/lib/puppet/acceptance/git_utils.rb deleted file mode 100644 index 6f27f6b6..00000000 --- a/acceptance/lib/puppet/acceptance/git_utils.rb +++ /dev/null @@ -1,19 +0,0 @@ -module Puppet - module Acceptance - module GitUtils - def lookup_in_env(env_variable_name, project_name, default) - project_specific_name = "#{project_name.upcase.gsub("-","_")}_#{env_variable_name}" - ENV[project_specific_name] || ENV[env_variable_name] || default - end - - def build_giturl(project_name, git_fork = nil, git_server = nil) - git_fork ||= lookup_in_env('FORK', project_name, 'puppetlabs') - git_server ||= lookup_in_env('GIT_SERVER', project_name, 'github.com') - repo = (git_server == 'github.com') ? - "#{git_fork}/#{project_name}.git" : - "#{git_fork}-#{project_name}.git" - "git://#{git_server}/#{repo}" - end - end - end -end diff --git a/acceptance/lib/puppet/acceptance/install_utils.rb b/acceptance/lib/puppet/acceptance/install_utils.rb deleted file mode 100644 index 862dbb40..00000000 --- a/acceptance/lib/puppet/acceptance/install_utils.rb +++ /dev/null @@ -1,226 +0,0 @@ -require 'open-uri' -require 'open3' -require 'uri' -require 'puppet/acceptance/common_utils' - -module Puppet - module Acceptance - module InstallUtils - PLATFORM_PATTERNS = { - :redhat => /fedora|el|centos/, - :debian => /debian|ubuntu/, - :debian_ruby18 => /debian|ubuntu-lucid|ubuntu-precise/, - :solaris_10 => /solaris-10/, - :solaris_11 => /solaris-11/, - :windows => /windows/, - }.freeze - - # Installs packages on the hosts. - # - # @param hosts [Array] Array of hosts to install packages to. - # @param package_hash [Hash{Symbol=>Array>}] - # Keys should be a symbol for a platform in PLATFORM_PATTERNS. Values - # should be an array of package names to install, or of two element - # arrays where a[0] is the command we expect to find on the platform - # and a[1] is the package name (when they are different). - # @param options [Hash{Symbol=>Boolean}] - # @option options [Boolean] :check_if_exists First check to see if - # command is present before installing package. (Default false) - # @return true - def install_packages_on(hosts, package_hash, options = {}) - check_if_exists = options[:check_if_exists] - hosts = [hosts] unless hosts.kind_of?(Array) - hosts.each do |host| - package_hash.each do |platform_key,package_list| - if pattern = PLATFORM_PATTERNS[platform_key] - if pattern.match(host['platform']) - package_list.each do |cmd_pkg| - if cmd_pkg.kind_of?(Array) - command, package = cmd_pkg - else - command = package = cmd_pkg - end - if !check_if_exists || !host.check_for_package(command) - host.logger.notify("Installing #{package}") - additional_switches = '--allow-unauthenticated' if platform_key == :debian - host.install_package(package, additional_switches) - end - end - end - else - raise("Unknown platform '#{platform_key}' in package_hash") - end - end - end - return true - end - - def fetch(base_url, file_name, dst_dir) - FileUtils.makedirs(dst_dir) - src = "#{base_url}/#{file_name}" - dst = File.join(dst_dir, file_name) - if File.exists?(dst) - logger.notify "Already fetched #{dst}" - else - logger.notify "Fetching: #{src}" - logger.notify " and saving to #{dst}" - open(src) do |remote| - File.open(dst, "w") do |file| - FileUtils.copy_stream(remote, file) - end - end - end - return dst - end - - def fetch_remote_dir(url, dst_dir) - logger.notify "fetch_remote_dir (url: #{url}, dst_dir #{dst_dir})" - if url[-1, 1] !~ /\// - url += '/' - end - url = URI.parse(url) - chunks = url.path.split('/') - dst = File.join(dst_dir, chunks.last) - #determine directory structure to cut - #only want to keep the last directory, thus cut total number of dirs - 2 (hostname + last dir name) - cut = chunks.length - 2 - wget_command = "wget -nv -P #{dst_dir} --reject \"index.html*\",\"*.gif\" --cut-dirs=#{cut} -np -nH --no-check-certificate -r #{url}" - - logger.notify "Fetching remote directory: #{url}" - logger.notify " and saving to #{dst}" - logger.notify " using command: #{wget_command}" - - #in ruby 1.9+ we can upgrade this to popen3 to gain access to the subprocess pid - result = `#{wget_command} 2>&1` - result.each_line do |line| - logger.debug(line) - end - if $?.to_i != 0 - raise "Failed to fetch_remote_dir '#{url}' (exit code #{$?}" - end - dst - end - - def stop_firewall_on(host) - case host['platform'] - when /debian/ - on host, 'iptables -F' - when /fedora|el-7/ - on host, puppet('resource', 'service', 'firewalld', 'ensure=stopped') - when /el|centos/ - on host, puppet('resource', 'service', 'iptables', 'ensure=stopped') - when /ubuntu/ - on host, puppet('resource', 'service', 'ufw', 'ensure=stopped') - else - logger.notify("Not sure how to clear firewall on #{host['platform']}") - end - end - - def install_repos_on(host, project, sha, repo_configs_dir) - platform = host['platform'].with_version_codename - platform_configs_dir = File.join(repo_configs_dir,platform) - tld = sha == 'nightly' ? 'ravi.puppetlabs.com' : 'builds.puppetlabs.lan' - project = sha == 'nightly' ? project + '-latest' : project - sha = sha == 'nightly' ? nil : sha - - case platform - when /^(fedora|el|centos)-(\d+)-(.+)$/ - variant = (($1 == 'centos') ? 'el' : $1) - fedora_prefix = ((variant == 'fedora') ? 'f' : '') - version = $2 - arch = $3 - - repo_filename = "pl-%s%s-%s-%s%s-%s.repo" % [ - project, - sha ? '-' + sha : '', - variant, - fedora_prefix, - version, - arch - ] - repo_url = "http://%s/%s/%s/repo_configs/rpm/%s" % [tld, project, sha, repo_filename] - - on host, "curl -o /etc/yum.repos.d/#{repo_filename} #{repo_url}" - when /^(debian|ubuntu)-([^-]+)-(.+)$/ - variant = $1 - version = $2 - arch = $3 - - list_filename = "pl-%s%s-%s.list" % [ - project, - sha ? '-' + sha : '', - version - ] - list_url = "http://%s/%s/%s/repo_configs/deb/%s" % [tld, project, sha, list_filename] - - on host, "curl -o /etc/apt/sources.list.d/#{list_filename} #{list_url}" - on host, "apt-get update" - else - host.logger.notify("No repository installation step for #{platform} yet...") - end - end - - # Configures gem sources on hosts to use a mirror, if specified - # This is a duplicate of the Gemfile logic. - def configure_gem_mirror(hosts) - hosts = [hosts] unless hosts.kind_of?(Array) - gem_source = ENV['GEM_SOURCE'] || 'https://rubygems.org' - - hosts.each do |host| - gem = Puppet::Acceptance::CommandUtils.gem_command(host) - gem_version = on(host, "#{gem} --version").stdout.chomp - if host['platform'] =~ /win/ && gem_version < '2.6.8' then - # The vendored gem command does not have an updated - # TLS cert on Windows. - # http://guides.rubygems.org/ssl-certificate-update - geotrust_ca = <<-EOS ------BEGIN CERTIFICATE----- -MIIDdTCCAl2gAwIBAgILBAAAAAABFUtaw5QwDQYJKoZIhvcNAQEFBQAwVzELMAkG -A1UEBhMCQkUxGTAXBgNVBAoTEEdsb2JhbFNpZ24gbnYtc2ExEDAOBgNVBAsTB1Jv -b3QgQ0ExGzAZBgNVBAMTEkdsb2JhbFNpZ24gUm9vdCBDQTAeFw05ODA5MDExMjAw -MDBaFw0yODAxMjgxMjAwMDBaMFcxCzAJBgNVBAYTAkJFMRkwFwYDVQQKExBHbG9i -YWxTaWduIG52LXNhMRAwDgYDVQQLEwdSb290IENBMRswGQYDVQQDExJHbG9iYWxT -aWduIFJvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDaDuaZ -jc6j40+Kfvvxi4Mla+pIH/EqsLmVEQS98GPR4mdmzxzdzxtIK+6NiY6arymAZavp -xy0Sy6scTHAHoT0KMM0VjU/43dSMUBUc71DuxC73/OlS8pF94G3VNTCOXkNz8kHp -1Wrjsok6Vjk4bwY8iGlbKk3Fp1S4bInMm/k8yuX9ifUSPJJ4ltbcdG6TRGHRjcdG -snUOhugZitVtbNV4FpWi6cgKOOvyJBNPc1STE4U6G7weNLWLBYy5d4ux2x8gkasJ -U26Qzns3dLlwR5EiUWMWea6xrkEmCMgZK9FGqkjWZCrXgzT/LCrBbBlDSgeF59N8 -9iFo7+ryUp9/k5DPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8E -BTADAQH/MB0GA1UdDgQWBBRge2YaRQ2XyolQL30EzTSo//z9SzANBgkqhkiG9w0B -AQUFAAOCAQEA1nPnfE920I2/7LqivjTFKDK1fPxsnCwrvQmeU79rXqoRSLblCKOz -yj1hTdNGCbM+w6DjY1Ub8rrvrTnhQ7k4o+YviiY776BQVvnGCv04zcQLcFGUl5gE -38NflNUVyRRBnMRddWQVDf9VMOyGj/8N7yy5Y0b2qvzfvGn9LhJIZJrglfCm7ymP -AbEVtQwdpf5pLGkkeB6zpxxxYu7KyJesF12KwvhHhm4qxFYxldBniYUr+WymXUad -DKqC5JlR3XC321Y9YeRq4VzW9v493kHMB65jUr9TU/Qr6cf9tveCX4XSQRjbgbME -HMUfpIBvFSDJ3gyICh3WZlXi/EjJKSZp4A== ------END CERTIFICATE----- -EOS - p = on(host, "#{gem} which rubygems").stdout.chomp.sub('rubygems.rb', 'rubygems/ssl_certs') - create_remote_file(host, "#{p}/geotrustglobal.pem", geotrust_ca) - end - on host, "#{gem} source --clear-all" - on host, "#{gem} source --add #{gem_source}" - end - end - - def install_puppet_from_msi( host, opts ) - if not link_exists?(opts[:url]) - raise "Puppet does not exist at #{opts[:url]}!" - end - - # `start /w` blocks until installation is complete, but needs to be wrapped in `cmd.exe /c` - on host, "cmd.exe /c start /w msiexec /qn /i #{opts[:url]} /L*V C:\\\\Windows\\\\Temp\\\\Puppet-Install.log" - - # make sure the background service isn't running while the test executes - on host, "net stop puppet" - - # make sure install is sane, beaker has already added puppet and ruby - # to PATH in ~/.ssh/environment - on host, puppet('--version') - ruby = Puppet::Acceptance::CommandUtils.ruby_command(host) - on host, "#{ruby} --version" - end - end - end -end diff --git a/acceptance/setup/aio/pre-suite/010_Install.rb b/acceptance/setup/aio/pre-suite/010_Install.rb deleted file mode 100644 index c038f641..00000000 --- a/acceptance/setup/aio/pre-suite/010_Install.rb +++ /dev/null @@ -1,79 +0,0 @@ -require 'puppet/acceptance/install_utils' - -extend Puppet::Acceptance::InstallUtils - -test_name "Install Packages" - -step "Install repositories on target machines..." do - - sha = ENV['SHA'] - repo_configs_dir = 'repo-configs' - - hosts.each do |host| - install_repos_on(host, 'puppet-agent', sha, repo_configs_dir) - end - - if master['passenger'] - passenger_version = ENV['PASSENGER_VERSION'] || '3518347c3480172fcef41406cad31b7ed34cd14f' - install_repos_on(master, 'puppet-master-passenger', passenger_version, repo_configs_dir) - else - server_version = ENV['SERVER_VERSION'] || 'nightly' - install_repos_on(master, 'puppetserver', server_version, repo_configs_dir) - end -end - -if master['passenger'] - MASTER_PACKAGES = { - :redhat => [ - 'puppet-master-passenger', - ], - :debian => [ - 'puppet-master-passenger', - ], - } -else - MASTER_PACKAGES = { - :redhat => [ - 'puppetserver', - ], - :debian => [ - 'puppetserver', - ], -# :solaris => [ -# 'puppet-server', -# ], -# :windows => [ -# 'puppet-server', -# ], - } -end - -AGENT_PACKAGES = { - :redhat => [ - 'puppet-agent', - ], - :debian => [ - 'puppet-agent', - ], -# :solaris => [ -# 'puppet', -# ], -# :windows => [ -# 'puppet', -# ], -} - -install_packages_on(master, MASTER_PACKAGES) -install_packages_on(agents, AGENT_PACKAGES) - -agents.each do |agent| - if agent['platform'] =~ /windows/ - arch = agent[:ruby_arch] || 'x86' - base_url = ENV['MSI_BASE_URL'] || "http://builds.puppetlabs.lan/puppet-agent/#{ENV['SHA']}/artifacts/windows" - filename = ENV['MSI_FILENAME'] || "puppet-agent-#{arch}.msi" - - install_puppet_from_msi(agent, :url => "#{base_url}/#{filename}") - end -end - -configure_gem_mirror(hosts) diff --git a/acceptance/setup/aio/pre-suite/015_PackageHostsPresets.rb b/acceptance/setup/aio/pre-suite/015_PackageHostsPresets.rb deleted file mode 100644 index 6476bbed..00000000 --- a/acceptance/setup/aio/pre-suite/015_PackageHostsPresets.rb +++ /dev/null @@ -1 +0,0 @@ -master['use-service'] = true diff --git a/acceptance/setup/aio/pre-suite/045_EnsureMasterStartedOnPassenger.rb b/acceptance/setup/aio/pre-suite/045_EnsureMasterStartedOnPassenger.rb deleted file mode 100644 index 20f4fdfb..00000000 --- a/acceptance/setup/aio/pre-suite/045_EnsureMasterStartedOnPassenger.rb +++ /dev/null @@ -1,3 +0,0 @@ -if master.graceful_restarts? - on(master, puppet('resource', 'service', master['puppetservice'], "ensure=running")) -end diff --git a/acceptance/setup/aio/pre-suite/050_Install-activemq.rb b/acceptance/setup/aio/pre-suite/050_Install-activemq.rb deleted file mode 100644 index e48a5159..00000000 --- a/acceptance/setup/aio/pre-suite/050_Install-activemq.rb +++ /dev/null @@ -1,122 +0,0 @@ -test_name 'install activemq' do - amq_version = ENV['ACTIVEMQ_VERSION'] || '5.14.4' - amq_source_url = - if amq_source = ENV['ACTIVEMQ_SOURCE'] - "#{amq_source}/activemq/#{amq_version}" - else - 'http://buildsources.delivery.puppetlabs.net' - end - jdk_version = '8' - jdk_source_url = - if jdk_source = ENV['JDK_SOURCE'] and jdk_version_full = ENV['JDK_VERSION_FULL'] - jdk_version, jdk_build = jdk_version_full.split('-') - "#{jdk_source}/#{jdk_version_full}" - else - 'http://buildsources.delivery.puppetlabs.net' - end - - # install activemq, copy config and trust/keystore - curl_options = '--silent --show-error --fail' - if mco_master.platform =~ /el-|centos/ then - install_package mco_master, "java-1.#{jdk_version}.0-openjdk" - curl_on(mco_master, "#{curl_options} -O #{amq_source_url}/apache-activemq-#{amq_version}-bin.tar.gz") - on(mco_master, "cd /opt && tar xzf /root/apache-activemq-#{amq_version}-bin.tar.gz") - activemq_confdir = "/opt/apache-activemq-#{amq_version}/conf" - elsif mco_master.platform =~/ubuntu|debian/ then - if mco_master.platform =~/ubuntu-14.04/ - # fallback to JDK 7 on older Ubuntu - jdk_version = '7' - end - install_package mco_master, "openjdk-#{jdk_version}-jdk" - curl_on(mco_master, "#{curl_options} -O #{amq_source_url}/apache-activemq-#{amq_version}-bin.tar.gz") - on(mco_master, "cd /opt && tar xzf /root/apache-activemq-#{amq_version}-bin.tar.gz") - activemq_confdir = "/opt/apache-activemq-#{amq_version}/conf" - elsif mco_master.platform =~/windows/ then - - step "Windows - Install Oracle JDK" - if mco_master[:ruby_arch] == 'x64' then - jdk_arch = 'x64' - else - jdk_arch = 'i586' - end - if mco_master.platform =~ /2003/ then - fail_test "Windows 2003 is not supported" - else - admin_dir = 'Users/Administrator' - end - jdk_exe = "jdk-#{jdk_version}-windows-#{jdk_arch}.exe" - - curl_on(mco_master, "-k -L -O -H 'Cookie: oraclelicense=accept-securebackup-cookie' '#{jdk_source_url}/#{jdk_exe}'") - - on(mco_master, "mv #{jdk_exe} /cygdrive/c/#{admin_dir}/") - manifest = < '0777', - } - -> - package {'java': - ensure => installed, - source => 'C:/#{admin_dir}/#{jdk_exe}', - install_options => ['INSTALLDIR=C:\\java', 'STATIC=1', '/s'], - } -EOS - apply_manifest_on(mco_master, manifest) - - step "Windows - Add JAVA_HOME environmental variable" - mco_master.add_env_var('JAVA_HOME','C:\java') - - step "Windows - Add java/bin to PATH environmental variable" - mco_master.add_env_var('PATH', 'C:\java\bin') - - step "Windows - Install activemq" - file_path = mco_master.tmpfile('activemq.zip') - curl_on(mco_master, "-o #{file_path}.zip #{amq_source_url}/apache-activemq-#{amq_version}-bin.zip") - on(mco_master, puppet("module install reidmv-unzip")) - manifest = < '#{file_path}.zip', - creates => 'C:/apache-activemq-#{amq_version}', - } -EOS - apply_manifest_on(mco_master, manifest) - - step "Windows - Add ACTIVEMQ_HOME environmental variable" - mco_master.add_env_var('ACTIVEMQ_HOME',"C:\\apache-activemq-#{amq_version}") - activemq_confdir = "C:/apache-activemq-#{amq_version}/conf" - # ///END Windows install - else - install_package mco_master, 'activemq' - activemq_confdir = "/etc/activemq" - end - - step "Setup activemq config files" - scp_to mco_master, 'files/activemq.xml', "#{activemq_confdir}/activemq.xml" - scp_to mco_master, 'files/activemq.truststore', "#{activemq_confdir}/activemq.truststore" - scp_to mco_master, 'files/activemq.keystore', "#{activemq_confdir}/activemq.keystore" - - step "Start activemq" - if mco_master.platform =~ /el-|centos|ubuntu|debian/ then - on mco_master, "cd /opt/apache-activemq-#{amq_version} && ./bin/activemq start" - elsif mco_master.platform =~/windows/ then - if mco_master[:ruby_arch] == 'x64' then - amq_arch = 'win64' - else - amq_arch = 'win32' - end - on mco_master, "C:/apache-activemq-#{amq_version}/bin/#{amq_arch}/InstallService.bat" - on mco_master, puppet('resource service activemq ensure=running') - else - on mco_master, 'service activemq start' - end - - unless port_open_within?(mco_master, 61613, 300 ) - raise Beaker::DSL::FailTest, 'Timed out trying to access ActiveMQ' - end - - step "Add activemq to hosts" do - ip = fact_on(mco_master, 'ipaddress') - hosts.each do |h| - apply_manifest_on(h, "host { 'activemq': ip => '#{ip}'}") - end - end -end diff --git a/acceptance/setup/aio/pre-suite/060_Install-mcollective-daemon.rb b/acceptance/setup/aio/pre-suite/060_Install-mcollective-daemon.rb deleted file mode 100644 index 93a8a64f..00000000 --- a/acceptance/setup/aio/pre-suite/060_Install-mcollective-daemon.rb +++ /dev/null @@ -1,78 +0,0 @@ -test_name 'configure mcollective daemon' do - - step "Setup mcollective config files" - unless mco_master.platform =~/windows/ then - mco_confdir = "/etc/puppetlabs/mcollective" - scp_to mco_master, 'files/client.cfg', "#{mco_confdir}/client.cfg" - else - mco_confdir = "C:/ProgramData/PuppetLabs/mcollective/etc" - scp_to mco_master, 'files/windows-client.cfg', "#{mco_confdir}/client.cfg" - end - - hosts.each do |h| - - unless h.platform =~/windows/ then - mco_confdir = "/etc/puppetlabs/mcollective" - libdir = "/opt/puppetlabs/mcollective/plugins" - logdir = "/var/log/puppetlabs/mcollective" - puppet_confdir = "/etc/puppetlabs/puppet" - else - mco_confdir = "C:/ProgramData/PuppetLabs/mcollective/etc" - libdir = "#{mco_confdir}/plugins" - logdir = "C:/ProgramData/PuppetLabs/mcollective/var/log" - puppet_confdir = "C:/ProgramData/PuppetLabs/puppet/etc/puppet" - end - - - server_cfg =<> $HOME/.ssh/known_hosts" - - repositories.each do |repository| - step "Install #{repository[:name]}" - if repository[:path] =~ /^file:\/\/(.+)$/ - on h, "test -d #{SourcePath} || mkdir -p #{SourcePath}" - source_dir = $1 - checkout_dir = "#{SourcePath}/#{repository[:name]}" - on h, "rm -f #{checkout_dir}" # just the symlink, do not rm -rf ! - on h, "ln -s #{source_dir} #{checkout_dir}" - on h, "cd #{checkout_dir} && if [ -f install.rb ]; then ruby ./install.rb ; else true; fi" - else - install_from_git_on h, SourcePath, repository - end - on h, "cd #{SourcePath}/#{repository[:name]} && for i in agent aggregate application data util validator ; do cp -a $i #{mco_libdir} ; done" - end - - unless h.platform =~/windows/ then - on h, 'service mcollective restart' - else - on h, puppet('resource service mcollective ensure=stopped') - on h, puppet('resource service mcollective ensure=running') - end - sleep 30 # wait for mco to finish starting - end - unless port_open_within?(mco_master, 61613, 300 ) - raise Beaker::DSL::FailTest, 'Timed out trying to access ActiveMQ' - end -end diff --git a/acceptance/setup/common/pre-suite/025_StopFirewall.rb b/acceptance/setup/common/pre-suite/025_StopFirewall.rb deleted file mode 100644 index 0d651ba0..00000000 --- a/acceptance/setup/common/pre-suite/025_StopFirewall.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'puppet/acceptance/install_utils' - -extend Puppet::Acceptance::InstallUtils - -test_name "Stop firewall" do - hosts.each do |host| - stop_firewall_on(host) - end -end diff --git a/acceptance/setup/common/pre-suite/040_ValidateSignCert.rb b/acceptance/setup/common/pre-suite/040_ValidateSignCert.rb deleted file mode 100644 index 54943242..00000000 --- a/acceptance/setup/common/pre-suite/040_ValidateSignCert.rb +++ /dev/null @@ -1,6 +0,0 @@ -test_name "Validate Sign Cert" - -require 'puppet/acceptance/common_utils' -extend Puppet::Acceptance::CAUtils - -initialize_ssl diff --git a/acceptance/setup/common/pre-suite/070_InstallCACerts.rb b/acceptance/setup/common/pre-suite/070_InstallCACerts.rb deleted file mode 100644 index 19d87751..00000000 --- a/acceptance/setup/common/pre-suite/070_InstallCACerts.rb +++ /dev/null @@ -1,93 +0,0 @@ -test_name "Install CA Certs" -confine :to, :platform => 'windows' - -GEOTRUST_GLOBAL_CA = <<-EOM ------BEGIN CERTIFICATE----- -MIIDVDCCAjygAwIBAgIDAjRWMA0GCSqGSIb3DQEBBQUAMEIxCzAJBgNVBAYTAlVT -MRYwFAYDVQQKEw1HZW9UcnVzdCBJbmMuMRswGQYDVQQDExJHZW9UcnVzdCBHbG9i -YWwgQ0EwHhcNMDIwNTIxMDQwMDAwWhcNMjIwNTIxMDQwMDAwWjBCMQswCQYDVQQG -EwJVUzEWMBQGA1UEChMNR2VvVHJ1c3QgSW5jLjEbMBkGA1UEAxMSR2VvVHJ1c3Qg -R2xvYmFsIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA2swYYzD9 -9BcjGlZ+W988bDjkcbd4kdS8odhM+KhDtgPpTSEHCIjaWC9mOSm9BXiLnTjoBbdq -fnGk5sRgprDvgOSJKA+eJdbtg/OtppHHmMlCGDUUna2YRpIuT8rxh0PBFpVXLVDv -iS2Aelet8u5fa9IAjbkU+BQVNdnARqN7csiRv8lVK83Qlz6cJmTM386DGXHKTubU -1XupGc1V3sjs0l44U+VcT4wt/lAjNvxm5suOpDkZALeVAjmRCw7+OC7RHQWa9k0+ -bw8HHa8sHo9gOeL6NlMTOdReJivbPagUvTLrGAMoUgRx5aszPeE4uwc2hGKceeoW -MPRfwCvocWvk+QIDAQABo1MwUTAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTA -ephojYn7qwVkDBF9qn1luMrMTjAfBgNVHSMEGDAWgBTAephojYn7qwVkDBF9qn1l -uMrMTjANBgkqhkiG9w0BAQUFAAOCAQEANeMpauUvXVSOKVCUn5kaFOSPeCpilKIn -Z57QzxpeR+nBsqTP3UEaBU6bS+5Kb1VSsyShNwrrZHYqLizz/Tt1kL/6cdjHPTfS -tQWVYrmm3ok9Nns4d0iXrKYgjy6myQzCsplFAMfOEVEiIuCl6rYVSAlk6l5PdPcF -PseKUgzbFbS9bZvlxrFUaKnjaZC2mqUPuLk/IH2uSrW4nOQdtqvmlKXBx4Ot2/Un -hw4EbNX/3aBd7YdStysVAq45pmp06drE57xNNB6pXE0zX5IJL4hmXXeXxx12E6nV -5fEWCRE11azbJHFwLJhWC9kXtNHjUStedejV0NxPNO3CBWaAocvmMw== ------END CERTIFICATE----- -EOM - -USERTRUST_NETWORK_CA = <<-EOM ------BEGIN CERTIFICATE----- -MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB -lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug -Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt -SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG -A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe -MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v -d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh -cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn -0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ -M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a -MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd -oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI -DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy -oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD -VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 -dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy -bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF -BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM -//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli -CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE -CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t -3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS -KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== ------END CERTIFICATE----- -EOM - -EQUIFAX_CA = <<-EOM ------BEGIN CERTIFICATE----- -MIIDIDCCAomgAwIBAgIENd70zzANBgkqhkiG9w0BAQUFADBOMQswCQYDVQQGEwJV -UzEQMA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2Vy -dGlmaWNhdGUgQXV0aG9yaXR5MB4XDTk4MDgyMjE2NDE1MVoXDTE4MDgyMjE2NDE1 -MVowTjELMAkGA1UEBhMCVVMxEDAOBgNVBAoTB0VxdWlmYXgxLTArBgNVBAsTJEVx -dWlmYXggU2VjdXJlIENlcnRpZmljYXRlIEF1dGhvcml0eTCBnzANBgkqhkiG9w0B -AQEFAAOBjQAwgYkCgYEAwV2xWGcIYu6gmi0fCG2RFGiYCh7+2gRvE4RiIcPRfM6f -BeC4AfBONOziipUEZKzxa1NfBbPLZ4C/QgKO/t0BCezhABRP/PvwDN1Dulsr4R+A -cJkVV5MW8Q+XarfCaCMczE1ZMKxRHjuvK9buY0V7xdlfUNLjUA86iOe/FP3gx7kC -AwEAAaOCAQkwggEFMHAGA1UdHwRpMGcwZaBjoGGkXzBdMQswCQYDVQQGEwJVUzEQ -MA4GA1UEChMHRXF1aWZheDEtMCsGA1UECxMkRXF1aWZheCBTZWN1cmUgQ2VydGlm -aWNhdGUgQXV0aG9yaXR5MQ0wCwYDVQQDEwRDUkwxMBoGA1UdEAQTMBGBDzIwMTgw -ODIyMTY0MTUxWjALBgNVHQ8EBAMCAQYwHwYDVR0jBBgwFoAUSOZo+SvSspXXR9gj -IBBPM5iQn9QwHQYDVR0OBBYEFEjmaPkr0rKV10fYIyAQTzOYkJ/UMAwGA1UdEwQF -MAMBAf8wGgYJKoZIhvZ9B0EABA0wCxsFVjMuMGMDAgbAMA0GCSqGSIb3DQEBBQUA -A4GBAFjOKer89961zgK5F7WF0bnj4JXMJTENAKaSbn+2kmOeUJXRmm/kEd5jhW6Y -7qj/WsjTVbJmcVfewCHrPSqnI0kBBIZCe/zuf6IWUrVnZ9NA2zsmWLIodz2uFHdh -1voqZiegDfqnc1zqcPGUIWVEX/r87yloqaKHee9570+sB3c4 ------END CERTIFICATE----- -EOM - -hosts.each do |host| - step "Installing Geotrust CA cert" - create_remote_file(host, "geotrustglobal.pem", GEOTRUST_GLOBAL_CA) - on host, "chmod 644 geotrustglobal.pem" - on host, "cmd /c certutil -v -addstore Root `cygpath -w geotrustglobal.pem`" - - step "Installing Usertrust Network CA cert" - create_remote_file(host, "usertrust-network.pem", USERTRUST_NETWORK_CA) - on host, "chmod 644 usertrust-network.pem" - on host, "cmd /c certutil -v -addstore Root `cygpath -w usertrust-network.pem`" - - step "Installing Equifax CA cert" - create_remote_file(host, "equifax.pem", EQUIFAX_CA) - on host, "chmod 644 equifax.pem" - on host, "cmd /c certutil -v -addstore Root `cygpath -w equifax.pem`" -end diff --git a/acceptance/setup/common/pre-suite/110_SetPEPuppetService.rb b/acceptance/setup/common/pre-suite/110_SetPEPuppetService.rb deleted file mode 100644 index 49ee5a8b..00000000 --- a/acceptance/setup/common/pre-suite/110_SetPEPuppetService.rb +++ /dev/null @@ -1 +0,0 @@ -master['puppetservice'] = 'pe-puppetserver' diff --git a/acceptance/ssl/ca/ca_crl.pem b/acceptance/ssl/ca/ca_crl.pem deleted file mode 100644 index f9230482..00000000 --- a/acceptance/ssl/ca/ca_crl.pem +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN X509 CRL----- -MIICmzCBhAIBATANBgkqhkiG9w0BAQUFADAhMR8wHQYDVQQDDBZQdXBwZXQgQ0E6 -IHNvY2tzLmxvY2FsFw0xNTAzMDMxMTU2MTlaFw0yMDAzMDExMTU2MjBaoC8wLTAf -BgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7nhV46zAKBgNVHRQEAwIBADANBgkq -hkiG9w0BAQUFAAOCAgEAOuf7KWxGzusauTsjVKFAc9/vZhMC9JcZUGPvlGENLMFz -Fn9kGh04Duq/HcO4KG4wS/VjO54wawQ8tcPXPOouVOXgoFLOUuEN5LQZZ7o9cOaF -pQ5e/1m6kveABW9kbwioZCXx1GivBEEuEaF2ZfneKqO3p6eTlnwaM400PypL3c4c -L/CvDqTgH7F9tPC6rhNJ8iQQHRRKfmpEAF2Ej5i6FPJMDT4CIq13gQkJVlXVp6xB -AxfKXcMCc129NyRq3/Jjrc58/PRdkFxR/0MdN1EX7q2DnlzVDw4TU1ow4IVexFXK -SQKJZmBgGYckIrQRumJatkeNB4k4aeSEesHkppZ7w57wAhsW1GTXZCdPs6Saqs3v -I44btc3N8kTk0syw7nvl6ifJpa3UNJdJ3HnNrPyDEnKJQ3eMRG1kslRJnFnKC/y/ -7C55cxL1wzCm+O/ks0MVG8QwK59Ooowlztimm7X9EZPOiga010lLgWXNN54Cha9u -AYo4rprqY/sK12eayhp0nodOGrpHV6PqhvtDfymoIT/hvocpxpPl80pYYO86+72d -G012NdlG1Pa3/okBIfSw6/JZLiv/MiE0JFaob/xdnWPCD8UpZq8LYjIhOv5RnYBj -VMisIczo6JRCwHhUB0mvK+afrEWgAodIve0ZNYAdeZ90NusDXBcA7ka9lUpau1E= ------END X509 CRL----- diff --git a/acceptance/ssl/ca/ca_crt.pem b/acceptance/ssl/ca/ca_crt.pem deleted file mode 100644 index 2e084dce..00000000 --- a/acceptance/ssl/ca/ca_crt.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFbTCCA1WgAwIBAgIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyMFoXDTIwMDMwMTExNTYy -MFowITEfMB0GA1UEAwwWUHVwcGV0IENBOiBzb2Nrcy5sb2NhbDCCAiIwDQYJKoZI -hvcNAQEBBQADggIPADCCAgoCggIBAL0qDiSB8u/6dxihgLSycKXGMeH+xzASvIWk -tsK6oPZ8vwBeEoz4A1iw6Lf7Xz9G9qUx0NlmLryN7HLFmidQ4aTwgYE/+/Q/xwa1 -FJaFl9yy49vU8/rfZ6m6/xNfBMP3MzDjqtlDYouo1A7h+VTN9sXMmltI9Qu8zwvT -0UYRy9J8wAMri7y0yVdiYZ4IjhzhwGaB4yrCZkFN6fsuoW3u2Yb1BtewffqXoav2 -R5G6m/tcgMzNkyJ9cG+GUM1bk5lEE8KOf360mHC8RsOu/7ZWLJmIUx5JKB+5xhGX -1nNd71mNB36f6JJ5ZlNSdVa4Yw5bv2HPyqk3BXbxfZlov/8ECfyEsW4LIANgTQbm -9Eep6wZE8inogDiGB5hMH1VgrbLjiNriScF3yeMVfdyzRdSp/OjyuE1CmOcBToIu -Bse7RDfpuJNDQs9ycqaa1ncFVA0sO6e06FszlznaioejELjCULT6qxVNgwTLTPqH -e1vTZe+iCtoVAIw0RFUhHltAZE4hUXAm+trgAy1FIpeG90TTtfHQEwpR9IgdzDaI -b2Oc4WeOG7pYxBRFz9NeESyBuKxXmCVfbprbtunpP/F0N4TgUc2zxdSZFN03GKL+ -jKYYDUcFq4Iune/uxdGudcUHfyx0XaRFmRAjtqwFoFzuebnuJuXKcCuh7x5OTB8i -4sF64VQJAgMBAAGjga8wgawwNQYJYIZIAYb4QgENBChQdXBwZXQgUnVieS9PcGVu -U1NMIEludGVybmFsIENlcnRpZmljYXRlMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB -Af8EBTADAQH/MB0GA1UdDgQWBBRqr/VzStqL/9HXVt6Qy5P7nhV46zAzBgNVHSME -LDAqoSWkIzAhMR8wHQYDVQQDDBZQdXBwZXQgQ0E6IHNvY2tzLmxvY2FsggEBMA0G -CSqGSIb3DQEBCwUAA4ICAQAWW4sT+13YO3E76jLgjhIjOmmQCSGJiJAHe8gxZxrZ -xbsybkImc+bR0DIJXMQzWpm183Wx0K3YKVD50zfoS5hTThU8OLuMHvXfmMwNy7Hi -0vwOaREyrfIzuCRhY3PUz7HXlncdmzAT/1Q9n07d7VCmsFvHtksSJUVcTAMsL9xv -D6fzO2or1ZtpJbckww6NmoOw+ofdn4vutn06do5SZbbPDfzEfdyPbeWFUXqFlCGk -/IO8RWjMt6XRBfc6z9HFqj4HI2n5t8gSVN8MTUo1vmzaR0rErpO+JGw8xm2XVvBW -jHwG8onTGErHYk+04M+woL8Q294SglpfvLONuJTBaKGaCtVnQvhDMvITJ7rnrBz3 -r1x1Fx635ofY+FGpVMX0EGLA79gaya6zeyBIlvtUcTN89UfQ0sCEClme9Oe48Scy -bSJKsJNsNqEljCWQ8sqlc4zGXGk2tysAHWBEPoPcfPeIyWKNx0KQ5dz8zf+adV+3 -JIZC70qF3fEnyhpv0Z6B7VxRR/EZUVAyLIkucQUNVcMR7uTuAWv67CciCS9EOfit -eYNnpfGRXYoiEgaSnv6oOCtgDSXd6nJgtfe9m4K7200KNPFJIWREFm21UCseHsRc -AC+KbxNdwopBY8IFS2PLJQPQfBnf4q5uPZagBg4r0mHH5TgiESpa15fI25fjxAHB -Zw== ------END CERTIFICATE----- diff --git a/acceptance/ssl/ca/ca_key.pem b/acceptance/ssl/ca/ca_key.pem deleted file mode 100644 index 17a454e2..00000000 --- a/acceptance/ssl/ca/ca_key.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/acceptance/ssl/ca/ca_pub.pem b/acceptance/ssl/ca/ca_pub.pem deleted file mode 100644 index 759c90a9..00000000 --- a/acceptance/ssl/ca/ca_pub.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAvSoOJIHy7/p3GKGAtLJw -pcYx4f7HMBK8haS2wrqg9ny/AF4SjPgDWLDot/tfP0b2pTHQ2WYuvI3scsWaJ1Dh -pPCBgT/79D/HBrUUloWX3LLj29Tz+t9nqbr/E18Ew/czMOOq2UNii6jUDuH5VM32 -xcyaW0j1C7zPC9PRRhHL0nzAAyuLvLTJV2JhngiOHOHAZoHjKsJmQU3p+y6hbe7Z -hvUG17B9+pehq/ZHkbqb+1yAzM2TIn1wb4ZQzVuTmUQTwo5/frSYcLxGw67/tlYs -mYhTHkkoH7nGEZfWc13vWY0Hfp/oknlmU1J1VrhjDlu/Yc/KqTcFdvF9mWi//wQJ -/ISxbgsgA2BNBub0R6nrBkTyKeiAOIYHmEwfVWCtsuOI2uJJwXfJ4xV93LNF1Kn8 -6PK4TUKY5wFOgi4Gx7tEN+m4k0NCz3JypprWdwVUDSw7p7ToWzOXOdqKh6MQuMJQ -tPqrFU2DBMtM+od7W9Nl76IK2hUAjDREVSEeW0BkTiFRcCb62uADLUUil4b3RNO1 -8dATClH0iB3MNohvY5zhZ44buljEFEXP014RLIG4rFeYJV9umtu26ek/8XQ3hOBR -zbPF1JkU3TcYov6MphgNRwWrgi6d7+7F0a51xQd/LHRdpEWZECO2rAWgXO55ue4m -5cpwK6HvHk5MHyLiwXrhVAkCAwEAAQ== ------END PUBLIC KEY----- diff --git a/acceptance/ssl/ca/inventory.txt b/acceptance/ssl/ca/inventory.txt deleted file mode 100644 index 477796b2..00000000 --- a/acceptance/ssl/ca/inventory.txt +++ /dev/null @@ -1,5 +0,0 @@ -0x0001 2015-03-02T11:56:20UTC 2020-03-01T11:56:20UTC /CN=Puppet CA: socks.local -0x0002 2015-03-02T11:56:21UTC 2020-03-01T11:56:21UTC /CN=socks.local -0x0003 2015-03-02T11:56:24UTC 2020-03-01T11:56:24UTC /CN=activemq -0x0004 2015-03-02T11:56:29UTC 2020-03-01T11:56:29UTC /CN=mcollective-client -0x0005 2015-03-02T12:18:35UTC 2020-03-01T12:18:35UTC /CN=mcollective-server diff --git a/acceptance/ssl/ca/private/ca.pass b/acceptance/ssl/ca/private/ca.pass deleted file mode 100644 index 48e05982..00000000 --- a/acceptance/ssl/ca/private/ca.pass +++ /dev/null @@ -1 +0,0 @@ -BHSQo_BL3oI?81FWI5lG \ No newline at end of file diff --git a/acceptance/ssl/ca/serial b/acceptance/ssl/ca/serial deleted file mode 100644 index b738ec89..00000000 --- a/acceptance/ssl/ca/serial +++ /dev/null @@ -1 +0,0 @@ -0006 \ No newline at end of file diff --git a/acceptance/ssl/ca/signed/activemq.pem b/acceptance/ssl/ca/signed/activemq.pem deleted file mode 100644 index ff90eb7c..00000000 --- a/acceptance/ssl/ca/signed/activemq.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFajCCA1KgAwIBAgIBAzANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyNFoXDTIwMDMwMTExNTYy -NFowEzERMA8GA1UEAwwIYWN0aXZlbXEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -ggIKAoICAQDu1l/bhODd+Rwgh1AfjVrauRrcEmmWX4Ma9Ai9tOzSEqHkDFc/aLeN -4I+mSGXFAuOyN2vuXFVlEv5R5yT8N1IRFQ5OR5oUmuEce8E2QMZWdmosDkTBGVMF -RjkLe4orDwpHOAGGbn1iIJnBTiKalGB/1XPdaTGSCNQ7/u5H/AteJ4Bk/5w08IcY -tah2zPOZ2pX/p5dT9SxtKNfuYXTbmf14PF6X43/+yxcI2QclTDLit1DhxXVF/h/U -7ffJMWFW2t/gmtfXOcRjMHy6umVKI67I1Q7FTnSbSDmw861mmQECGa9TG2lDb3OO -SwH0bWHj+arbxS5B67ihEwBMDGRyGAaMcabiUll4BDd6HN42H6Q3BC5ohoUgPOOB -xMHkztwNUl08I/stPw40nudJV/KRC6zc4waH5ivZAhQzyyafeDDhNrT1o2i/I+UL -sDZ7R/q+aQ+lgyGV3cPYrorPn/aTWGW4Zo1uEFHumSVUfcpK141DwU+fI8idGbDi -XELV7mRWOKSrJ1ywpbTxfhgStnMjLRyAd5g7taKQ8ImWnCyqYOQBXZRGGNyoG0dj -zzio+2eWe1QQZOpKeMm5T00qyBJi6fQTuAPLLGIbZMS5WmWLvv00o312lw2a53Ic -mbwH1eefBvyBDENklZpgKh0RBDLZ5iItmFtDPqEKVzjnFMb2sm92IwIDAQABo4G6 -MIG3MDUGCWCGSAGG+EIBDQQoUHVwcGV0IFJ1YnkvT3BlblNTTCBJbnRlcm5hbCBD -ZXJ0aWZpY2F0ZTAOBgNVHQ8BAf8EBAMCBaAwIAYDVR0lAQH/BBYwFAYIKwYBBQUH -AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOgvbtpFuQfBRhXM -PG9EvkScrrehMB8GA1UdIwQYMBaAFGqv9XNK2ov/0ddW3pDLk/ueFXjrMA0GCSqG -SIb3DQEBCwUAA4ICAQCuyi/sp1xzTC/nIcwg2k9CiEJbY4I7lBX68Gh1uVVAJtTN -TEJNGa+ot0pEWp5OQiD6tTZboCvJdIgq3SsZ/YHjjPEGWDbGwmTEWxAI/Oy6WOn7 -fK6EyAsuoMFfIDoZo45AnIsCLjXAjwEOyRYcVYElJtFJYa806RsPMAeOkdQQDk1G -Hk9QB5Z37ESfYZ11ULVPJ/akpgWMXk994SwE+B3Nqsrk1Gc3C/78ZXz+shHrJq6L -1Eiy4FzrYbOGD2+6B2SduD17YOyhuv8205eZnMyRKQ2RCyOA9ginUIJmE/xeAxgT -ITpxEKI/dCAEt4QqUM2ffmvSTaaWeftEvSsrRHBmU82MRWGPCkockCL9cU9dAa+F -cpJUYLDmjGs/xXQJpY+is5ZwdXNZE8Onv2zMPV3nAMWrw6l/N+SlWyHjyAgba9k4 -ZJcSUQ20ZFX6R4zK7/FAQGBOpTpIYmWEMfJCJoV20Bf31HlYE7qToOnazw424csV -OdjvXbusZdADw5TdVPRvl2Y3Ej6NA4AEqgOi4fC8C512GYQIMFOj8bcq+c3CGhfB -X0fk9AKhxaen0HmSGR+CWx3EdUnjHRp64d5UKLaw/190Ny7bKNhLFFLVuMNn86dg -oCabruNoOGS/ITCnQ8xYmY7KuhNFuh5/F3Z9PYp8Ej3lDMhO3ltZ7h4d7NP3uQ== ------END CERTIFICATE----- diff --git a/acceptance/ssl/ca/signed/mcollective-client.pem b/acceptance/ssl/ca/signed/mcollective-client.pem deleted file mode 100644 index ec630c8c..00000000 --- a/acceptance/ssl/ca/signed/mcollective-client.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIBBDANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyOVoXDTIwMDMwMTExNTYy -OVowHTEbMBkGA1UEAwwSbWNvbGxlY3RpdmUtY2xpZW50MIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAopc/qeMD7V4sQSeLjI71Na9gabTXC5dxDV5T/qoM -ZOGVInuMUVEjWWYM/Pjsj+/rEqjcN+0N8sZHpY9sMkbqB97I9c0A/zdVskF8EGVe -7vS/ytUIUtKwONffD0/FEPNpIDUc++rSj5PPgJvn35ObhB7Q2MpXLx6iamD1gnyF -fmrssnv6/f5esjV/n61YaskLd1zQD6t7Zghdkzjy6pFz/AfGqWdzq4r2D5ANnsTO -WAH1qWyphv8Jbwf7vE+H8x7jySmwrxmHu/PDiqZMz28ZRTVjWBWY67hI7Z/p1otQ -kzoFFf7XLNfNuyajcsG1YCaGXwbBHdb5aa+leLKLzPd1Xy1X0/h4MEG63xFzkrml -B7PQZHbzVHdi1lI4tI6erUyb3RjsIT2EE8ED732iDIPZduDg5I1yOUKa7R8wEYVE -RyHv7V6Hbo2ydJu/oIMwJZ0oGlzK1T1xpEsg8t7wgcLKUY7n/+zG23dLOh49/ubm -+ecC3QbN1z3pMc9pOceTwkWCi6vGf0Otd1TrraHAlK22dzkfQqFK2mKMegoQZXSW -HPd5JAdSy4qnXCJPxGJaStQGDii5SndFWiuduKlQpSE6WSgMdiBwncAFmdYsnlNM -gY0wHL/ufjWhsVdePVrEagNux6PdLdmolWawoHxtNh4UuwyByiyJU8gF7A6f/DoA -H8cCAwEAAaOBujCBtzA1BglghkgBhvhCAQ0EKFB1cHBldCBSdWJ5L09wZW5TU0wg -SW50ZXJuYWwgQ2VydGlmaWNhdGUwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQW -MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQV -Bygt19XIerwYMgLimQ1LAQjwATAfBgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7 -nhV46zANBgkqhkiG9w0BAQsFAAOCAgEANw3dHIu3J71utQB8eiCwfhNKxBhM8Yj3 -i+eeAvyo28ZN3knyCbXRQmaegVRbeRLHi3YC+9yG0MFTBeOasxVPK508WosaaQJA -yOuP/++IYbPiqB/xZArAbe5lA/bmp7xekS/hB9PA0hp6eS2tj13GQBOfJ+tNDEFf -su8H1w+OIA626LfR1PwaqkKCTQBHEtX+Cv8KyvDeYLt0cP2tMi4MQbqdqO093AHO -4o8ZdyBOJUwzjYzt+4Y3M5Lh7i/WXh4XxnH59F0RfhVmztf2cSrjRKUvop+IDP7b -b1bx4vtnorFSFqcngb9jg6Om21+x1Akhew26ZsvlazSWwAxCrlPOSCPy/taW680K -gA02sbAonkanLe44E3UE5puugWz8ImDp28gOGt1PWiCyNxtJcJ3j3OTw/L5jur8Q -OEZNhTrZ5j7WdZrzEFMiu6K7C2c0RL2Gz1TWd4MocGTe/vs+0ebbh6D1w9EGci8V -+FBIavrOc9ZmI03wGIHLnHD+QgxLeTvQOnFENr0k9ah320b8UHdpbbvr3pRf8an5 -Q8GdVahd5gTY1CbkZGeaIlY8OWUG7s9mvGGDEswSsLIah0UjTNDpzexw1ibr+LDZ -tSH7GPdtwSniix47ywtv/qzVa8erCpJQ6rucUThR7o4jn3BcZJm0ZDGzZhWhpxjt -cCoGKqzr1xk= ------END CERTIFICATE----- diff --git a/acceptance/ssl/ca/signed/mcollective-server.pem b/acceptance/ssl/ca/signed/mcollective-server.pem deleted file mode 100644 index 7b26714e..00000000 --- a/acceptance/ssl/ca/signed/mcollective-server.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIBBTANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjEyMTgzNVoXDTIwMDMwMTEyMTgz -NVowHTEbMBkGA1UEAwwSbWNvbGxlY3RpdmUtc2VydmVyMIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAmb8vabpZIw3WHUXqV6HkOjmKXz5IgjhXBg5/XA6A -TGrvFlo5mj6W2JpE7CxI+DhHYZ4+Z5NLkzIAYoSXuV/0umuPpXOAH5ma5JYudzKd -5RV7RiffSj5LfzBnGZwn3dVVhaZiR6vUM4cmP8CqUqfoxvpweh9nGDbkEKoHdC4p -ENerwvuheAnPGflonDZFxaWEqXn5oXxSiuf88XZ0tgn1mMBn/JF0rXtCKYFfOAr7 -Evw1eUak+4wEHym26q+BCogKpeW+lT0C/v/TH5XG63ycyhmqiWjTI2vPab6BC7t2 -Bmgd5Grcr7cofvt4QYwIsIwL6ZZWahyLynHMBs85AAm1bfPdnMeia5WP+J98euAJ -7MK8TuX4sEjHt/yAiXT75aD0rsViP9BkKstBudMALokywnMHLa0/KdJCwXP5JH/Z -D8v4LJvpfGxDodC+tyUCsr82Wn67AAculvMZDjY6SD9LtaNrtiNZeNj2PXYBYWWs -CQ4aR2LzelDPDZ3TUu0BqMjNQ07zz/Daol6DASuF1TLUv6YW2tLZ5nggt1rkARx6 -m2BTEpa1Jl6j8KkE2l+7KR6EaUCwz3bqlvAweqY/8mnHrwhXkeeQ6Bta53o5YjXD -WFXTmZD/iSlT8hbnWmoww/EgRjsQyXZ9dS3OelsAPYBTKTwXsKIGdVgj4t8Xd9CK -gicCAwEAAaOBujCBtzA1BglghkgBhvhCAQ0EKFB1cHBldCBSdWJ5L09wZW5TU0wg -SW50ZXJuYWwgQ2VydGlmaWNhdGUwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQW -MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRm -iqXTvKMtefbQHDRT3F9MVe9xtjAfBgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7 -nhV46zANBgkqhkiG9w0BAQsFAAOCAgEAWSNWAshqJtEeV1vfdlU581ppNmodUs7Q -6Ypdx0eot6S9lvTl69GFYonuOvwf8U6chs5Hp051oMB8M1PWzD7CZHj+wMwRKqvG -PWymZUsZQHFpb01tnABLL62fyqxEnaVPqUyRwNQMsXoD6OiW495kY+o17i2k6oYF -pE2ofyREQd40i3wGk7u0MlpWvSfdpcUgzVTTlruOuHh+E2HTf53PH3AuFDgqd8Ee -+2JCO2hcAT7JXfmlxzE6XtbalnaorBnJD09vRR/MshppN6l/11+yMDg3CpfkARBJ -OqSVLd8PD3BZm4jUWd3w7tBMs1WUYrtMtUKVGc2Ct4QyxCpi1bKKZRcrnROo3lLH -ZZGEYo+19KpCff/kOoBiyqkim8SN9cdy5nzEllGsIj+72mJuqRhkh58tlrTBUDl1 -8Sc1rRLZ+T6k2A/UWybkPMVFw+e1DFOtK8QvjwXPiZyNTDmf05uesgp8sJ81iebv -1llZu24x5gVobMHEeXGmKGGs6vquwTrs0/mAy4ujHhkEXZPRkrdv1uBt0sG969/5 -0Bnk+Lq0xxGDbgTt+8TOpV++cE3dU6K3Fb7JCJT8S6dzd/78+T+m13maW6WKdiZc -QAzFNkiw4D0qvdCoL8bu45P58tPFGdJtRbIQ83Ik1Ie6M8nXxCcq0qIttw83Od+H -qDxqCwAZL8E= ------END CERTIFICATE----- diff --git a/acceptance/ssl/ca/signed/socks.local.pem b/acceptance/ssl/ca/signed/socks.local.pem deleted file mode 100644 index b1d805a2..00000000 --- a/acceptance/ssl/ca/signed/socks.local.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFmzCCA4OgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyMVoXDTIwMDMwMTExNTYy -MVowFjEUMBIGA1UEAwwLc29ja3MubG9jYWwwggIiMA0GCSqGSIb3DQEBAQUAA4IC -DwAwggIKAoICAQDePeeGJz7ozXK/j4SJKvyi6Sw7i6alPizI2uR/snx8tYv/V7uj -p3sgZ66uvBBW1Uz7lZpNxD19GOICZXF6m3UozafeGsjiffLA60KoEzL2NKoexIz8 -UDVzQtLfu6RsqnfKzFbq8vjFmwkd+mg64v6bkW3UBpKXySrEEGp/MUiAKEXyi+ab -R7FNH9rvmyr0TL0xDLpUDdR9KMueyltfXDlGyeI7+2GNq0vdSzqjftIhB7QIUeeV -05Xh6ioWQECoX2oQfTg3tssfRkQ7H/hBRvIg9dcn/ehAujdF4juATFUzhzXgBlcn -4QkYqsPAJj+Ax+FIHlZoCil0gGz8Qn0xdfGs+WUfe9p4w9CwxbT9s7hKgJzF4yux -hsb+1uDalvlblOMHBPZ5nAnYxwPELmdtaaDwI3pN+MFTVFvgmkoPdybQY8dTu2np -hJLfjyDs1n9c1spNb1aqv8RSAw8lWzJ7XBCcARD90tcdbxfKLytwz6JUC+hxqC2m -SbL8cHPcFRLoDNk6T5muVrszypMBYZpmgh1Bum3kPwm/nSRsM3RweBUSfsqr8xFW -U6ApNrdUclGha/yjORHNOiIiJFrEdrK/riO6yYyU/s/An8Vmo5HVtP4Lee1J2VcO -5vGVariJhEIsq+XwbLSe6dMk0gOPDAHujjxBBf8m/2yjpb+Ru0CUHUJSBwIDAQAB -o4HoMIHlMDUGCWCGSAGG+EIBDQQoUHVwcGV0IFJ1YnkvT3BlblNTTCBJbnRlcm5h -bCBDZXJ0aWZpY2F0ZTAsBgNVHREEJTAjggZwdXBwZXSCDHB1cHBldC5sb2NhbIIL -c29ja3MubG9jYWwwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQGCCsGAQUF -BwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRobiTuhmr7JT89 -XappKM3PKSknKzAfBgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7nhV46zANBgkq -hkiG9w0BAQsFAAOCAgEAZ2Nm54dwCWnaAGiP+eR+rcrz+tmKfeuqHQMR6jihIFsy -QSrTomBMaMmaw7wdSAyfusbAg6ZZ23lzjHUDUMdbzXNO8/zAKwt93JNdJdeGrpB9 -TxR8Bhb5Xv4axSQszCP/X0iBWm3KjbmhCNXjMqKZ1UuEt8K80bRT+a3YzdRJFNm8 -HmzWyZcN+ramkGIWQoAtjRmxYVOHOatlTOXnX3J5Yg8Z7BUTbtgPl4fGpTEu5fX+ -60uHJy2CB/jXUO1cPb9E7vVt/EPW1GjuYyQGfpG/2lNEGGkDDztbCL2Fn44BZrYO -HuIcmPumTmTR6BKxx5TBnaql2KbX7bvJm9KbBUEwE36KbvwvefcYiTBAtCBzBNXR -u/7BbfSvWHv2Mhtl6O4sF3sSUdhH+eilgO7tzvq1P5DcDGifdu6U8DWeBYGcaPFV -60QciJHZnyFjwoCcYthNIhfAaVjO89q2AFXCD0tlYGL+BLdxutlmHjM5WTau3pSW -cdqF3FWM8dVGbc5gkWce3PL3Am745tpncySH0JGn9cvBPfUK7aoba8r2/Z7BGS/C -EJWgJ+KWHJ1H7F91mImdr3lADvLIDvudb+NulloLdo8qE54csyOno4zPoM5G+5Nz -vcpC7tLkC+kJ8vcn+cnVx41NYwGuonGGCPg053yrcsmvd/wQHFVhiq3RSHZK318= ------END CERTIFICATE----- diff --git a/acceptance/ssl/certs/activemq.pem b/acceptance/ssl/certs/activemq.pem deleted file mode 100644 index ff90eb7c..00000000 --- a/acceptance/ssl/certs/activemq.pem +++ /dev/null @@ -1,31 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFajCCA1KgAwIBAgIBAzANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyNFoXDTIwMDMwMTExNTYy -NFowEzERMA8GA1UEAwwIYWN0aXZlbXEwggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAw -ggIKAoICAQDu1l/bhODd+Rwgh1AfjVrauRrcEmmWX4Ma9Ai9tOzSEqHkDFc/aLeN -4I+mSGXFAuOyN2vuXFVlEv5R5yT8N1IRFQ5OR5oUmuEce8E2QMZWdmosDkTBGVMF -RjkLe4orDwpHOAGGbn1iIJnBTiKalGB/1XPdaTGSCNQ7/u5H/AteJ4Bk/5w08IcY -tah2zPOZ2pX/p5dT9SxtKNfuYXTbmf14PF6X43/+yxcI2QclTDLit1DhxXVF/h/U -7ffJMWFW2t/gmtfXOcRjMHy6umVKI67I1Q7FTnSbSDmw861mmQECGa9TG2lDb3OO -SwH0bWHj+arbxS5B67ihEwBMDGRyGAaMcabiUll4BDd6HN42H6Q3BC5ohoUgPOOB -xMHkztwNUl08I/stPw40nudJV/KRC6zc4waH5ivZAhQzyyafeDDhNrT1o2i/I+UL -sDZ7R/q+aQ+lgyGV3cPYrorPn/aTWGW4Zo1uEFHumSVUfcpK141DwU+fI8idGbDi -XELV7mRWOKSrJ1ywpbTxfhgStnMjLRyAd5g7taKQ8ImWnCyqYOQBXZRGGNyoG0dj -zzio+2eWe1QQZOpKeMm5T00qyBJi6fQTuAPLLGIbZMS5WmWLvv00o312lw2a53Ic -mbwH1eefBvyBDENklZpgKh0RBDLZ5iItmFtDPqEKVzjnFMb2sm92IwIDAQABo4G6 -MIG3MDUGCWCGSAGG+EIBDQQoUHVwcGV0IFJ1YnkvT3BlblNTTCBJbnRlcm5hbCBD -ZXJ0aWZpY2F0ZTAOBgNVHQ8BAf8EBAMCBaAwIAYDVR0lAQH/BBYwFAYIKwYBBQUH -AwEGCCsGAQUFBwMCMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOgvbtpFuQfBRhXM -PG9EvkScrrehMB8GA1UdIwQYMBaAFGqv9XNK2ov/0ddW3pDLk/ueFXjrMA0GCSqG -SIb3DQEBCwUAA4ICAQCuyi/sp1xzTC/nIcwg2k9CiEJbY4I7lBX68Gh1uVVAJtTN -TEJNGa+ot0pEWp5OQiD6tTZboCvJdIgq3SsZ/YHjjPEGWDbGwmTEWxAI/Oy6WOn7 -fK6EyAsuoMFfIDoZo45AnIsCLjXAjwEOyRYcVYElJtFJYa806RsPMAeOkdQQDk1G -Hk9QB5Z37ESfYZ11ULVPJ/akpgWMXk994SwE+B3Nqsrk1Gc3C/78ZXz+shHrJq6L -1Eiy4FzrYbOGD2+6B2SduD17YOyhuv8205eZnMyRKQ2RCyOA9ginUIJmE/xeAxgT -ITpxEKI/dCAEt4QqUM2ffmvSTaaWeftEvSsrRHBmU82MRWGPCkockCL9cU9dAa+F -cpJUYLDmjGs/xXQJpY+is5ZwdXNZE8Onv2zMPV3nAMWrw6l/N+SlWyHjyAgba9k4 -ZJcSUQ20ZFX6R4zK7/FAQGBOpTpIYmWEMfJCJoV20Bf31HlYE7qToOnazw424csV -OdjvXbusZdADw5TdVPRvl2Y3Ej6NA4AEqgOi4fC8C512GYQIMFOj8bcq+c3CGhfB -X0fk9AKhxaen0HmSGR+CWx3EdUnjHRp64d5UKLaw/190Ny7bKNhLFFLVuMNn86dg -oCabruNoOGS/ITCnQ8xYmY7KuhNFuh5/F3Z9PYp8Ej3lDMhO3ltZ7h4d7NP3uQ== ------END CERTIFICATE----- diff --git a/acceptance/ssl/certs/ca.pem b/acceptance/ssl/certs/ca.pem deleted file mode 100644 index 2e084dce..00000000 --- a/acceptance/ssl/certs/ca.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFbTCCA1WgAwIBAgIBATANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyMFoXDTIwMDMwMTExNTYy -MFowITEfMB0GA1UEAwwWUHVwcGV0IENBOiBzb2Nrcy5sb2NhbDCCAiIwDQYJKoZI -hvcNAQEBBQADggIPADCCAgoCggIBAL0qDiSB8u/6dxihgLSycKXGMeH+xzASvIWk -tsK6oPZ8vwBeEoz4A1iw6Lf7Xz9G9qUx0NlmLryN7HLFmidQ4aTwgYE/+/Q/xwa1 -FJaFl9yy49vU8/rfZ6m6/xNfBMP3MzDjqtlDYouo1A7h+VTN9sXMmltI9Qu8zwvT -0UYRy9J8wAMri7y0yVdiYZ4IjhzhwGaB4yrCZkFN6fsuoW3u2Yb1BtewffqXoav2 -R5G6m/tcgMzNkyJ9cG+GUM1bk5lEE8KOf360mHC8RsOu/7ZWLJmIUx5JKB+5xhGX -1nNd71mNB36f6JJ5ZlNSdVa4Yw5bv2HPyqk3BXbxfZlov/8ECfyEsW4LIANgTQbm -9Eep6wZE8inogDiGB5hMH1VgrbLjiNriScF3yeMVfdyzRdSp/OjyuE1CmOcBToIu -Bse7RDfpuJNDQs9ycqaa1ncFVA0sO6e06FszlznaioejELjCULT6qxVNgwTLTPqH -e1vTZe+iCtoVAIw0RFUhHltAZE4hUXAm+trgAy1FIpeG90TTtfHQEwpR9IgdzDaI -b2Oc4WeOG7pYxBRFz9NeESyBuKxXmCVfbprbtunpP/F0N4TgUc2zxdSZFN03GKL+ -jKYYDUcFq4Iune/uxdGudcUHfyx0XaRFmRAjtqwFoFzuebnuJuXKcCuh7x5OTB8i -4sF64VQJAgMBAAGjga8wgawwNQYJYIZIAYb4QgENBChQdXBwZXQgUnVieS9PcGVu -U1NMIEludGVybmFsIENlcnRpZmljYXRlMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMB -Af8EBTADAQH/MB0GA1UdDgQWBBRqr/VzStqL/9HXVt6Qy5P7nhV46zAzBgNVHSME -LDAqoSWkIzAhMR8wHQYDVQQDDBZQdXBwZXQgQ0E6IHNvY2tzLmxvY2FsggEBMA0G -CSqGSIb3DQEBCwUAA4ICAQAWW4sT+13YO3E76jLgjhIjOmmQCSGJiJAHe8gxZxrZ -xbsybkImc+bR0DIJXMQzWpm183Wx0K3YKVD50zfoS5hTThU8OLuMHvXfmMwNy7Hi -0vwOaREyrfIzuCRhY3PUz7HXlncdmzAT/1Q9n07d7VCmsFvHtksSJUVcTAMsL9xv -D6fzO2or1ZtpJbckww6NmoOw+ofdn4vutn06do5SZbbPDfzEfdyPbeWFUXqFlCGk -/IO8RWjMt6XRBfc6z9HFqj4HI2n5t8gSVN8MTUo1vmzaR0rErpO+JGw8xm2XVvBW -jHwG8onTGErHYk+04M+woL8Q294SglpfvLONuJTBaKGaCtVnQvhDMvITJ7rnrBz3 -r1x1Fx635ofY+FGpVMX0EGLA79gaya6zeyBIlvtUcTN89UfQ0sCEClme9Oe48Scy -bSJKsJNsNqEljCWQ8sqlc4zGXGk2tysAHWBEPoPcfPeIyWKNx0KQ5dz8zf+adV+3 -JIZC70qF3fEnyhpv0Z6B7VxRR/EZUVAyLIkucQUNVcMR7uTuAWv67CciCS9EOfit -eYNnpfGRXYoiEgaSnv6oOCtgDSXd6nJgtfe9m4K7200KNPFJIWREFm21UCseHsRc -AC+KbxNdwopBY8IFS2PLJQPQfBnf4q5uPZagBg4r0mHH5TgiESpa15fI25fjxAHB -Zw== ------END CERTIFICATE----- diff --git a/acceptance/ssl/certs/mcollective-client.pem b/acceptance/ssl/certs/mcollective-client.pem deleted file mode 100644 index ec630c8c..00000000 --- a/acceptance/ssl/certs/mcollective-client.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIBBDANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyOVoXDTIwMDMwMTExNTYy -OVowHTEbMBkGA1UEAwwSbWNvbGxlY3RpdmUtY2xpZW50MIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAopc/qeMD7V4sQSeLjI71Na9gabTXC5dxDV5T/qoM -ZOGVInuMUVEjWWYM/Pjsj+/rEqjcN+0N8sZHpY9sMkbqB97I9c0A/zdVskF8EGVe -7vS/ytUIUtKwONffD0/FEPNpIDUc++rSj5PPgJvn35ObhB7Q2MpXLx6iamD1gnyF -fmrssnv6/f5esjV/n61YaskLd1zQD6t7Zghdkzjy6pFz/AfGqWdzq4r2D5ANnsTO -WAH1qWyphv8Jbwf7vE+H8x7jySmwrxmHu/PDiqZMz28ZRTVjWBWY67hI7Z/p1otQ -kzoFFf7XLNfNuyajcsG1YCaGXwbBHdb5aa+leLKLzPd1Xy1X0/h4MEG63xFzkrml -B7PQZHbzVHdi1lI4tI6erUyb3RjsIT2EE8ED732iDIPZduDg5I1yOUKa7R8wEYVE -RyHv7V6Hbo2ydJu/oIMwJZ0oGlzK1T1xpEsg8t7wgcLKUY7n/+zG23dLOh49/ubm -+ecC3QbN1z3pMc9pOceTwkWCi6vGf0Otd1TrraHAlK22dzkfQqFK2mKMegoQZXSW -HPd5JAdSy4qnXCJPxGJaStQGDii5SndFWiuduKlQpSE6WSgMdiBwncAFmdYsnlNM -gY0wHL/ufjWhsVdePVrEagNux6PdLdmolWawoHxtNh4UuwyByiyJU8gF7A6f/DoA -H8cCAwEAAaOBujCBtzA1BglghkgBhvhCAQ0EKFB1cHBldCBSdWJ5L09wZW5TU0wg -SW50ZXJuYWwgQ2VydGlmaWNhdGUwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQW -MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBQV -Bygt19XIerwYMgLimQ1LAQjwATAfBgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7 -nhV46zANBgkqhkiG9w0BAQsFAAOCAgEANw3dHIu3J71utQB8eiCwfhNKxBhM8Yj3 -i+eeAvyo28ZN3knyCbXRQmaegVRbeRLHi3YC+9yG0MFTBeOasxVPK508WosaaQJA -yOuP/++IYbPiqB/xZArAbe5lA/bmp7xekS/hB9PA0hp6eS2tj13GQBOfJ+tNDEFf -su8H1w+OIA626LfR1PwaqkKCTQBHEtX+Cv8KyvDeYLt0cP2tMi4MQbqdqO093AHO -4o8ZdyBOJUwzjYzt+4Y3M5Lh7i/WXh4XxnH59F0RfhVmztf2cSrjRKUvop+IDP7b -b1bx4vtnorFSFqcngb9jg6Om21+x1Akhew26ZsvlazSWwAxCrlPOSCPy/taW680K -gA02sbAonkanLe44E3UE5puugWz8ImDp28gOGt1PWiCyNxtJcJ3j3OTw/L5jur8Q -OEZNhTrZ5j7WdZrzEFMiu6K7C2c0RL2Gz1TWd4MocGTe/vs+0ebbh6D1w9EGci8V -+FBIavrOc9ZmI03wGIHLnHD+QgxLeTvQOnFENr0k9ah320b8UHdpbbvr3pRf8an5 -Q8GdVahd5gTY1CbkZGeaIlY8OWUG7s9mvGGDEswSsLIah0UjTNDpzexw1ibr+LDZ -tSH7GPdtwSniix47ywtv/qzVa8erCpJQ6rucUThR7o4jn3BcZJm0ZDGzZhWhpxjt -cCoGKqzr1xk= ------END CERTIFICATE----- diff --git a/acceptance/ssl/certs/mcollective-server.pem b/acceptance/ssl/certs/mcollective-server.pem deleted file mode 100644 index 7b26714e..00000000 --- a/acceptance/ssl/certs/mcollective-server.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFdDCCA1ygAwIBAgIBBTANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjEyMTgzNVoXDTIwMDMwMTEyMTgz -NVowHTEbMBkGA1UEAwwSbWNvbGxlY3RpdmUtc2VydmVyMIICIjANBgkqhkiG9w0B -AQEFAAOCAg8AMIICCgKCAgEAmb8vabpZIw3WHUXqV6HkOjmKXz5IgjhXBg5/XA6A -TGrvFlo5mj6W2JpE7CxI+DhHYZ4+Z5NLkzIAYoSXuV/0umuPpXOAH5ma5JYudzKd -5RV7RiffSj5LfzBnGZwn3dVVhaZiR6vUM4cmP8CqUqfoxvpweh9nGDbkEKoHdC4p -ENerwvuheAnPGflonDZFxaWEqXn5oXxSiuf88XZ0tgn1mMBn/JF0rXtCKYFfOAr7 -Evw1eUak+4wEHym26q+BCogKpeW+lT0C/v/TH5XG63ycyhmqiWjTI2vPab6BC7t2 -Bmgd5Grcr7cofvt4QYwIsIwL6ZZWahyLynHMBs85AAm1bfPdnMeia5WP+J98euAJ -7MK8TuX4sEjHt/yAiXT75aD0rsViP9BkKstBudMALokywnMHLa0/KdJCwXP5JH/Z -D8v4LJvpfGxDodC+tyUCsr82Wn67AAculvMZDjY6SD9LtaNrtiNZeNj2PXYBYWWs -CQ4aR2LzelDPDZ3TUu0BqMjNQ07zz/Daol6DASuF1TLUv6YW2tLZ5nggt1rkARx6 -m2BTEpa1Jl6j8KkE2l+7KR6EaUCwz3bqlvAweqY/8mnHrwhXkeeQ6Bta53o5YjXD -WFXTmZD/iSlT8hbnWmoww/EgRjsQyXZ9dS3OelsAPYBTKTwXsKIGdVgj4t8Xd9CK -gicCAwEAAaOBujCBtzA1BglghkgBhvhCAQ0EKFB1cHBldCBSdWJ5L09wZW5TU0wg -SW50ZXJuYWwgQ2VydGlmaWNhdGUwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQW -MBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRm -iqXTvKMtefbQHDRT3F9MVe9xtjAfBgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7 -nhV46zANBgkqhkiG9w0BAQsFAAOCAgEAWSNWAshqJtEeV1vfdlU581ppNmodUs7Q -6Ypdx0eot6S9lvTl69GFYonuOvwf8U6chs5Hp051oMB8M1PWzD7CZHj+wMwRKqvG -PWymZUsZQHFpb01tnABLL62fyqxEnaVPqUyRwNQMsXoD6OiW495kY+o17i2k6oYF -pE2ofyREQd40i3wGk7u0MlpWvSfdpcUgzVTTlruOuHh+E2HTf53PH3AuFDgqd8Ee -+2JCO2hcAT7JXfmlxzE6XtbalnaorBnJD09vRR/MshppN6l/11+yMDg3CpfkARBJ -OqSVLd8PD3BZm4jUWd3w7tBMs1WUYrtMtUKVGc2Ct4QyxCpi1bKKZRcrnROo3lLH -ZZGEYo+19KpCff/kOoBiyqkim8SN9cdy5nzEllGsIj+72mJuqRhkh58tlrTBUDl1 -8Sc1rRLZ+T6k2A/UWybkPMVFw+e1DFOtK8QvjwXPiZyNTDmf05uesgp8sJ81iebv -1llZu24x5gVobMHEeXGmKGGs6vquwTrs0/mAy4ujHhkEXZPRkrdv1uBt0sG969/5 -0Bnk+Lq0xxGDbgTt+8TOpV++cE3dU6K3Fb7JCJT8S6dzd/78+T+m13maW6WKdiZc -QAzFNkiw4D0qvdCoL8bu45P58tPFGdJtRbIQ83Ik1Ie6M8nXxCcq0qIttw83Od+H -qDxqCwAZL8E= ------END CERTIFICATE----- diff --git a/acceptance/ssl/certs/socks.local.pem b/acceptance/ssl/certs/socks.local.pem deleted file mode 100644 index b1d805a2..00000000 --- a/acceptance/ssl/certs/socks.local.pem +++ /dev/null @@ -1,32 +0,0 @@ ------BEGIN CERTIFICATE----- -MIIFmzCCA4OgAwIBAgIBAjANBgkqhkiG9w0BAQsFADAhMR8wHQYDVQQDDBZQdXBw -ZXQgQ0E6IHNvY2tzLmxvY2FsMB4XDTE1MDMwMjExNTYyMVoXDTIwMDMwMTExNTYy -MVowFjEUMBIGA1UEAwwLc29ja3MubG9jYWwwggIiMA0GCSqGSIb3DQEBAQUAA4IC -DwAwggIKAoICAQDePeeGJz7ozXK/j4SJKvyi6Sw7i6alPizI2uR/snx8tYv/V7uj -p3sgZ66uvBBW1Uz7lZpNxD19GOICZXF6m3UozafeGsjiffLA60KoEzL2NKoexIz8 -UDVzQtLfu6RsqnfKzFbq8vjFmwkd+mg64v6bkW3UBpKXySrEEGp/MUiAKEXyi+ab -R7FNH9rvmyr0TL0xDLpUDdR9KMueyltfXDlGyeI7+2GNq0vdSzqjftIhB7QIUeeV -05Xh6ioWQECoX2oQfTg3tssfRkQ7H/hBRvIg9dcn/ehAujdF4juATFUzhzXgBlcn -4QkYqsPAJj+Ax+FIHlZoCil0gGz8Qn0xdfGs+WUfe9p4w9CwxbT9s7hKgJzF4yux -hsb+1uDalvlblOMHBPZ5nAnYxwPELmdtaaDwI3pN+MFTVFvgmkoPdybQY8dTu2np -hJLfjyDs1n9c1spNb1aqv8RSAw8lWzJ7XBCcARD90tcdbxfKLytwz6JUC+hxqC2m -SbL8cHPcFRLoDNk6T5muVrszypMBYZpmgh1Bum3kPwm/nSRsM3RweBUSfsqr8xFW -U6ApNrdUclGha/yjORHNOiIiJFrEdrK/riO6yYyU/s/An8Vmo5HVtP4Lee1J2VcO -5vGVariJhEIsq+XwbLSe6dMk0gOPDAHujjxBBf8m/2yjpb+Ru0CUHUJSBwIDAQAB -o4HoMIHlMDUGCWCGSAGG+EIBDQQoUHVwcGV0IFJ1YnkvT3BlblNTTCBJbnRlcm5h -bCBDZXJ0aWZpY2F0ZTAsBgNVHREEJTAjggZwdXBwZXSCDHB1cHBldC5sb2NhbIIL -c29ja3MubG9jYWwwDgYDVR0PAQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQGCCsGAQUF -BwMBBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBRobiTuhmr7JT89 -XappKM3PKSknKzAfBgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7nhV46zANBgkq -hkiG9w0BAQsFAAOCAgEAZ2Nm54dwCWnaAGiP+eR+rcrz+tmKfeuqHQMR6jihIFsy -QSrTomBMaMmaw7wdSAyfusbAg6ZZ23lzjHUDUMdbzXNO8/zAKwt93JNdJdeGrpB9 -TxR8Bhb5Xv4axSQszCP/X0iBWm3KjbmhCNXjMqKZ1UuEt8K80bRT+a3YzdRJFNm8 -HmzWyZcN+ramkGIWQoAtjRmxYVOHOatlTOXnX3J5Yg8Z7BUTbtgPl4fGpTEu5fX+ -60uHJy2CB/jXUO1cPb9E7vVt/EPW1GjuYyQGfpG/2lNEGGkDDztbCL2Fn44BZrYO -HuIcmPumTmTR6BKxx5TBnaql2KbX7bvJm9KbBUEwE36KbvwvefcYiTBAtCBzBNXR -u/7BbfSvWHv2Mhtl6O4sF3sSUdhH+eilgO7tzvq1P5DcDGifdu6U8DWeBYGcaPFV -60QciJHZnyFjwoCcYthNIhfAaVjO89q2AFXCD0tlYGL+BLdxutlmHjM5WTau3pSW -cdqF3FWM8dVGbc5gkWce3PL3Am745tpncySH0JGn9cvBPfUK7aoba8r2/Z7BGS/C -EJWgJ+KWHJ1H7F91mImdr3lADvLIDvudb+NulloLdo8qE54csyOno4zPoM5G+5Nz -vcpC7tLkC+kJ8vcn+cnVx41NYwGuonGGCPg053yrcsmvd/wQHFVhiq3RSHZK318= ------END CERTIFICATE----- diff --git a/acceptance/ssl/crl.pem b/acceptance/ssl/crl.pem deleted file mode 100644 index f9230482..00000000 --- a/acceptance/ssl/crl.pem +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN X509 CRL----- -MIICmzCBhAIBATANBgkqhkiG9w0BAQUFADAhMR8wHQYDVQQDDBZQdXBwZXQgQ0E6 -IHNvY2tzLmxvY2FsFw0xNTAzMDMxMTU2MTlaFw0yMDAzMDExMTU2MjBaoC8wLTAf -BgNVHSMEGDAWgBRqr/VzStqL/9HXVt6Qy5P7nhV46zAKBgNVHRQEAwIBADANBgkq -hkiG9w0BAQUFAAOCAgEAOuf7KWxGzusauTsjVKFAc9/vZhMC9JcZUGPvlGENLMFz -Fn9kGh04Duq/HcO4KG4wS/VjO54wawQ8tcPXPOouVOXgoFLOUuEN5LQZZ7o9cOaF -pQ5e/1m6kveABW9kbwioZCXx1GivBEEuEaF2ZfneKqO3p6eTlnwaM400PypL3c4c -L/CvDqTgH7F9tPC6rhNJ8iQQHRRKfmpEAF2Ej5i6FPJMDT4CIq13gQkJVlXVp6xB -AxfKXcMCc129NyRq3/Jjrc58/PRdkFxR/0MdN1EX7q2DnlzVDw4TU1ow4IVexFXK -SQKJZmBgGYckIrQRumJatkeNB4k4aeSEesHkppZ7w57wAhsW1GTXZCdPs6Saqs3v -I44btc3N8kTk0syw7nvl6ifJpa3UNJdJ3HnNrPyDEnKJQ3eMRG1kslRJnFnKC/y/ -7C55cxL1wzCm+O/ks0MVG8QwK59Ooowlztimm7X9EZPOiga010lLgWXNN54Cha9u -AYo4rprqY/sK12eayhp0nodOGrpHV6PqhvtDfymoIT/hvocpxpPl80pYYO86+72d -G012NdlG1Pa3/okBIfSw6/JZLiv/MiE0JFaob/xdnWPCD8UpZq8LYjIhOv5RnYBj -VMisIczo6JRCwHhUB0mvK+afrEWgAodIve0ZNYAdeZ90NusDXBcA7ka9lUpau1E= ------END X509 CRL----- diff --git a/acceptance/ssl/private_keys/activemq.pem b/acceptance/ssl/private_keys/activemq.pem deleted file mode 100644 index d7d582a8..00000000 --- a/acceptance/ssl/private_keys/activemq.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/acceptance/ssl/private_keys/mcollective-client.pem b/acceptance/ssl/private_keys/mcollective-client.pem deleted file mode 100644 index 00e37304..00000000 --- a/acceptance/ssl/private_keys/mcollective-client.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/acceptance/ssl/private_keys/mcollective-server.pem b/acceptance/ssl/private_keys/mcollective-server.pem deleted file mode 100644 index 98bee06d..00000000 --- a/acceptance/ssl/private_keys/mcollective-server.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/acceptance/ssl/private_keys/socks.local.pem b/acceptance/ssl/private_keys/socks.local.pem deleted file mode 100644 index 8917e1a2..00000000 --- a/acceptance/ssl/private_keys/socks.local.pem +++ /dev/null @@ -1,51 +0,0 @@ ------BEGIN RSA PRIVATE KEY----- -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 ------END RSA PRIVATE KEY----- diff --git a/acceptance/ssl/public_keys/activemq.pem b/acceptance/ssl/public_keys/activemq.pem deleted file mode 100644 index 41ecb6ec..00000000 --- a/acceptance/ssl/public_keys/activemq.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA7tZf24Tg3fkcIIdQH41a -2rka3BJpll+DGvQIvbTs0hKh5AxXP2i3jeCPpkhlxQLjsjdr7lxVZRL+Ueck/DdS -ERUOTkeaFJrhHHvBNkDGVnZqLA5EwRlTBUY5C3uKKw8KRzgBhm59YiCZwU4impRg -f9Vz3WkxkgjUO/7uR/wLXieAZP+cNPCHGLWodszzmdqV/6eXU/UsbSjX7mF025n9 -eDxel+N//ssXCNkHJUwy4rdQ4cV1Rf4f1O33yTFhVtrf4JrX1znEYzB8urplSiOu -yNUOxU50m0g5sPOtZpkBAhmvUxtpQ29zjksB9G1h4/mq28UuQeu4oRMATAxkchgG -jHGm4lJZeAQ3ehzeNh+kNwQuaIaFIDzjgcTB5M7cDVJdPCP7LT8ONJ7nSVfykQus -3OMGh+Yr2QIUM8smn3gw4Ta09aNovyPlC7A2e0f6vmkPpYMhld3D2K6Kz5/2k1hl -uGaNbhBR7pklVH3KSteNQ8FPnyPInRmw4lxC1e5kVjikqydcsKW08X4YErZzIy0c -gHeYO7WikPCJlpwsqmDkAV2URhjcqBtHY884qPtnlntUEGTqSnjJuU9NKsgSYun0 -E7gDyyxiG2TEuVpli779NKN9dpcNmudyHJm8B9Xnnwb8gQxDZJWaYCodEQQy2eYi -LZhbQz6hClc45xTG9rJvdiMCAwEAAQ== ------END PUBLIC KEY----- diff --git a/acceptance/ssl/public_keys/mcollective-client.pem b/acceptance/ssl/public_keys/mcollective-client.pem deleted file mode 100644 index ffedf74a..00000000 --- a/acceptance/ssl/public_keys/mcollective-client.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAopc/qeMD7V4sQSeLjI71 -Na9gabTXC5dxDV5T/qoMZOGVInuMUVEjWWYM/Pjsj+/rEqjcN+0N8sZHpY9sMkbq -B97I9c0A/zdVskF8EGVe7vS/ytUIUtKwONffD0/FEPNpIDUc++rSj5PPgJvn35Ob -hB7Q2MpXLx6iamD1gnyFfmrssnv6/f5esjV/n61YaskLd1zQD6t7Zghdkzjy6pFz -/AfGqWdzq4r2D5ANnsTOWAH1qWyphv8Jbwf7vE+H8x7jySmwrxmHu/PDiqZMz28Z -RTVjWBWY67hI7Z/p1otQkzoFFf7XLNfNuyajcsG1YCaGXwbBHdb5aa+leLKLzPd1 -Xy1X0/h4MEG63xFzkrmlB7PQZHbzVHdi1lI4tI6erUyb3RjsIT2EE8ED732iDIPZ -duDg5I1yOUKa7R8wEYVERyHv7V6Hbo2ydJu/oIMwJZ0oGlzK1T1xpEsg8t7wgcLK -UY7n/+zG23dLOh49/ubm+ecC3QbN1z3pMc9pOceTwkWCi6vGf0Otd1TrraHAlK22 -dzkfQqFK2mKMegoQZXSWHPd5JAdSy4qnXCJPxGJaStQGDii5SndFWiuduKlQpSE6 -WSgMdiBwncAFmdYsnlNMgY0wHL/ufjWhsVdePVrEagNux6PdLdmolWawoHxtNh4U -uwyByiyJU8gF7A6f/DoAH8cCAwEAAQ== ------END PUBLIC KEY----- diff --git a/acceptance/ssl/public_keys/mcollective-server.pem b/acceptance/ssl/public_keys/mcollective-server.pem deleted file mode 100644 index e1ce57b1..00000000 --- a/acceptance/ssl/public_keys/mcollective-server.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAmb8vabpZIw3WHUXqV6Hk -OjmKXz5IgjhXBg5/XA6ATGrvFlo5mj6W2JpE7CxI+DhHYZ4+Z5NLkzIAYoSXuV/0 -umuPpXOAH5ma5JYudzKd5RV7RiffSj5LfzBnGZwn3dVVhaZiR6vUM4cmP8CqUqfo -xvpweh9nGDbkEKoHdC4pENerwvuheAnPGflonDZFxaWEqXn5oXxSiuf88XZ0tgn1 -mMBn/JF0rXtCKYFfOAr7Evw1eUak+4wEHym26q+BCogKpeW+lT0C/v/TH5XG63yc -yhmqiWjTI2vPab6BC7t2Bmgd5Grcr7cofvt4QYwIsIwL6ZZWahyLynHMBs85AAm1 -bfPdnMeia5WP+J98euAJ7MK8TuX4sEjHt/yAiXT75aD0rsViP9BkKstBudMALoky -wnMHLa0/KdJCwXP5JH/ZD8v4LJvpfGxDodC+tyUCsr82Wn67AAculvMZDjY6SD9L -taNrtiNZeNj2PXYBYWWsCQ4aR2LzelDPDZ3TUu0BqMjNQ07zz/Daol6DASuF1TLU -v6YW2tLZ5nggt1rkARx6m2BTEpa1Jl6j8KkE2l+7KR6EaUCwz3bqlvAweqY/8mnH -rwhXkeeQ6Bta53o5YjXDWFXTmZD/iSlT8hbnWmoww/EgRjsQyXZ9dS3OelsAPYBT -KTwXsKIGdVgj4t8Xd9CKgicCAwEAAQ== ------END PUBLIC KEY----- diff --git a/acceptance/ssl/public_keys/socks.local.pem b/acceptance/ssl/public_keys/socks.local.pem deleted file mode 100644 index 2204ff8a..00000000 --- a/acceptance/ssl/public_keys/socks.local.pem +++ /dev/null @@ -1,14 +0,0 @@ ------BEGIN PUBLIC KEY----- -MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA3j3nhic+6M1yv4+EiSr8 -ouksO4umpT4syNrkf7J8fLWL/1e7o6d7IGeurrwQVtVM+5WaTcQ9fRjiAmVxept1 -KM2n3hrI4n3ywOtCqBMy9jSqHsSM/FA1c0LS37ukbKp3ysxW6vL4xZsJHfpoOuL+ -m5Ft1AaSl8kqxBBqfzFIgChF8ovmm0exTR/a75sq9Ey9MQy6VA3UfSjLnspbX1w5 -RsniO/thjatL3Us6o37SIQe0CFHnldOV4eoqFkBAqF9qEH04N7bLH0ZEOx/4QUby -IPXXJ/3oQLo3ReI7gExVM4c14AZXJ+EJGKrDwCY/gMfhSB5WaAopdIBs/EJ9MXXx -rPllH3vaeMPQsMW0/bO4SoCcxeMrsYbG/tbg2pb5W5TjBwT2eZwJ2McDxC5nbWmg -8CN6TfjBU1Rb4JpKD3cm0GPHU7tp6YSS348g7NZ/XNbKTW9Wqr/EUgMPJVsye1wQ -nAEQ/dLXHW8Xyi8rcM+iVAvocagtpkmy/HBz3BUS6AzZOk+Zrla7M8qTAWGaZoId -Qbpt5D8Jv50kbDN0cHgVEn7Kq/MRVlOgKTa3VHJRoWv8ozkRzToiIiRaxHayv64j -usmMlP7PwJ/FZqOR1bT+C3ntSdlXDubxlWq4iYRCLKvl8Gy0nunTJNIDjwwB7o48 -QQX/Jv9so6W/kbtAlB1CUgcCAwEAAQ== ------END PUBLIC KEY----- diff --git a/acceptance/tests/mco_ping.rb b/acceptance/tests/mco_ping.rb deleted file mode 100644 index b94b1297..00000000 --- a/acceptance/tests/mco_ping.rb +++ /dev/null @@ -1,12 +0,0 @@ -test_name "mco ping" do - step "run ping" - if mco_master.platform =~ /windows/ then - mco_bin = 'cmd.exe /c mco.bat' - else - mco_bin = 'mco' - end - on(mco_master, "#{mco_bin} ping") do - assert_equal(0, exit_code) - assert_match(/^#{hosts.size}\s+replies/, stdout) - end -end diff --git a/acceptance/tests/mco_puppet_batch_count.rb b/acceptance/tests/mco_puppet_batch_count.rb deleted file mode 100644 index 2daa43e1..00000000 --- a/acceptance/tests/mco_puppet_batch_count.rb +++ /dev/null @@ -1,12 +0,0 @@ -test_name "mco puppet count" do - step "run puppet count" - if mco_master.platform =~ /windows/ then - mco_bin = 'cmd.exe /c mco.bat' - else - mco_bin = 'mco' - end - on(mco_master, "#{mco_bin} puppet count --batch=1") do - assert_equal(0, exit_code) - assert_match(/^Total Puppet nodes:\s+#{hosts.size}/, stdout) - end -end diff --git a/acceptance/tests/mco_puppet_count.rb b/acceptance/tests/mco_puppet_count.rb deleted file mode 100644 index 6c81af80..00000000 --- a/acceptance/tests/mco_puppet_count.rb +++ /dev/null @@ -1,12 +0,0 @@ -test_name "mco puppet count" do - step "run puppet count" - if mco_master.platform =~ /windows/ then - mco_bin = 'cmd.exe /c mco.bat' - else - mco_bin = 'mco' - end - on(mco_master, "#{mco_bin} puppet count") do - assert_equal(0, exit_code) - assert_match(/^Total Puppet nodes:\s+#{hosts.size}/, stdout) - end -end diff --git a/acceptance/tests/mco_puppet_exec.rb b/acceptance/tests/mco_puppet_exec.rb deleted file mode 100644 index 8aff6ff9..00000000 --- a/acceptance/tests/mco_puppet_exec.rb +++ /dev/null @@ -1,93 +0,0 @@ -require 'yaml' - -test_name "mco puppet exec" do - - testdir = master.tmpdir('mco_puppet_exec') - - step "Add manifest to host classification" do - apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) - File { - ensure => directory, - mode => "0750", - owner => #{master.puppet['user']}, - group => #{master.puppet['group']}, - } - file { - '#{testdir}':; - '#{testdir}/environments':; - '#{testdir}/environments/production':; - '#{testdir}/environments/production/manifests':; - '#{testdir}/environments/production/manifests/site.pp': - ensure => file, - mode => "0640", - content => ' - node default { - exec { "hostname": - path => ["/bin", "/usr/bin", "C:/cygwin32/bin", "C:/cygwin64/bin", "C:/cygwin/bin"], - logoutput => true, - } - } - '; - } -MANIFEST - - end - - master_opts = { - 'main' => { - 'environmentpath' => "#{testdir}/environments", - } - } - - def last_run_report(h) - if h['platform'] =~ /windows/ - 'C:/ProgramData/PuppetLabs/puppet/cache' - else - '/opt/puppetlabs/puppet/cache' - end + '/state/last_run_report.yaml' - end - - with_puppet_running_on(master, master_opts) do - if mco_master.platform =~ /windows/ then - mco_bin = 'cmd.exe /c mco.bat' - else - mco_bin = 'mco' - end - - step "Stub puppet and run agent to get certs" - hosts.each do |host| - stub_hosts_on(host, 'puppet' => master.ip) - on(host, "rm -f #{last_run_report(host)}") - end - - step "Run mco puppet runonce" - on(mco_master, "#{mco_bin} puppet runonce") - sleep 30 - - step "Verify that hostname results logged" - hosts.each do |h| - cmd = "cat #{last_run_report(h)}" - (1..10).each do |iter| - @res = on(h, cmd, :acceptable_exit_codes => (0..254)) - unless @res.exit_code == 0 - sleep (2**iter)/2 - else - break - end - end - - assert_equal(0, @res.exit_code, 'puppet failed to run') - - # Parse the last run report, ignoring object tags - data = YAML.parse(@res.stdout) - data.root.each do |o| - o.tag = nil if o.respond_to?(:tag=) - end - data = data.to_ruby - - hostname = on(h, 'hostname').stdout.chomp - expected = data['logs'].select {|log| log['source'] =~ /Exec\[hostname\]/}.select {|log| log['message'] == hostname} - assert_equal(1, expected.count, 'puppet failed to exec hostname') - end - end -end diff --git a/acceptance/tests/mco_puppet_powershell.rb b/acceptance/tests/mco_puppet_powershell.rb deleted file mode 100644 index 7f4a5ccb..00000000 --- a/acceptance/tests/mco_puppet_powershell.rb +++ /dev/null @@ -1,99 +0,0 @@ -test_name "MCOP-521 mco puppet run with powershell provider" do - - testdir = master.tmpdir('mco_powershell') - testfile = master.tmpfile('mco_powershell').split('/')[-1] - - # gather array of windows hosts - windows_hosts = [] - hosts.each do |h| - if /windows/ =~ h[:platform] - windows_hosts << h - end - end - if windows_hosts.empty? - skip_test "No windows hosts to test powershell on" - end - - - step "Add powershell manifest to windows host classification" do - node_str = '' - windows_hosts.each do |h| - n =<<-EOS - node #{h} { - exec { "create-test-file": - command => "\\'$::fqdn\\' | Out-file C:\\#{testfile}.txt -Encoding ascii", - provider => powershell, - } - } -EOS - node_str << n - end - - apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) - File { - ensure => directory, - mode => "0750", - owner => #{master.puppet['user']}, - group => #{master.puppet['group']}, - } - file { - '#{testdir}':; - '#{testdir}/environments':; - '#{testdir}/environments/production':; - '#{testdir}/environments/production/manifests':; - '#{testdir}/environments/production/manifests/site.pp': - ensure => file, - mode => "0640", - content => ' - #{node_str} - node default {} - '; - } -MANIFEST - - end - - master_opts = { - 'main' => { - 'environmentpath' => "#{testdir}/environments", - } - } - - with_puppet_running_on(master, master_opts) do - if mco_master.platform =~ /windows/ then - mco_bin = 'cmd.exe /c mco.bat' - else - mco_bin = 'mco' - end - - step "Stub puppet and run agent to get certs" - hosts.each do |host| - stub_hosts_on(host, 'puppet' => master.ip) - end - - step "Install powershell module on master" - install_puppet_module_via_pmt_on(master, {:module_name => "puppetlabs-powershell"}) - - step "Run mco puppet runonce" - on(mco_master, "#{mco_bin} puppet runonce") - sleep 45 - - step "Verify that powershell created file on windows systems" - cmd = "cmd.exe /c \"dir c:\\#{testfile}.txt\"" - windows_hosts.each do |h| - (1..10).each do |iter| - res = on(h, cmd, :acceptable_exit_codes => (0..254)) - unless res.exit_code == 0 - sleep (2**iter)/2 - else - break - end - end - result = on(h, cmd, :acceptable_exit_codes => (0..254)) - assert_equal(0, result.exit_code, 'mco failed to create file using powershell') - res = on h, "cat 'C:/#{testfile}.txt'" - assert_match(h.hostname, res.stdout, "FQDN fact not included in file generated by mco") - end - end - -end diff --git a/acceptance/tests/mco_puppet_runonce.rb b/acceptance/tests/mco_puppet_runonce.rb deleted file mode 100644 index d457d9f3..00000000 --- a/acceptance/tests/mco_puppet_runonce.rb +++ /dev/null @@ -1,88 +0,0 @@ -test_name "mco puppet runonce" do - - testdir = master.tmpdir('mco_puppet_runonce') - @testfilename = master.tmpfile('mco_puppet_runonce').split('/')[-1] - - def testfile(h) - if /windows/ =~ h[:platform] - "C:/#{@testfilename}" - else - "/tmp/#{@testfilename}" - end - end - - step "Add manifest to host classification" do - node_str = '' - hosts.each do |h| - n =<<-EOS - node #{h} { - file { "#{testfile(h)}": - ensure => file, - } - } -EOS - node_str << n - end - - apply_manifest_on(master, <<-MANIFEST, :catch_failures => true) - File { - ensure => directory, - mode => "0750", - owner => #{master.puppet['user']}, - group => #{master.puppet['group']}, - } - file { - '#{testdir}':; - '#{testdir}/environments':; - '#{testdir}/environments/production':; - '#{testdir}/environments/production/manifests':; - '#{testdir}/environments/production/manifests/site.pp': - ensure => file, - mode => "0640", - content => ' - #{node_str} - node default {} - '; - } -MANIFEST - - end - - master_opts = { - 'main' => { - 'environmentpath' => "#{testdir}/environments", - } - } - - with_puppet_running_on(master, master_opts) do - if mco_master.platform =~ /windows/ then - mco_bin = 'cmd.exe /c mco.bat' - else - mco_bin = 'mco' - end - - step "Stub puppet and run agent to get certs" - hosts.each do |host| - stub_hosts_on(host, 'puppet' => master.ip) - end - - step "Run mco puppet runonce" - on(mco_master, "#{mco_bin} puppet runonce") - sleep 30 - - step "Verify that file created on systems" - hosts.each do |h| - cmd = "ls #{testfile(h)}" - (1..10).each do |iter| - res = on(h, cmd, :acceptable_exit_codes => (0..254)) - unless res.exit_code == 0 - sleep (2**iter)/2 - else - break - end - end - result = on(h, cmd, :acceptable_exit_codes => (0..254)) - assert_equal(0, result.exit_code, 'mco failed to create file') - end - end -end diff --git a/acceptance/tests/mco_reconnect.rb b/acceptance/tests/mco_reconnect.rb deleted file mode 100644 index ae02527b..00000000 --- a/acceptance/tests/mco_reconnect.rb +++ /dev/null @@ -1,36 +0,0 @@ -test_name "mco servers should reconnect if cut-off from broker" do - skip_test "Can't manipulate firewall rules easily on Windows" if mco_master.platform =~ /windows/ - - # Add firewall rules blocking the source ports for the current incoming connections. - # When nodes attempt to reconnect, they'll grab a new outgoing port and should bypass the firewall rules. - step "disconnect servers" do - hosts.reject {|host| host == mco_master}.each do |host| - ip = fact_on(host, 'ipaddress') - mco_port = on(mco_master, "netstat -anpt|grep 61613|grep ESTAB|grep -Po '#{ip}:\\K\\d+'").stdout.chomp - step "blocking traffic to #{ip}:#{mco_port}" do - on(mco_master, "iptables -A INPUT -p tcp --sport #{mco_port} -j DROP") - on(mco_master, "iptables -A OUTPUT -p tcp --dport #{mco_port} -j DROP") - end - end - - on(mco_master, 'iptables -L') - teardown do - on(mco_master, 'iptables -F') - end - end - - step "check unreachable ping" do - on(mco_master, "mco ping") do - assert_equal(0, exit_code) - assert_match(/^1\s+replies/, stdout) - end - end - - # It takes 2 heartbeat failures before stomp will retry. The minimum configurable heartbeat is 30 seconds, - # so 30 is the minimum time before mcollective could reconnect. - sleep 30 - - step "check for reconnection" do - retry_on(mco_master, "mco ping | grep '#{hosts.size} replies'", {:max_retries => 10, :retry_interval => 5}) - end -end diff --git a/bin/mcollectived b/bin/mcollectived deleted file mode 100755 index d4c9a5f9..00000000 --- a/bin/mcollectived +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/env ruby - -# For security reasons, ensure that '.' is not on the load path -# This is primarily for 1.8.7 since 1.9.2+ doesn't put '.' on the load path -$LOAD_PATH.delete '.' - -require 'mcollective' -require 'mcollective/runner' -require 'getoptlong' - -opts = GetoptLong.new( - [ '--daemonize', GetoptLong::NO_ARGUMENT], - [ '--no-daemonize', GetoptLong::NO_ARGUMENT], - [ '--help', '-h', GetoptLong::NO_ARGUMENT ], - [ '--config', '-c', GetoptLong::REQUIRED_ARGUMENT], - [ '--pidfile', '-p', GetoptLong::REQUIRED_ARGUMENT] -) - -daemonize = nil - -if MCollective::Util.windows? - configfile = File.join(MCollective::Util.windows_prefix, "etc", "server.cfg") -else - # search for the server.cfg in the AIO path then the traditional one - configfiles = ['/etc/puppetlabs/mcollective/server.cfg', - '/etc/mcollective/server.cfg' ] - - found = configfiles.find_index { |file| File.readable?(file) } - - # didn't find any? default to the first - if found.nil? - found = 0 - end - - configfile = configfiles[found] -end -pid = "" - -opts.each do |opt, arg| - case opt - when '--help' - puts "Usage: mcollectived.rb [--config /path/to/config] [--pidfile /path/to/pid] [--daemonize | --no-daemonize]" - exit - when '--config' - configfile = arg - when '--pidfile' - pid = arg - when '--daemonize' - daemonize = true - when '--no-daemonize' - daemonize = false - end -end - -config = MCollective::Config.instance - -config.loadconfig(configfile) unless config.configured - -# If daemonize has not been set on the cli, reach into the config file to decide -if daemonize == nil - daemonize = config.daemonize -end - -MCollective::Log.info("The Marionette Collective #{MCollective::VERSION} started logging at #{config.loglevel} level") - -if daemonize - MCollective::Log.debug("Starting in the background (#{config.daemonize})") - - if MCollective::Util.windows? - require 'mcollective/windows_daemon' - MCollective::WindowsDaemon.daemonize_runner - else - require 'mcollective/unix_daemon' - MCollective::UnixDaemon.daemonize_runner(pid) - end -else - MCollective::Log.debug("Starting in the foreground") - runner = MCollective::Runner.new(configfile) - runner.main_loop -end diff --git a/ext/Makefile b/ext/Makefile deleted file mode 100644 index f8246b40..00000000 --- a/ext/Makefile +++ /dev/null @@ -1,43 +0,0 @@ -#!/usr/bin/make -f - -DESTDIR= - -build: - -clean: - -install: install-bin install-lib install-conf install-plugins install-doc - -install-bin: - install -d $(DESTDIR)/usr/sbin - install -d $(DESTDIR)/usr/bin - cp bin/mco $(DESTDIR)/usr/bin - cp bin/mcollectived $(DESTDIR)/usr/sbin/mcollectived - -install-lib: - install -d $(DESTDIR)/usr/lib/ruby/1.8/ - cp -a lib/* $(DESTDIR)/usr/lib/ruby/1.8/ - -install-conf: - install -d $(DESTDIR)/etc/mcollective/ - install -d $(DESTDIR)/etc/init.d - cp -r etc/* $(DESTDIR)/etc/mcollective/ - cp mcollective.init $(DESTDIR)/etc/init.d/mcollective - rm $(DESTDIR)/etc/mcollective/ssl/PLACEHOLDER - rm $(DESTDIR)/etc/mcollective/ssl/clients/PLACEHOLDER - -install-plugins: - install -d $(DESTDIR)/usr/share/mcollective/ - cp -a plugins $(DESTDIR)/usr/share/mcollective/ - -install-doc: - install -d $(DESTDIR)/usr/share/doc/ - cp -a doc $(DESTDIR)/usr/share/doc/mcollective - -uninstall: - rm -f $(DESTDIR)/usr/sbin/mcollectived - rm -rf $(DESTDIR)/usr/lib/ruby/1.8/mcollective* - rm -rf $(DESTDIR)/usr/share/mcollective - rm -rf $(DESTDIR)/etc/mcollective - -.PHONY: build clean install uninstall diff --git a/ext/action_helpers/perl/.gitignore b/ext/action_helpers/perl/.gitignore deleted file mode 100644 index 0b5bd399..00000000 --- a/ext/action_helpers/perl/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -Makefile -blib -pm_to_blib diff --git a/ext/action_helpers/perl/Makefile.PL b/ext/action_helpers/perl/Makefile.PL deleted file mode 100644 index 6acf3a42..00000000 --- a/ext/action_helpers/perl/Makefile.PL +++ /dev/null @@ -1,9 +0,0 @@ -#!perl -use strict; -use ExtUtils::MakeMaker; -WriteMakefile( - NAME => "MCollective::Action", - PREREQ_PM => { - "JSON" => 0, - }, -); diff --git a/ext/action_helpers/perl/lib/MCollective/Action.pm b/ext/action_helpers/perl/lib/MCollective/Action.pm deleted file mode 100644 index 05d536fe..00000000 --- a/ext/action_helpers/perl/lib/MCollective/Action.pm +++ /dev/null @@ -1,158 +0,0 @@ -package MCollective::Action; -use strict; -use warnings; -use JSON; - -=head1 NAME - -MCollective::Action - helper class for writing mcollective actions in perl - -=head1 SYNOPSIS - - -In your mcollective agent - - action "echo" do - validate :message, String - - implemented by "/tmp/echo.perl" - end - -And C - - #!/usr/bin/env perl - use strict; - use MCollective::Action; - - my $mc = MCollective::Action->new; - $mc->reply->{message} = $mc->request->{message}; - $mc->reply->{timestamp} = time; - $mc->info("some text to log on the server"); - - -=head1 DESCRIPTION - -mcollective version 1.X introduced a mechanism for writing agent actions as -external commands. This module provides a convenient api for writing them in -perl which performs some of the boilerplate for you. - -=head2 METHODS - -=over - -=item new - -create a new MCollection::Action helper object - -=cut - -sub new { - my $class = shift; - my $self = bless { - request => {}, - reply => {}, - }, $class; - $self->_load; - return $self; -} - -=item request - -returns a hash reference containing the request - -=cut - - -sub request { $_[0]->{request} } - - -=item reply - -returns a hash reference you should populate with your reply - -=cut - -sub reply { $_[0]->{reply} } - - -sub _load { - my $self = shift; - my $file = $ENV{MCOLLECTIVE_REQUEST_FILE}; - open my $fh, "<$file" - or die "Can't open '$file': $!"; - my $json = do { local $/; <$fh> }; - $self->{request} = JSON->new->decode( $json ); - delete $self->request->{data}{process_results}; -} - -sub DESTROY { - my $self = shift; - $self->_save; -} - -sub _save { - my $self = shift; - my $file = $ENV{MCOLLECTIVE_REPLY_FILE}; - open my $fh, ">$file" - or die "Can't open '$file': $!"; - print $fh JSON->new->encode( $self->reply ); -} - -=item info($message) - -report a message into the server log - -=cut - -sub info { - my ($self, $message) = @_; - print STDOUT $message, "\n"; -} - -=item error($message) - -report an error into the server log - -=cut - - -sub error { - my ($self, $message) = @_; - print STDERR $message, "\n"; -} - -=item fail - -reports an error and exits immediately - -=cut - -sub fail { - my ($self, $message) = @_; - $self->error( $message ); - exit 1; -} - -1; - -__END__ - -=back - -=head1 AUTHOR - -Richard Clamp - -=head1 COPYRIGHT - -Copyright 2011, Richard Clamp. All Rights Reserved. - -This program is free software; you can redistribute it -and/or modify it under the same terms as Perl itself. - -=head1 SEE ALSO - -https://docs.puppetlabs.com/mcollective/ - -=cut - diff --git a/ext/action_helpers/perl/t/basic.t b/ext/action_helpers/perl/t/basic.t deleted file mode 100644 index 4717efb5..00000000 --- a/ext/action_helpers/perl/t/basic.t +++ /dev/null @@ -1,30 +0,0 @@ -#!perl -use strict; -use Test::More; -use JSON; -use File::Temp; - -my $class = "MCollective::Action"; -use_ok( $class ); - -my $infile = File::Temp->new; -my $outfile = File::Temp->new; - -$ENV{MCOLLECTIVE_REQUEST_FILE} = $infile->filename; -$ENV{MCOLLECTIVE_REPLY_FILE} = $outfile->filename; -print $infile JSON->new->encode({ red => "apples", blue => "moon" }); -close $infile; -{ - my $mc = $class->new; - isa_ok( $mc, $class ); - is( $mc->request->{red}, "apples", "apples are red" ); - $mc->reply->{potato} = "chips"; -} - -my $json = do { local $/; <$outfile> }; -ok( $json, "Got some JSON" ); -my $reply = JSON->new->decode( $json ); - -is( $reply->{potato}, "chips", "Got the reply that potato = chips" ); - -done_testing(); diff --git a/ext/action_helpers/php/README.markdown b/ext/action_helpers/php/README.markdown deleted file mode 100644 index ad677f66..00000000 --- a/ext/action_helpers/php/README.markdown +++ /dev/null @@ -1,38 +0,0 @@ -A simple helper to assist with writing MCollective actions in PHP. - -Given an action as below: - -
-action "echo" do
-   validate :message, String
-
-   implemented_by "/tmp/echo.php"
-end
-
- -The following PHP script will implement the echo action externally -replying with _message_ and _timestamp_ - -
-<?php
-    require("mcollective_action.php");
-
-    $mc = new MCollectiveAction();
-    $mc->message = $mc->data["message"];
-    $mc->timestamp = strftime("%c");
-    $mc->info("some text to info log on the server");
-?>
-
- -Calling it with _mco rpc_ results in: - -
-$ mco rpc test echo message="hello world"
-Determining the amount of hosts matching filter for 2 seconds .... 1
-
- * [ ============================================================> ] 1 / 1
-
-
-nephilim.ml.org                         : OK
-    {:message=>"hello world", :time=>"Tue Mar 15 19:20:53 +0000 2011"}
-
diff --git a/ext/action_helpers/php/mcollective_action.php b/ext/action_helpers/php/mcollective_action.php deleted file mode 100644 index 64876688..00000000 --- a/ext/action_helpers/php/mcollective_action.php +++ /dev/null @@ -1,65 +0,0 @@ -infile = $_ENV["MCOLLECTIVE_REQUEST_FILE"]; - $this->outfile = $_ENV["MCOLLECTIVE_REPLY_FILE"]; - - $this->readJSON(); - } - - function __destruct() { - $this->save(); - } - - function readJSON() { - $this->request = json_decode(file_get_contents($this->infile), true); - unset($this->request["data"]["process_results"]); - } - - function save() { - file_put_contents($this->outfile, json_encode($this->request["data"])); - } - - // prints a line to STDERR that will log at error level in the - // mcollectived log file - function error($msg) { - fwrite(STDERR, "$msg\n"); - } - - // prints a line to STDOUT that will log at info level in the - // mcollectived log file - function info($msg) { - fwrite(STDOUT, "$msg\n"); - } - - // logs an error message and exits with RPCAborted - function fail($msg) { - $this->error($msg); - exit(1); - } - - function __get($property) { - if (isSet($this->request[$property])) { - return $this->request[$property]; - } else { - throw new Exception("No $property in request"); - } - } - - function __set($property, $value) { - $this->request["data"][$property] = $value; - } -} -?> diff --git a/ext/action_helpers/python/kwilczynski/README.markdown b/ext/action_helpers/python/kwilczynski/README.markdown deleted file mode 100644 index d73f3fb2..00000000 --- a/ext/action_helpers/python/kwilczynski/README.markdown +++ /dev/null @@ -1,46 +0,0 @@ -A simple helper to assist with writing MCollective actions in Python. - -Given an action as below: - -
-action "echo" do
-   validate :message, String
-
-   implemented_by "/tmp/echo.py"
-end
-
- -The following Python script will implement the echo action externally -replying with _message_ and current _time_. - -
-#!/usr/bin/env python
-
-import sys
-import time
-import mcollective_action as mc
-
-if __name__ == '__main__':
-    mc = mc.MCollectiveAction()
-    request = mc.request()
-    mc.message = request['data']['message']
-    mc.time = time.strftime('%c')
-    mc.info("Some text to info log in the server")
-
-    sys.exit(0)
-
- -Calling it with _mco rpc_ results in: - -
-$ mco rpc test echo message="Hello World"
-Determining the amount of hosts matching filter for 2 seconds .... 1
-
- * [ ============================================================> ] 1 / 1
-
-
-host.example.com              : OK
-    {:message=>"Hello World", :time=>"Tue Mar 15 19:20:53 +0000 2011"}
-
- -This implementation was successfully tested with Python 2.4 and 2.6. diff --git a/ext/action_helpers/python/kwilczynski/echo.py b/ext/action_helpers/python/kwilczynski/echo.py deleted file mode 100644 index bc22b635..00000000 --- a/ext/action_helpers/python/kwilczynski/echo.py +++ /dev/null @@ -1,16 +0,0 @@ -#!/usr/bin/env python - -import sys -import time -import mcollective_action as mc - -if __name__ == '__main__': - mc = mc.MCollectiveAction() - request = mc.request() - mc.message = request['data']['message'] - mc.time = time.strftime('%c') - mc.info("An example echo agent") - - sys.exit(0) - -# vim: set ts=4 sw=4 et : diff --git a/ext/action_helpers/python/kwilczynski/mcollective_action.py b/ext/action_helpers/python/kwilczynski/mcollective_action.py deleted file mode 100644 index 84c182a1..00000000 --- a/ext/action_helpers/python/kwilczynski/mcollective_action.py +++ /dev/null @@ -1,112 +0,0 @@ -#!/usr/bin/env python - -import os -import sys - -class Error(Exception): - pass - -class MissingModule(Error): - pass - -class MissingFiles(Error): - pass - -class MissingEnvironemntVariable(Error): - pass - -class FileReadError(Error): - pass - -class JSONParsingError(Error): - pass - -try: - import simplejson as json -except ImportError: - raise MissingModule('Unable to load JSON module. Missing module?') - -class MCollectiveAction(object): - - _environment_variables = [ 'MCOLLECTIVE_REQUEST_FILE', - 'MCOLLECTIVE_REPLY_FILE' ] - - def __init__(self): - self._info = sys.__stdout__ - self._error = sys.__stderr__ - - for entry in '_reply', '_request': - self.__dict__[entry] = {} - - self._arguments = sys.argv[1:] - - if len(self._arguments) < 2: - try: - for variable in self._environment_variables: - self._arguments.append(os.environ[variable]) - except KeyError: - raise MissingEnvironemntVariable("Environment variable `%s' " - "is not set." % variable) - - self._request_file, self._reply_file = self._arguments - - if len(self._request_file) == 0 or len(self._reply_file) == 0: - raise MissingFiles("Both request and reply files have to be set.") - - def __setattr__(self, name, value): - if name.startswith('_'): - object.__setattr__(self, name, value) - else: - self.__dict__['_reply'][name] = value - - def __getattr__(self, name): - if name.startswith('_'): - return self.__dict__.get(name, None) - else: - return self.__dict__['_reply'].get(name, None) - - def __del__(self): - if self._reply: - try: - file = open(self._reply_file, 'w') - json.dump(self._reply, file) - file.close() - except IOError, error: - raise FileReadError("Unable to open reply file `%s': %s" % - (self._reply_file, error)) - - def info(self, message): - print >> self._info, message - self._info.flush() - - def error(self, message): - print >> self._error, message - self._error.flush() - - def fail(self, message, exit_code=1): - self.error(message) - sys.exit(exit_code) - - def reply(self): - return self._reply - - def request(self): - if self._request: - return self._request - else: - try: - file = open(self._request_file, 'r') - self._request = json.load(file) - file.close() - except IOError, error: - raise FileReadError("Unable to open request file `%s': %s" % - (self._request_file, error)) - except json.JSONDecodeError, error: - raise JSONParsingError("An error occurred during parsing of " - "the JSON data in the file `%s': %s" % - (self._request_file, error)) - file.close() - - return self._request - -# vim: set ts=4 sw=4 et : diff --git a/ext/action_helpers/python/romke/README.markdown b/ext/action_helpers/python/romke/README.markdown deleted file mode 100644 index fe1c62eb..00000000 --- a/ext/action_helpers/python/romke/README.markdown +++ /dev/null @@ -1,38 +0,0 @@ -A simple helper to assist with writing MCollective actions in Python. - -Given an action as below: - -
-action "echo" do
-   validate :message, String
-
-   implemented_by "/tmp/echo.py"
-end
-
- -The following Python script will implement the echo action externally -replying with _message_ and _timestamp_ - -
-#!/bin/env python
-import mcollectiveah
-import time
-
-mc = mcollectiveah.MCollectiveAction()
-mc.reply['message'] = mc.request['message']
-mc.reply['timestamp'] = time.strftime("%c")
-mc.reply['info'] = "some text to info log in the server"
-
- -Calling it with _mco rpc_ results in: - -
-$ mco rpc test echo message="hello world"
-Determining the amount of hosts matching filter for 2 seconds .... 1
-
- * [ ============================================================> ] 1 / 1
-
-
-nephilim.ml.org                         : OK
-    {:message=>"hello world", :time=>"Tue Mar 15 19:20:53 +0000 2011"}
-
diff --git a/ext/action_helpers/python/romke/mcollectiveah.py b/ext/action_helpers/python/romke/mcollectiveah.py deleted file mode 100644 index bca8ab63..00000000 --- a/ext/action_helpers/python/romke/mcollectiveah.py +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/env python -# -*- coding: utf-8 -*- vim: set ts=4 et sw=4 fdm=indent : -import os, sys - -try: - import simplejson -except ImportError: - sys.stderr.write('Unable to load simplejson python module.') - sys.exit(1) - -class MCollectiveActionNoEnv(Exception): - pass -class MCollectiveActionFileError(Exception): - pass - -class MCollectiveAction(object): - def __init__(self, *args, **kwargs): - try: - self.infile = os.environ['MCOLLECTIVE_REQUEST_FILE'] - except KeyError: - raise MCollectiveActionNoEnv("No MCOLLECTIVE_REQUEST_FILE environment variable") - try: - self.outfile = os.environ['MCOLLECTIVE_REPLY_FILE'] - except KeyError: - raise MCollectiveActionNoEnv("No MCOLLECTIVE_REPLY_FILE environment variable") - - self.request = {} - self.reply = {} - - self.load() - - def load(self): - if not self.infile: - return False - try: - infile = open(self.infile, 'r') - self.request = simplejson.load(infile) - infile.close() - except IOError, e: - raise MCollectiveActionFileError("Could not read request file `%s`: %s" % (self.infile, e)) - except simplejson.JSONDecodeError, e: - infile.close() - raise MCollectiveActionFileError("Could not parse JSON data in file `%s`: %s", (self.infile, e)) - - def send(self): - if not getattr(self, 'outfile', None): # if exception was raised during or before setting self.outfile - return False - try: - outfile = open(self.outfile, 'w') - simplejson.dump(self.reply, outfile) - outfile.close() - except IOError, e: - raise MCollectiveActionFileError("Could not write reply file `%s`: %s" % (self.outfile, e)) - - def error(self, msg): - """Prints line to STDERR that will be logged at error level in the mcollectived log file""" - sys.stderr.write("%s\n" % msg) - - def fail(self, msg): - """Logs error message and exitst with RPCAborted""" - self.error(msg) - sys.exit(1) - - def info(self, msg): - """Prints line to STDOUT that will be logged at info level in the mcollectived log file""" - sys.stdout.write("%s\n" % msg) - - def __del__(self): - self.send() diff --git a/ext/action_helpers/python/romke/test.py b/ext/action_helpers/python/romke/test.py deleted file mode 100644 index 3bfe083e..00000000 --- a/ext/action_helpers/python/romke/test.py +++ /dev/null @@ -1,50 +0,0 @@ -#!/bin/env python -# -*- coding: utf-8 -*- vim: set ts=4 et sw=4 fdm=indent : -import unittest, tempfile, simplejson, os, random - -import mcollectiveah - -class TestFunctions(unittest.TestCase): - def test_raise_environ(self): - try: - del os.environ['MCOLLECTIVE_REQUEST_FILE'] - del os.environ['MCOLLECTIVE_REPLY_FILE'] - except: pass - self.assertRaises(mcollectiveah.MCollectiveActionNoEnv, mcollectiveah.MCollectiveAction) - - def test_raise_file_error(self): - os.environ['MCOLLECTIVE_REQUEST_FILE'] = '/tmp/mcollectiveah-test-request.%d' % random.randrange(100000) - os.environ['MCOLLECTIVE_REPLY_FILE'] = '/tmp/mcollectiveah-test-reply.%d' % random.randrange(100000) - - self.assertRaises(mcollectiveah.MCollectiveActionFileError, mcollectiveah.MCollectiveAction) - - os.unlink(os.environ['MCOLLECTIVE_REPLY_FILE']) - - def test_echo(self): - tin = tempfile.NamedTemporaryFile(mode='w', delete=False) - self.data = {'message': 'test'} - - simplejson.dump(self.data, tin) - os.environ['MCOLLECTIVE_REQUEST_FILE'] = tin.name - tin.close() - - tout = tempfile.NamedTemporaryFile(mode='w') - os.environ['MCOLLECTIVE_REPLY_FILE'] = tout.name - tout.close() - - mc = mcollectiveah.MCollectiveAction() - mc.reply['message'] = mc.request['message'] - del mc - - tout = open(os.environ['MCOLLECTIVE_REPLY_FILE'], 'r') - data = simplejson.load(tout) - tout.close() - - self.assertEqual(data, self.data) - - - os.unlink(os.environ['MCOLLECTIVE_REQUEST_FILE']) - os.unlink(os.environ['MCOLLECTIVE_REPLY_FILE']) - -if __name__ == '__main__': - unittest.main() diff --git a/ext/activemq/apache-activemq.spec b/ext/activemq/apache-activemq.spec deleted file mode 100644 index e98bdd4d..00000000 --- a/ext/activemq/apache-activemq.spec +++ /dev/null @@ -1,206 +0,0 @@ -Summary: Apache ActiveMQ -Name: activemq -Version: 5.3.0 -Release: 1%{?dist} -License: Apache -Group: Network/Daemons -Source0: apache-activemq-%{version}-bin.tar.gz -Source1: wlcg-patch.tgz -BuildRoot: %{_tmppath}/%{name}-%{version}-root -BuildArch: noarch -Requires: tanukiwrapper >= 3.2.0 - -#%define buildver 5.1.0 - -%define homedir /usr/share/%{name} -%define libdir /var/lib/%{name} -%define libexecdir /usr/libexec/%{name} -%define cachedir /var/cache/%{name} -%define docsdir /usr/share/doc/%{name}-%{version} - -%description -ApacheMQ is a JMS Compliant Messaging System - -%package info-provider -Summary: An LDAP information provider for activemq -Group:grid/lcg -%description info-provider -An LDAP infomation provider for activemq - -%package meta -Summary: A metapackage -Group:grid/lcg -Requires: activemq = ${version}-${release}, activemq-info-provider = ${version}-${release} -%description meta -A metapackage - -%prep -%setup -q -a1 -n apache-activemq-%{version} - -%build -install --directory ${RPM_BUILD_ROOT} - -%install -rm -rf $RPM_BUILD_ROOT -install --directory ${RPM_BUILD_ROOT}%{homedir} -install --directory ${RPM_BUILD_ROOT}%{homedir}/bin -install --directory ${RPM_BUILD_ROOT}%{docsdir} -install --directory ${RPM_BUILD_ROOT}%{libdir}/lib -install --directory ${RPM_BUILD_ROOT}%{libexecdir} -install --directory ${RPM_BUILD_ROOT}%{libdir}/webapps -install --directory ${RPM_BUILD_ROOT}%{cachedir} -install --directory ${RPM_BUILD_ROOT}%{cachedir}/data -install --directory ${RPM_BUILD_ROOT}/var/log/%{name} -install --directory ${RPM_BUILD_ROOT}/var/run/%{name} -install --directory ${RPM_BUILD_ROOT}/etc/%{name} -install --directory ${RPM_BUILD_ROOT}/etc/init.d -install --directory ${RPM_BUILD_ROOT}/etc/httpd/conf.d - -# Config files -install conf/activemq.xml ${RPM_BUILD_ROOT}/etc/%{name} -install conf/credentials.properties ${RPM_BUILD_ROOT}/etc/%{name} -install conf/jetty.xml ${RPM_BUILD_ROOT}/etc/%{name} -install conf/log4j.properties ${RPM_BUILD_ROOT}/etc/%{name} -install conf/activemq-wrapper.conf ${RPM_BUILD_ROOT}/etc/%{name} -install conf/activemq-httpd.conf ${RPM_BUILD_ROOT}/etc/httpd/conf.d - -# startup script -install bin/activemq ${RPM_BUILD_ROOT}/etc/init.d - -# Bin and doc dirs -install *.txt *.html ${RPM_BUILD_ROOT}%{docsdir} -cp -r docs ${RPM_BUILD_ROOT}%{docsdir} - -install bin/run.jar bin/activemq-admin ${RPM_BUILD_ROOT}%{homedir}/bin -install --directory ${RPM_BUILD_ROOT}/usr/bin -%{__ln_s} -f %{homedir}/bin/activemq-admin ${RPM_BUILD_ROOT}/usr/bin - -# Runtime directory -cp -r lib ${RPM_BUILD_ROOT}%{libdir} -cp -r webapps/admin ${RPM_BUILD_ROOT}%{libdir}/webapps - -# Info provider -install info-provider-activemq ${RPM_BUILD_ROOT}/%{libexecdir} - -pushd ${RPM_BUILD_ROOT}%{homedir} - [ -d conf ] || %{__ln_s} -f /etc/%{name} conf - [ -d data ] || %{__ln_s} -f %{cachedir}/data data - [ -d docs ] || %{__ln_s} -f %{docsdir} docs - [ -d lib ] || %{__ln_s} -f %{libdir}/lib lib - [ -d lib ] || %{__ln_s} -f %{libdir}/libexec libexec - [ -d log ] || %{__ln_s} -f /var/log/%{name} log - [ -d webapps ] || %{__ln_s} -f %{libdir}/webapps webapps -popd - - -%pre -# Add the "activemq" user and group -# we need a shell to be able to use su - later -/usr/sbin/groupadd -g 92 -r activemq 2> /dev/null || : -/usr/sbin/useradd -c "Apache Activemq" -u 92 -g activemq \ - -s /bin/bash -r -d /usr/share/activemq activemq 2> /dev/null || : - -%post -# install activemq (but don't activate) -/sbin/chkconfig --add activemq - -%preun -if [ $1 = 0 ]; then - [ -f /var/lock/subsys/activemq ] && /etc/init.d/activemq stop - [ -f /etc/init.d/activemq ] && /sbin/chkconfig --del activemq -fi - -%postun - -%clean -rm -rf $RPM_BUILD_ROOT - - -%files -%defattr(-,root,root) -%attr(755,root,root) /usr/bin/activemq-admin -%{homedir} -%docdir %{docsdir} -%{docsdir} -%{libdir} -%attr(775,activemq,activemq) %dir /var/log/%{name} -%attr(775,activemq,activemq) %dir /var/run/%{name} -%attr(775,root,activemq) %dir %{cachedir}/data -%attr(755,root,root) /etc/init.d/activemq -%config(noreplace) /etc/httpd/conf.d/activemq-httpd.conf -%config(noreplace) /etc/%{name}/activemq.xml -%config(noreplace) %attr(750,root,activemq) /etc/%{name}/credentials.properties -%config(noreplace) /etc/%{name}/jetty.xml -%config(noreplace) /etc/%{name}/activemq-wrapper.conf -%config(noreplace) /etc/%{name}/log4j.properties - -%files info-provider -%defattr(-,root,root) -%attr(755,root,root) %{libexecdir}/info-provider-activemq - -%changelog -* Sat Jan 16 2010 R.I.Pienaar 5.3.0 -- Adjusted for ActiveMQ 5.3.0 - -* Wed Oct 29 2008 James Casey 5.2.0-2 -- fixed defattr on subpackages - -* Tue Sep 02 2008 James Casey 5.2.0-1 -- Upgraded to activemq 5.2.0 - -* Tue Sep 02 2008 James Casey 5.1.0-7 -- Added separate logging of messages whenever the logging interceptor is enabled in the config file -- removed BrokerRegistry messages casued by REST API -- now we don't log messages to stdout (so no duplicates in wrapper log). -- upped the number and size of the rolling logs - -* Fri Aug 29 2008 James Casey 5.1.0-6 -- make ServiceData be correct LDIF - -* Wed Aug 27 2008 James Casey 5.1.0-5 -- changed glue path from mds-vo-name=local to =resource - -* Tue Aug 05 2008 James Casey 5.1.0-4 -- fixed up info-provider to give both REST and STOMP endpoints - -* Mon Aug 04 2008 James Casey 5.1.0-3 -- reverted out APP_NAME change to ActiveMQ from init.d since it - causes too many problems -* Mon Aug 04 2008 James Casey 5.1.0-2 -- Added info-provider -- removed mysql as a requirement - -* Thu Mar 20 2008 Daniel RODRIGUES - 5.1-SNAPSHOT-1 -- Changed to version 5.1 SNAPSHOT of 18 Mar, fizing AMQ Message Store -- small fixes to makefile - -* Fri Dec 14 2007 James CASEY - 5.0.0-3rc4 -- Added apache config file to forward requests to Jetty - -* Thu Dec 13 2007 James CASEY - 5.0.0-2rc4 -- fixed /usr/bin symlink -- added useJmx to the default config - -* Thu Dec 13 2007 James CASEY - 5.0.0-RC4.1 -- Moved to RC4 of the 5.0.0 release candidates - -* Mon Dec 10 2007 James CASEY - 5.0-SNAPSHOT-7 -- added symlink in /usr/bin for activemq-admin - -* Wed Nov 26 2007 James CASEY - 5.0-SNAPSHOT-6 -- fix bug with group name setting in init.d script - -* Wed Nov 26 2007 James CASEY - 5.0-SNAPSHOT-5 -- fix typos in config file for activemq - -* Wed Nov 26 2007 James CASEY - 5.0-SNAPSHOT-4 -- add support for lib64 version of tanukiwrapper in config -- turned off mysql persistence in the "default" config - -* Wed Oct 17 2007 James CASEY - 5.0-SNAPSHOT-2 -- more re-org to mirror how tomcat is installed. -- support for running as activemq user - -* Tue Oct 16 2007 James CASEY - 5.0-SNAPSHOT-1 -- Initial Version - diff --git a/ext/activemq/examples/multi-broker/README b/ext/activemq/examples/multi-broker/README deleted file mode 100644 index 49229169..00000000 --- a/ext/activemq/examples/multi-broker/README +++ /dev/null @@ -1,12 +0,0 @@ -3 ActiveMQ servers clustered together in a star foramt: - - - broker2 <-----> broker1 <----> broker3 - - -Pay attention to the names in the config file as well -as the users. This is identical to the simple single -broker example except with the aded amq user for the -clustering and the connection setups in broker1 - -Tested to work with ActiveMQ 5.5.0 diff --git a/ext/activemq/examples/multi-broker/broker1-activemq.xml b/ext/activemq/examples/multi-broker/broker1-activemq.xml deleted file mode 100755 index 6c767367..00000000 --- a/ext/activemq/examples/multi-broker/broker1-activemq.xml +++ /dev/null @@ -1,241 +0,0 @@ - - - - - file:${activemq.base}/conf/credentials.properties - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ext/activemq/examples/multi-broker/broker2-activemq.xml b/ext/activemq/examples/multi-broker/broker2-activemq.xml deleted file mode 100755 index e3e0a03a..00000000 --- a/ext/activemq/examples/multi-broker/broker2-activemq.xml +++ /dev/null @@ -1,175 +0,0 @@ - - - - - file:${activemq.base}/conf/credentials.properties - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ext/activemq/examples/multi-broker/broker3-activemq.xml b/ext/activemq/examples/multi-broker/broker3-activemq.xml deleted file mode 100755 index 6b1cd17b..00000000 --- a/ext/activemq/examples/multi-broker/broker3-activemq.xml +++ /dev/null @@ -1,175 +0,0 @@ - - - - - file:${activemq.base}/conf/credentials.properties - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ext/activemq/examples/single-broker/README b/ext/activemq/examples/single-broker/README deleted file mode 100644 index 0986867f..00000000 --- a/ext/activemq/examples/single-broker/README +++ /dev/null @@ -1,5 +0,0 @@ -Simple single broker setup for ActiveMQ 5.5.0. - -Provides 2 users, one admin and one for mcollective. -Admin user can create all sorts of queues and topics, -mcollective user is restricted. diff --git a/ext/activemq/examples/single-broker/activemq.xml b/ext/activemq/examples/single-broker/activemq.xml deleted file mode 100644 index d861962e..00000000 --- a/ext/activemq/examples/single-broker/activemq.xml +++ /dev/null @@ -1,137 +0,0 @@ - - - - - file:${activemq.base}/conf/credentials.properties - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ext/activemq/wlcg-patch.tgz b/ext/activemq/wlcg-patch.tgz deleted file mode 100644 index 469169726c3fa4116c04ae5bb498580bec0620f2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9890 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 URHibO>HjGG7fk7{PyomP0D|Ex-2eap diff --git a/ext/aio/README.md b/ext/aio/README.md deleted file mode 100644 index 0c177ec5..00000000 --- a/ext/aio/README.md +++ /dev/null @@ -1,5 +0,0 @@ -All-In-One Agent Support files - -Here you'll find the init scripts and supplemental files used by the -Puppetlabs All-In-One Agent packages built with -https://github.com/puppetlabs/puppet-agent diff --git a/ext/aio/common/client.cfg.dist b/ext/aio/common/client.cfg.dist deleted file mode 100644 index f2372c48..00000000 --- a/ext/aio/common/client.cfg.dist +++ /dev/null @@ -1,24 +0,0 @@ -main_collective = mcollective -collectives = mcollective - -libdir = /opt/puppetlabs/mcollective/plugins - -# consult the "classic" libdirs too -libdir = /usr/share/mcollective/plugins -libdir = /usr/libexec/mcollective - -logger_type = console -loglevel = warn - -# Plugins -securityprovider = psk -plugin.psk = unset - -connector = activemq -plugin.activemq.pool.size = 1 -plugin.activemq.pool.1.host = stomp1 -plugin.activemq.pool.1.port = 6163 -plugin.activemq.pool.1.user = mcollective -plugin.activemq.pool.1.password = marionette - -connection_timeout = 3 diff --git a/ext/aio/common/server.cfg.dist b/ext/aio/common/server.cfg.dist deleted file mode 100644 index df8b1531..00000000 --- a/ext/aio/common/server.cfg.dist +++ /dev/null @@ -1,27 +0,0 @@ -main_collective = mcollective -collectives = mcollective - -libdir = /opt/puppetlabs/mcollective/plugins - -# consult the "classic" libdirs too -libdir = /usr/share/mcollective/plugins -libdir = /usr/libexec/mcollective - -logfile = /var/log/puppetlabs/mcollective/mcollective.log -loglevel = info -daemonize = 1 - -# Plugins -securityprovider = psk -plugin.psk = unset - -connector = activemq -plugin.activemq.pool.size = 1 -plugin.activemq.pool.1.host = stomp1 -plugin.activemq.pool.1.port = 6163 -plugin.activemq.pool.1.user = mcollective -plugin.activemq.pool.1.password = marionette - -# Facts -factsource = yaml -plugin.yaml = /etc/puppetlabs/mcollective/facts.yaml diff --git a/ext/aio/debian/mcollective.default b/ext/aio/debian/mcollective.default deleted file mode 100644 index ea9713e3..00000000 --- a/ext/aio/debian/mcollective.default +++ /dev/null @@ -1,2 +0,0 @@ -START=true -DAEMON_OPTS="--pid ${PIDFILE}" diff --git a/ext/aio/debian/mcollective.init b/ext/aio/debian/mcollective.init deleted file mode 100755 index 436f6c3d..00000000 --- a/ext/aio/debian/mcollective.init +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/sh -# -# mcollective Application Server for STOMP based agents -# -# -# description: mcollective lets you build powerful Stomp compatible middleware clients in ruby without having to worry too -# much about all the setup and management of a Stomp connection, it also provides stats, logging and so forth -# as a bonus. -# -### BEGIN INIT INFO -# Provides: mcollective -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start daemon at boot time -# Description: Enable service provided by mcollective. -### END INIT INFO - -# check permissions - -uid=`id -u` -[ $uid -gt 0 ] && { echo "You need to be root to run file" ; exit 4 ; } - - -# PID directory -piddir="/var/run/puppetlabs" -pidfile="${piddir}/mcollectived.pid" - -name="mcollective" -mcollectived=/opt/puppetlabs/puppet/bin/mcollectived -daemonopts="--pid=${pidfile} --config=/etc/puppetlabs/mcollective/server.cfg" - - -# Source function library. -. /lib/lsb/init-functions - -if [ -f /etc/default/mcollective ]; then - . /etc/default/mcollective -fi - -# Check that binary exists -if ! [ -f $mcollectived ] -then - echo "mcollectived binary not found" - exit 5 -fi - -# create pid file if it does not exist -[ ! -f ${pidfile} ] && { touch ${pidfile} ; } - -# See how we were called. -case "$1" in - start) - echo "Starting daemon: " $name - mkdir -p ${piddir} - # start the program - start-stop-daemon --start --pidfile ${pidfile} --oknodo --quiet --startas ${mcollectived} -- ${daemonopts} --daemonize - [ $? = 0 ] && { exit 0 ; } || { exit 1 ; } - log_success_msg "mcollective started" - ;; - stop) - echo "Stopping daemon: " $name - start-stop-daemon --stop --retry 5 --signal "TERM" --oknodo --quiet --pidfile ${pidfile} - [ $? = 0 ] && { exit 0 ; } || { exit 1 ; } - log_success_msg "mcollective stopped" - ;; - restart) - echo "Restarting daemon: " $name - $0 stop - sleep 2 - $0 start - [ $? = 0 ] && { echo "mcollective restarted" ; exit 0 ; } - ;; - condrestart) - # status prints a horrible error message when it fails. We - # don't want to scare the user away. - if $0 status >/dev/null 2>&1; then - $0 restart - fi - ;; - force-reload) - echo "not implemented" - ;; - status) - status_of_proc -p ${pidfile} ${mcollectived} ${name} && exit 0 || exit $? - ;; - *) - echo "Usage: mcollectived {start|stop|restart|condrestart|status}" - exit 2 - ;; -esac diff --git a/ext/aio/osx/mcollective.plist b/ext/aio/osx/mcollective.plist deleted file mode 100644 index a6a7a3db..00000000 --- a/ext/aio/osx/mcollective.plist +++ /dev/null @@ -1,30 +0,0 @@ - - - - - EnvironmentVariables - - PATH - /opt/puppetlabs/puppet/bin:/usr/bin:/bin:/usr/sbin:/sbin - LANG - en_US.UTF-8 - - Label - mcollective - KeepAlive - - ProgramArguments - - /opt/puppetlabs/puppet/bin/mcollectived - --no-daemonize - --config=/etc/puppetlabs/mcollective/server.cfg - --pidfile=/var/run/puppetlabs/mcollective.pid - - RunAtLoad - - StandardErrorPath - /var/log/puppetlabs/mcollective/mcollective.log - StandardOutPath - /var/log/puppetlabs/mcollective/mcollective.log - - diff --git a/ext/aio/redhat/mcollective-systemd.logrotate b/ext/aio/redhat/mcollective-systemd.logrotate deleted file mode 100644 index f05012c5..00000000 --- a/ext/aio/redhat/mcollective-systemd.logrotate +++ /dev/null @@ -1,8 +0,0 @@ -/var/log/puppetlabs/mcollective/mcollective.log { - missingok - notifempty - sharedscripts - postrotate - systemctl restart mcollective >/dev/null 2>&1 || true - endscript -} diff --git a/ext/aio/redhat/mcollective-sysv.logrotate b/ext/aio/redhat/mcollective-sysv.logrotate deleted file mode 100644 index 3d700d2b..00000000 --- a/ext/aio/redhat/mcollective-sysv.logrotate +++ /dev/null @@ -1,8 +0,0 @@ -/var/log/puppetlabs/mcollective/mcollective.log { - missingok - notifempty - sharedscripts - postrotate - /etc/init.d/mcollective restart >/dev/null 2>&1 || true - endscript -} diff --git a/ext/aio/redhat/mcollective.init b/ext/aio/redhat/mcollective.init deleted file mode 100755 index f839d895..00000000 --- a/ext/aio/redhat/mcollective.init +++ /dev/null @@ -1,142 +0,0 @@ -#!/bin/sh -# -# mcollective Application Server for STOMP based agents -# -# chkconfig: - 24 76 -# -# description: mcollective lets you build powerful Stomp compatible middleware clients in ruby without having to worry too -# much about all the setup and management of a Stomp connection, it also provides stats, logging and so forth -# as a bonus. -# -### BEGIN INIT INFO -# Provides: mcollective -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Short-Description: Start daemon at boot time -# Description: Enable service provided by daemon. -### END INIT INFO - -mcollectived="/opt/puppetlabs/puppet/bin/mcollectived" -piddir="/var/run/puppetlabs" -pidfile="${piddir}/mcollectived.pid" - -if [ -d /var/lock/subsys ]; then - # RedHat/CentOS/etc who use subsys - lockfile="/var/lock/subsys/mcollective" -else - # The rest of them - lockfile="/var/lock/mcollective" -fi - -# Check that binary exists -if ! [ -f $mcollectived ]; then - echo "mcollectived binary not found" - exit 5 -fi - -# Source function library. -. /etc/init.d/functions - -if [ -f /etc/sysconfig/mcollective ]; then - . /etc/sysconfig/mcollective -fi - -# Determine if we can use the -p option to daemon, killproc, and status. -# RHEL < 5 can't. -if status | grep -q -- '-p' 2>/dev/null; then - daemonopts="--pidfile $pidfile" - pidopts="-p $pidfile" -fi - -start() { - echo -n "Starting mcollective: " - mkdir -p ${piddir} - # Only try to start if not already started - if ! rh_status_q; then - daemon ${daemonopts} ${mcollectived} --pid=${pidfile} --config="/etc/puppetlabs/mcollective/server.cfg" --daemonize - fi - # This will be 0 if mcollective is already running - RETVAL=$? - echo - [ $RETVAL -eq 0 ] && touch ${lockfile} - return $RETVAL -} - -stop() { - echo -n "Shutting down mcollective: " - # If running, try to stop it - if rh_status_q; then - killproc ${pidopts} -d 10 ${mcollectived} - else - # Non-zero status either means lockfile and pidfile need cleanup (1 and 2) - # or the process is already stopped (3), so we can just call true to - # trigger the cleanup that happens below. - true - fi - RETVAL=$? - echo - [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile} - return $RETVAL -} - -restart() { - stop - start -} - -reload_agents() { - echo -n "Reloading mcollective agents: " - killproc ${pidopts} ${mcollectived} -USR1 - RETVAL=$? - echo - return $RETVAL -} - -reload_loglevel() { - echo -n "Cycling mcollective logging level: " - killproc ${pidopts} ${mcollectived} -USR2 - RETVAL=$? - echo - return $RETVAL -} - -rh_status() { - status ${pidopts} ${mcollectived} - RETVAL=$? - return $RETVAL -} - -rh_status_q() { - rh_status >/dev/null 2>&1 -} - -# See how we were called. -case "$1" in - start) - start - ;; - stop) - stop - ;; - restart) - restart - ;; - condrestart) - rh_status_q || exit 0 - restart - ;; - reload-agents) - reload_agents - ;; - reload-loglevel) - reload_loglevel - ;; - status) - rh_status - ;; - *) - echo "Usage: mcollectived {start|stop|restart|condrestart|reload-agents|reload-loglevel|status}" - RETVAL=2 - ;; -esac -exit $RETVAL diff --git a/ext/aio/redhat/mcollective.service b/ext/aio/redhat/mcollective.service deleted file mode 100644 index 0ad0556e..00000000 --- a/ext/aio/redhat/mcollective.service +++ /dev/null @@ -1,25 +0,0 @@ -# -# Local settings can be configured without being overwritten by package upgrades, for example -# if you want to increase mcollective open-files-limit to 10000, -# you need to increase systemd's LimitNOFILE setting, so create a file named -# "/etc/systemd/system/mcollective.service.d/limits.conf" containing: -# [Service] -# LimitNOFILE=10000 -# You can confirm it worked by running systemctl daemon-reload -# then running systemctl show mcollective | grep LimitNOFILE -# -[Unit] -Description=The Marionette Collective -After=network.target - -[Service] -Type=forking -StandardOutput=syslog -StandardError=syslog -ExecStart=/opt/puppetlabs/puppet/bin/mcollectived --config=/etc/puppetlabs/mcollective/server.cfg --pidfile=/var/run/puppetlabs/mcollective.pid --daemonize -ExecReload=/bin/kill -USR1 $MAINPID -PIDFile=/var/run/puppetlabs/mcollective.pid -KillMode=process - -[Install] -WantedBy=multi-user.target diff --git a/ext/aio/redhat/mcollective.sysconfig b/ext/aio/redhat/mcollective.sysconfig deleted file mode 100644 index f5c3e997..00000000 --- a/ext/aio/redhat/mcollective.sysconfig +++ /dev/null @@ -1,10 +0,0 @@ -# Configuration file for mcollectived - -# Full path to the mcollectived binary -#DAEMON=/opt/puppetlabs/agent/bin/mcollectived - -# Location of PID file -#PIDFILE=/var/run/puppetlabs/agent/mcollectived.pid - -# Any other parameters to pass to mcollectived -#DAEMON_OPTS= diff --git a/ext/aio/solaris/smf/mcollective.xml b/ext/aio/solaris/smf/mcollective.xml deleted file mode 100644 index 5f40353e..00000000 --- a/ext/aio/solaris/smf/mcollective.xml +++ /dev/null @@ -1,44 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ext/aio/suse/mcollective.init b/ext/aio/suse/mcollective.init deleted file mode 100755 index a9bb6af3..00000000 --- a/ext/aio/suse/mcollective.init +++ /dev/null @@ -1,121 +0,0 @@ -#!/bin/sh -# -# mcollective Application Server for STOMP based agents -# -# chkconfig: 345 24 76 -# -# description: mcollective lets you build powerful Stomp compatible middleware clients in ruby without having to worry too -# much about all the setup and management of a Stomp connection, it also provides stats, logging and so forth -# as a bonus. -# -### BEGIN INIT INFO -# Provides: mcollective -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Default-Start: 3 5 -# Default-Stop: 0 1 2 6 -# Short-Description: Start daemon at boot time -# Description: Enable service provided by daemon. -### END INIT INFO - -# Shell functions sourced from /etc/rc.status: -# rc_check check and set local and overall rc status -# rc_status check and set local and overall rc status -# rc_status -v ditto but be verbose in local rc status -# rc_status -v -r ditto and clear the local rc status -# rc_failed set local and overall rc status to failed -# rc_reset clear local rc status (overall remains) -# rc_exit exit appropriate to overall rc status -[ -f /etc/rc.status ] && . /etc/rc.status -[ -f /etc/sysconfig/mcollective ] && . /etc/sysconfig/mcollective - -desc=${DESC:-mcollective daemon} -daemon=${DAEMON:-/opt/puppetlabs/puppet/bin/mcollectived} -name=${NAME:-mcollectived} -piddir=${PIDDIR:-/var/run/puppetlabs} -pidfile=${PIDFILE:-${piddir}/mcollectived.pid} -daemon_opts=${DAEMON_OPTS:---pid ${pidfile}} - -# First reset status of this service -rc_reset - -# Return values acc. to LSB for all commands but status: -# 0 - success -# 1 - misc error -# 2 - invalid or excess args -# 3 - unimplemented feature (e.g. reload) -# 4 - insufficient privilege -# 5 - program not installed -# 6 - program not configured -# -# Note that starting an already running service, stopping -# or restarting a not-running service as well as the restart -# with force-reload (in case signalling is not supported) are -# considered a success. - -case "$1" in - start) - echo -n "Starting ${desc}: " - ## Start daemon with startproc(8). If this fails - ## the echo return value is set appropriate. - # startproc should return 0, even if service is - # already running to match LSB spec. - mkdir -p ${piddir} - startproc -p $pidfile $daemon $daemon_opts - # Remember status and be verbose - rc_status -v - ;; - stop) - echo -n "Stopping ${desc}: " - ## Stop daemon with killproc(8) and if this fails - ## set echo the echo return value. - - killproc -p $pidfile $daemon && rm -f ${pidfile} - - # Remember status and be verbose - rc_status -v - ;; - try-restart|condrestart|force-reload) - ## Stop the service and if this succeeds (i.e. the - ## service was running before), start it again. - $0 status &> /dev/null - if test $? = 0; then - $0 restart - else - rc_reset # Not running is not a failure. - fi - - # Remember status and be quiet - rc_status - ;; - restart) - ## Stop the service and regardless of whether it was - ## running or not, start it again. - $0 stop - sleep 1 - $0 start - - # Remember status and be quiet - rc_status - ;; - status) - echo -n "Checking ${desc}: " - ## Check status with checkproc(8), if process is running - ## checkproc will return with exit status 0. - - # Status has a slightly different for the status command: - # 0 - service running - # 1 - service dead, but /var/run/ pid file exists - # 2 - service dead, but /var/lock/ lock file exists - # 3 - service not running - - # NOTE: checkproc returns LSB compliant status values. - checkproc -p ${pidfile} ${daemon} - rc_status -v - ;; - *) - echo "Usage: $0 {start|stop|status|try-restart|condrestart|restart|force-reload}" - exit 1 - ;; -esac -rc_exit diff --git a/ext/bash/mco_completion.sh b/ext/bash/mco_completion.sh deleted file mode 100644 index f37b1d4a..00000000 --- a/ext/bash/mco_completion.sh +++ /dev/null @@ -1,57 +0,0 @@ -_mco() { - local agents options - - COMPREPLY=() - local cur=${COMP_WORDS[COMP_CWORD]} - local prev=${COMP_WORDS[COMP_CWORD-1]} - - # Where are the plugins? - local libdir=$(sed -n 's@libdir = @@p' /etc/mcollective/client.cfg) - - # All arguments by options - noopt=($(tr ' ' '\n' <<<${COMP_WORDS[@]} | \ - grep -v "^$cur$" | grep -v -- '^-')) - - local count_noopt=${#noopt[@]} - local cmd=${noopt[0]} - local app=${noopt[1]} - - # A bug in the output of --help prevents - # from parsing all options, so we list the common ones here - local common_options="-T --target -c --config --dt --discovery-timeout \ - -t --timeout -q --quiet -v --verbose -h --help -W --with -F \ - --wf --with-fact -C --wc --with-class -A --wa --with-agent -I \ - --wi --with-identity" - - if [ $COMP_CWORD -eq 1 ]; then - apps=$($cmd completion --list-applications) - COMPREPLY=($(compgen -W "$apps" -- "$cur")) - elif [ $COMP_CWORD -gt 1 ]; then - options="${common_options} $($cmd $app --help | grep -o -- '-[^, ]\+')" - - if [ "x${app}" = "xrpc" ]; then - if [[ $count_noopt -eq 2 || "x${prev}" = "x--agent" ]]; then - # Complete with agents - agents=$($cmd completion --list-agents) - options="$options $agents" - elif [[ $count_noopt -eq 3 || "x${prev}" = "x--action" ]]; then - # Complete with agent actions - rpcagent=${noopt[2]} - actions=$($cmd completion --list-actions \ - --agent "$rpcagent") - options="$options $actions" - elif [ $count_noopt -gt 3 ]; then - # Complete with key=value - rpcagent=${noopt[2]} - rpcaction=${noopt[3]} - inputs=$($cmd completion --list-inputs \ - --agent "$rpcagent" --action "$rpcaction") - options="$options $inputs" - fi - fi - - COMPREPLY=($(compgen -W "$options" -S ' ' -- "$cur")) - fi -} -[ -n "${have:-}" ] && complete -o nospace -F _mco mco - diff --git a/ext/build_defaults.yaml b/ext/build_defaults.yaml deleted file mode 100644 index a30724c0..00000000 --- a/ext/build_defaults.yaml +++ /dev/null @@ -1,21 +0,0 @@ ---- -packaging_url: 'git://github.com/puppetlabs/packaging.git --branch=master' -packaging_repo: 'packaging' -#default_cow: 'base-squeeze-i386.cow' -#cows: 'base-precise-i386.cow base-trusty-i386.cow' -pbuild_conf: '/etc/pbuilderrc' -packager: 'puppetlabs' -gpg_name: 'info@puppetlabs.com' -gpg_key: '7F438280EF8D349F' -sign_tar: FALSE -# a space separated list of mock configs -#final_mocks: 'pl-el-6-i386 pl-el-7-x86_64' -yum_host: 'yum.puppetlabs.com' -yum_repo_path: '/opt/repository/yum/' -build_gem: TRUE -build_dmg: FALSE -build_doc: TRUE -build_ips: FALSE -apt_host: 'apt.puppetlabs.com' -apt_repo_url: 'http://apt.puppetlabs.com' -apt_repo_path: '/opt/repository/incoming' diff --git a/ext/debian/changelog.erb b/ext/debian/changelog.erb deleted file mode 100644 index 686e6c88..00000000 --- a/ext/debian/changelog.erb +++ /dev/null @@ -1,5 +0,0 @@ -mcollective (<%= @debversion %>) unstable sid squeeze wheezy precise; urgency=low - - * Update to version <%= @debversion %> - - -- Puppet Labs Release <%= Time.now.strftime("%a, %d %b %Y %H:%M:%S %z")%> diff --git a/ext/debian/compat b/ext/debian/compat deleted file mode 100644 index 7f8f011e..00000000 --- a/ext/debian/compat +++ /dev/null @@ -1 +0,0 @@ -7 diff --git a/ext/debian/control b/ext/debian/control deleted file mode 100644 index 07267008..00000000 --- a/ext/debian/control +++ /dev/null @@ -1,42 +0,0 @@ -Source: mcollective -Section: utils -Priority: extra -Maintainer: Riccardo Setti -Build-Depends: debhelper (>= 7), quilt, cdbs, ruby -Standards-Version: 3.8.0 -Homepage: https://docs.puppetlabs.com/mcollective/ - -Package: mcollective -Architecture: all -Depends: ruby (>= 1.8.1), mcollective-common (= ${source:Version}) -Description: build server orchestration or parallel job execution systems - The Marionette Collective aka. mcollective is a framework - to build server orchestration or parallel job execution systems. - -Package: mcollective-client -Architecture: all -Depends: ruby (>= 1.8.1), mcollective-common (= ${source:Version}) -Description: build server orchestration or parallel job execution systems - The Marionette Collective aka. mcollective is a framework - to build server orchestration or parallel job execution system - -Package: mcollective-common -Replaces: mcollective (<< 2.0.0-1) -Breaks: mcollective (<< 2.0.0-1), mcollective-client (<< 2.0.0-1) -Architecture: all -Depends: ruby (>= 1.8.1) , rubygems1.8 | rubygems1.9.1 | libruby (>= 1:1.9.3.4), ruby-stomp | libstomp-ruby, libjson-ruby | ruby-json -Description: build server orchestration or parallel job execution systems - The Marionette Collective aka. mcollective is a framework - to build server orchestration or parallel job execution systems. - . - Common files for mcollective packages. - -Package: mcollective-doc -Architecture: all -Section: doc -Description: Documentation for mcollective - The Marionette Collective aka. mcollective is a framework - to build server orchestration or parallel job execution systems. - . - Documentation package. - diff --git a/ext/debian/copyright b/ext/debian/copyright deleted file mode 100644 index ec55a9a4..00000000 --- a/ext/debian/copyright +++ /dev/null @@ -1,29 +0,0 @@ -This package was debianized by Riccardo Setti on -Mon, 04 Jan 2010 17:09:50 +0000. - -It was downloaded from http://code.google.com/p/mcollective - -Upstream Author: - R.I.Pienaar - -Copyright: - - Copyright 2009 R.I.Pienaar - -License: - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - -The Debian packaging is (C) 2010, Riccardo Setti and -is licensed under the Apache License v2. - diff --git a/ext/debian/mcollective-client.install b/ext/debian/mcollective-client.install deleted file mode 100644 index 6cb5148d..00000000 --- a/ext/debian/mcollective-client.install +++ /dev/null @@ -1,2 +0,0 @@ -usr/bin/mco usr/bin/ -etc/mcollective/client.cfg etc/mcollective diff --git a/ext/debian/mcollective-common.install b/ext/debian/mcollective-common.install deleted file mode 100644 index 1c4c2a66..00000000 --- a/ext/debian/mcollective-common.install +++ /dev/null @@ -1,2 +0,0 @@ -usr/lib/ruby/vendor_ruby/* usr/lib/ruby/vendor_ruby/ -etc/mcollective/*.erb etc/mcollective diff --git a/ext/debian/mcollective-doc.install b/ext/debian/mcollective-doc.install deleted file mode 100644 index 0bd8547d..00000000 --- a/ext/debian/mcollective-doc.install +++ /dev/null @@ -1 +0,0 @@ -usr/share/doc/mcollective/* usr/share/doc/mcollective-doc/ diff --git a/ext/debian/mcollective.dirs b/ext/debian/mcollective.dirs deleted file mode 100644 index cfb1fd7b..00000000 --- a/ext/debian/mcollective.dirs +++ /dev/null @@ -1,2 +0,0 @@ -etc/mcollective/ssl/clients -etc/mcollective/plugin.d diff --git a/ext/debian/mcollective.init b/ext/debian/mcollective.init deleted file mode 100755 index 09d22026..00000000 --- a/ext/debian/mcollective.init +++ /dev/null @@ -1,92 +0,0 @@ -#!/bin/sh -# -# mcollective Application Server for STOMP based agents -# -# -# description: mcollective lets you build powerful Stomp compatible middleware clients in ruby without having to worry too -# much about all the setup and management of a Stomp connection, it also provides stats, logging and so forth -# as a bonus. -# -### BEGIN INIT INFO -# Provides: mcollective -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start daemon at boot time -# Description: Enable service provided by mcollective. -### END INIT INFO - -# check permissions - -uid=`id -u` -[ $uid -gt 0 ] && { echo "You need to be root to run file" ; exit 4 ; } - - -# PID directory -pidfile="/var/run/mcollectived.pid" - -name="mcollective" -mcollectived=/usr/sbin/mcollectived -daemonopts="--pid=${pidfile} --config=/etc/mcollective/server.cfg" - - -# Source function library. -. /lib/lsb/init-functions - -if [ -f /etc/default/mcollective ]; then - . /etc/default/mcollective -fi - -# Check that binary exists -if ! [ -f $mcollectived ] -then - echo "mcollectived binary not found" - exit 5 -fi - -# create pid file if it does not exist -[ ! -f ${pidfile} ] && { touch ${pidfile} ; } - -# See how we were called. -case "$1" in - start) - echo "Starting daemon: " $name - # start the program - start-stop-daemon -S -p ${pidfile} --oknodo -q -a ${mcollectived} -- ${daemonopts} --daemonize - [ $? = 0 ] && { exit 0 ; } || { exit 1 ; } - log_success_msg "mcollective started" - touch $lock - ;; - stop) - echo "Stopping daemon: " $name - start-stop-daemon -K -R 5 -s "TERM" --oknodo -q -p ${pidfile} - [ $? = 0 ] && { exit 0 ; } || { exit 1 ; } - log_success_msg "mcollective stopped" - ;; - restart) - echo "Restarting daemon: " $name - $0 stop - sleep 2 - $0 start - [ $? = 0 ] && { echo "mcollective restarted" ; exit 0 ; } - ;; - condrestart) - if [ -f $lock ]; then - $0 stop - # avoid race - sleep 2 - $0 start - fi - ;; - force-reload) - echo "not implemented" - ;; - status) - status_of_proc -p ${pidfile} ${mcollectived} ${name} && exit 0 || exit $? - ;; - *) - echo "Usage: mcollectived {start|stop|restart|condrestart|status}" - exit 2 - ;; -esac diff --git a/ext/debian/mcollective.install b/ext/debian/mcollective.install deleted file mode 100644 index f099689b..00000000 --- a/ext/debian/mcollective.install +++ /dev/null @@ -1,3 +0,0 @@ -usr/sbin/mcollectived -etc/mcollective/facts.yaml -etc/mcollective/server.cfg diff --git a/ext/debian/patches/pluginsdir.patch b/ext/debian/patches/pluginsdir.patch deleted file mode 100755 index 4fe03187..00000000 --- a/ext/debian/patches/pluginsdir.patch +++ /dev/null @@ -1,24 +0,0 @@ -diff --git a/etc/client.cfg.dist b/etc/client.cfg.dist -index 1acffee..5c36486 100644 ---- a/etc/client.cfg.dist -+++ b/etc/client.cfg.dist -@@ -1,6 +1,6 @@ - main_collective = mcollective - collectives = mcollective --libdir = /usr/libexec/mcollective -+libdir = /usr/share/mcollective/plugins - logger_type = console - loglevel = warn - -diff --git a/etc/server.cfg.dist b/etc/server.cfg.dist -index 2038324..c28a826 100644 ---- a/etc/server.cfg.dist -+++ b/etc/server.cfg.dist -@@ -1,6 +1,6 @@ - main_collective = mcollective - collectives = mcollective --libdir = /usr/libexec/mcollective -+libdir = /usr/share/mcollective/plugins - logfile = /var/log/mcollective.log - loglevel = info - daemonize = 1 diff --git a/ext/debian/patches/series b/ext/debian/patches/series deleted file mode 100644 index 15b17e6c..00000000 --- a/ext/debian/patches/series +++ /dev/null @@ -1 +0,0 @@ -pluginsdir.patch diff --git a/ext/debian/rules b/ext/debian/rules deleted file mode 100755 index 70ad4c4a..00000000 --- a/ext/debian/rules +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/make -f - -include /usr/share/cdbs/1/rules/debhelper.mk -BUILD_ROOT=$(CURDIR)/debian/tmp -RUBY_LIBDIR=$(shell /usr/bin/ruby -rrbconfig -e 'puts RbConfig::CONFIG["vendordir"]') -PLUGINDIR=/usr/share/mcollective/plugins -DOCDIR=$(BUILD_ROOT)/usr/share/doc/mcollective - -install/mcollective:: - /usr/bin/ruby install.rb --destdir=$(CURDIR)/debian/tmp --no-rdoc --sitelibdir=$(RUBY_LIBDIR) --ruby=/usr/bin/ruby --plugindir=$(PLUGINDIR) - mkdir -p $(DOCDIR) - cp -a doc/* $(DOCDIR) - -binary-fixup/mcollective:: - chmod 640 $(CURDIR)/debian/mcollective/etc/mcollective/server.cfg diff --git a/ext/debian/source/format b/ext/debian/source/format deleted file mode 100644 index 163aaf8d..00000000 --- a/ext/debian/source/format +++ /dev/null @@ -1 +0,0 @@ -3.0 (quilt) diff --git a/ext/help-templates/README b/ext/help-templates/README deleted file mode 100644 index 0b60383f..00000000 --- a/ext/help-templates/README +++ /dev/null @@ -1 +0,0 @@ -A number of templates for the SimpleRPC DDL based help system diff --git a/ext/help-templates/rpc-help-markdown.erb b/ext/help-templates/rpc-help-markdown.erb deleted file mode 100644 index 39caf2b3..00000000 --- a/ext/help-templates/rpc-help-markdown.erb +++ /dev/null @@ -1,49 +0,0 @@ -<%= meta[:name].upcase %> AGENT -<% (meta[:name].size + 7).times do %>=<% end %> - -<%= meta[:description] %> - - Author: <%= meta[:author] %> - Version: <%= meta[:version] %> - License: <%= meta[:license] %> - Timeout: <%= meta[:timeout] %> - Home Page: <%= meta[:url] %> - - - -ACTIONS: -======== -% actions.keys.sort.each do |action| - * <%= action %> -% end - -% actions.keys.sort.each do |action| -_<%= action %>_ action: -<% (action.size + 8).times do %>-<% end %> -<%= actions[action][:description] %> - -% if actions[action][:input].keys.size > 0 - INPUT: -% end -% actions[action][:input].keys.sort.each do |input| - <%= input %>: - Description: <%= actions[action][:input][input][:description] %> - Prompt: <%= actions[action][:input][input][:prompt] %> - Type: <%= actions[action][:input][input][:type] %> -% if actions[action][:input][input][:type] == :string - Validation: <%= actions[action][:input][input][:validation] %> - Length: <%= actions[action][:input][input][:maxlength] %> -% elsif actions[action][:input][input][:type] == :list - Valid Values: <%= actions[action][:input][input][:list].join(", ") %> -% end - -% end - - OUTPUT: -% actions[action][:output].keys.sort.each do |output| - <%= output %>: - Description: <%= actions[action][:output][output][:description] %> - Display As: <%= actions[action][:output][output][:display_as] %> - -% end -% end diff --git a/ext/mc-irb b/ext/mc-irb deleted file mode 100755 index 63e75165..00000000 --- a/ext/mc-irb +++ /dev/null @@ -1,252 +0,0 @@ -#!/usr/bin/env ruby - -# Simple IRB shell for mcollective -# -# mc-irb nrpe -# Determining the amount of hosts matching filter for 2 seconds .... 47 -# >> rpc :runcommand, :command => "check_disks" -# -# * [ ============================================================> ] 47 / 47 -# -# -# dev1.your.net Request Aborted -# CRITICAL -# Output: DISK CRITICAL - free space: / 176 MB (4% inode=86%); -# Exit Code: 2 -# Performance Data: /=3959MB;3706;3924;0;4361 /boot=26MB;83;88;0;98 /dev/shm=0MB;217;230;0;256 -# -# => true -# >> mchelp -# -# => true -# >> rpc(:runcommand, :command => "check_disks") do |resp| -# ?> puts resp[:sender] + ": " + resp[:data][:output] -# >> end -# -# * [ ============================================================> ] 47 / 47 -# -# dev1.your.net: DISK OK -# -# => true -# >> -# -# You can access the agent variable via @agent from where you can do the usual manipulation of filters etc, -# if you wish to switch to a different agent mid run just do newagent("some_other_agent") -# -# If you install the Bond gem you'll get some DDL assisted completion in the rpc method -require 'rubygems' -require 'irb' - -def consolize &block - yield - - IRB.setup(nil) - irb = IRB::Irb.new - IRB.conf[:MAIN_CONTEXT] = irb.context - irb.context.evaluate("require 'irb/completion'", 0) - - begin - require 'bond' - Bond.start - - Bond.complete(:method => "rpc") do |e| - begin - if e.argument == 1 - if e.arguments.last == "?" - puts "\n\nActions for #{@agent_name}:\n" - - @agent.ddl.actions.each do |action| - puts "%20s - %s" % [ ":#{action}", @agent.ddl.action_interface(action)[:description] ] - end - - print "\n" + e.line - end - - @agent.ddl.actions - - elsif e.argument > 1 - action = eval(e.arguments[0]).to_s - ddl = @agent.ddl.action_interface(action) - - if e.arguments.last == "?" - puts "\n\nArguments for #{action}:\n" - ddl[:input].keys.each do |input| - puts "%20s - %s" % [ ":#{input}", ddl[:input][input][:description] ] - end - - print "\n" + e.line - end - - [ddl[:input].keys, :verbose].flatten - end - rescue Exception - [] - end - end - rescue Exception - end - - trap("SIGINT") do - irb.signal_handle - end - catch(:IRB_EXIT) do - irb.eval_input - end -end - -def mchelp - system("mc-rpc --agent-help #{@agent_name}|less") - true -end - -def rpc(method_name, *args, &block) - unless block_given? - if args.size > 0 - args = args.first - else - args = {} - end - - if args[:verbose] - args.delete(:verbose) - - printrpc(@agent.send(method_name, args), :verbose => true) - printrpcstats - else - printrpc @agent.send(method_name, args) - printrpcstats - end - - else - @agent.send(method_name, args.first).each do |resp| - yield resp - end - - printrpcstats - end - - true -rescue MCollective::DDLValidationError => e - puts "Request did not pass DDL validation: #{e}" -end - -def print_filter - puts "Active Filter matched #{discover.size} hosts:" - puts "\tIdentity: #{@agent.filter['identity'].pretty_inspect}" - puts "\t Classes: #{@agent.filter['cf_class'].pretty_inspect}" - puts "\t Facts: #{@agent.filter['fact'].pretty_inspect}" - puts "\t Agents: #{@agent.filter['agent'].pretty_inspect}" - - discover.size > 0 ? true : false -end - -def newagent(agent) - @agent_name = agent - - @options[:filter]["agent"] = [] - @agent = rpcclient(@agent_name, :options => @options) - - discover - - @agent.progress = true - - print_filter -end - -def identity_filter(*args) - @agent.identity_filter(*args) - - print_filter -end - -def fact_filter(*args) - @agent.fact_filter(*args) - - print_filter -end - -def agent_filter(*args) - @agent.agent_filter(*args) - - print_filter -end - -def class_filter(*args) - @agent.class_filter(*args) - - print_filter -end - -def reset_filter - @agent.reset_filter - - print_filter -end - -def reset - @agent.reset - - print_filter -end - -def discover - @agent.discover -end - -def mc? - puts < for a list of actions or - arguments, do simple : to get completion on action names and - arguments without description of each - -EOF - true -end - -consolize do - require 'mcollective' - - - include MCollective::RPC - - @options = rpcoptions - - unless ARGV.size == 1 - puts "Please specify an agent name on the command line" - exit 1 - end - - puts "The Marionette Collective Interactive Ruby Shell version #{MCollective.version}" - - puts - newagent(ARGV[0]) - puts - puts "Use mc? to get help on using this shell" -end diff --git a/ext/mc-rpc-restserver.rb b/ext/mc-rpc-restserver.rb deleted file mode 100755 index 1ba68a2d..00000000 --- a/ext/mc-rpc-restserver.rb +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env ruby - -# A very simple demonstration of writing a REST server -# for Simple RPC clients that takes requests over HTTP -# and returns results as JSON structures. - -require 'rubygems' -require 'sinatra' -require 'mcollective' -require 'json' - -include MCollective::RPC - -# http:///mcollective/rpctest/echo/msg=hello%20world -# -# Creates a new Simple RPC client for the 'rpctest' agent, calls -# the echo action with a message 'hello world'. -# -# Returns all the answers as a JSON data block -get '/mcollective/:agent/:action/*' do - mc = rpcclient(params[:agent]) - mc.discover - - arguments = {} - - # split up the wildcard params into key=val pairs and - # build the arguments hash - params[:splat].each do |arg| - arguments[$1.to_sym] = $2 if arg =~ /^(.+?)=(.+)$/ - end - - JSON.dump(mc.send(params[:action], arguments).map{|r| r.results}) -end - diff --git a/ext/openbsd/README b/ext/openbsd/README deleted file mode 100644 index 4467d1d4..00000000 --- a/ext/openbsd/README +++ /dev/null @@ -1,4 +0,0 @@ -These files are meant to be places in /usr/ports/mystuff/sysutils/ and then follow -the OpenBSD guidelines for "custom" ports. - -Happy hacking diff --git a/ext/openbsd/port-files/mcollective/Makefile b/ext/openbsd/port-files/mcollective/Makefile deleted file mode 100644 index 5bd6f48f..00000000 --- a/ext/openbsd/port-files/mcollective/Makefile +++ /dev/null @@ -1,28 +0,0 @@ -PKG_ARCH= * -COMMENT= The Marionette Collective - -DISTNAME= mcollective-2.2.1 - -CATEGORIES= sysutils - -HOMEPAGE= http://puppetlabs.com/mcollective/ -MASTER_SITES= http://puppetlabs.com/downloads/mcollective/ -EXTRACT_SUFX= .tgz - -# GFDL -PERMIT_PACKAGE_CDROM= Yes -PERMIT_PACKAGE_FTP= Yes -PERMIT_DISTFILES_CDROM= Yes -PERMIT_DISTFILES_FTP= Yes - -NO_BUILD= Yes -NO_REGRESS= Yes - -# makefile is in ext/ -MAKE_FILE=ext/Makefile - -post-patch: - ${SUBST_CMD} ${WRKSRC}/etc/server.cfg.dist - rm -f ${WRKSRC}/etc/server.cfg.dist.{orig,beforesubst} - -.include diff --git a/ext/openbsd/port-files/mcollective/distinfo b/ext/openbsd/port-files/mcollective/distinfo deleted file mode 100644 index b351977c..00000000 --- a/ext/openbsd/port-files/mcollective/distinfo +++ /dev/null @@ -1,5 +0,0 @@ -MD5 (mcollective-2.2.1.tgz) = 82t1Sk+HRSXR9L4x4eTaSg== -RMD160 (mcollective-2.2.1.tgz) = PMTcw0VMqrr7rSOYApCW0q8SXok= -SHA1 (mcollective-2.2.1.tgz) = 1yPH0CgmQa5J4VeiDEAExNbn3K4= -SHA256 (mcollective-2.2.1.tgz) = 2KaZk2KrMXPJ7aycM+Uy+0E8hyUsONoP9HkdjpAZ4YM= -SIZE (mcollective-2.2.1.tgz) = 1123400 diff --git a/ext/openbsd/port-files/mcollective/patches/patch-etc_server_cfg_dist b/ext/openbsd/port-files/mcollective/patches/patch-etc_server_cfg_dist deleted file mode 100644 index acf348b7..00000000 --- a/ext/openbsd/port-files/mcollective/patches/patch-etc_server_cfg_dist +++ /dev/null @@ -1,9 +0,0 @@ -$OpenBSD$ ---- etc/server.cfg.dist.orig Thu Jun 24 15:57:17 2010 -+++ etc/server.cfg.dist Thu Jun 24 15:57:25 2010 -@@ -1,5 +1,5 @@ --libdir = /usr/libexec/mcollective -+libdir = ${PREFIX}/share/mcollective/plugins - logfile = /var/log/mcollective.log - loglevel = info - daemonize = 1 diff --git a/ext/openbsd/port-files/mcollective/patches/patch-ext_Makefile b/ext/openbsd/port-files/mcollective/patches/patch-ext_Makefile deleted file mode 100644 index 26e2616a..00000000 --- a/ext/openbsd/port-files/mcollective/patches/patch-ext_Makefile +++ /dev/null @@ -1,67 +0,0 @@ -diff --git ext/Makefile ext/Makefile -index 029fda4..638d7a5 100644 ---- ext/Makefile -+++ ext/Makefile -@@ -1,6 +1,5 @@ - #!/usr/bin/make -f - --DESTDIR= - - build: - -@@ -9,36 +8,34 @@ clean: - install: install-bin install-lib install-conf install-plugins install-doc - - install-bin: -- install -d $(DESTDIR)/usr/sbin -- install -d $(DESTDIR)/usr/bin -- cp bin/mc-* $(DESTDIR)/usr/sbin -- cp bin/mco $(DESTDIR)/usr/bin -- cp bin/mcollectived $(DESTDIR)/usr/sbin/mcollectived -+ install -d $(PREFIX)/sbin -+ install -d $(PREFIX)/bin -+ cp bin/mc-* $(PREFIX)/sbin -+ cp bin/mco $(PREFIX)/bin -+ cp bin/mcollectived $(PREFIX)/sbin/mcollectived - - install-lib: -- install -d $(DESTDIR)/usr/lib/ruby/1.8/ -- cp -a lib/* $(DESTDIR)/usr/lib/ruby/1.8/ -+ install -d $(PREFIX)/lib/ruby/1.8/ -+ cp -R lib/* $(PREFIX)/lib/ruby/1.8/ - - install-conf: -- install -d $(DESTDIR)/etc/mcollective/ -- install -d $(DESTDIR)/etc/init.d -- cp -r etc/* $(DESTDIR)/etc/mcollective/ -- cp mcollective.init $(DESTDIR)/etc/init.d/mcollective -- rm $(DESTDIR)/etc/mcollective/ssl/PLACEHOLDER -- rm $(DESTDIR)/etc/mcollective/ssl/clients/PLACEHOLDER -+ install -d $(PREFIX)/share/examples/mcollective/ -+ cp -R etc/* $(PREFIX)/share/examples/mcollective/ -+ rm $(PREFIX)/share/examples/mcollective/ssl/PLACEHOLDER -+ rm $(PREFIX)/share/examples/mcollective/ssl/clients/PLACEHOLDER - - install-plugins: -- install -d $(DESTDIR)/usr/share/mcollective/ -- cp -a plugins $(DESTDIR)/usr/share/mcollective/ -+ install -d $(PREFIX)/share/mcollective/ -+ cp -R plugins $(PREFIX)/share/mcollective/ - - install-doc: -- install -d $(DESTDIR)/usr/share/doc/ -- cp -a doc $(DESTDIR)/usr/share/doc/mcollective -+ install -d $(PREFIX)/share/doc/ -+ cp -R doc $(PREFIX)/share/doc/mcollective - - uninstall: -- rm -f $(DESTDIR)/usr/sbin/mcollectived -- rm -rf $(DESTDIR)/usr/lib/ruby/1.8/mcollective* -- rm -rf $(DESTDIR)/usr/share/mcollective -- rm -rf $(DESTDIR)/etc/mcollective -+ rm -f $(PREFIX)/sbin/mcollectived -+ rm -rf $(PREFIX)/lib/ruby/1.8/mcollective* -+ rm -rf $(PREFIX)/share/mcollective -+ rm -rf $(PREFIX)/share/examples/mcollective - - .PHONY: build clean install uninstall diff --git a/ext/openbsd/port-files/mcollective/pkg/DESCR b/ext/openbsd/port-files/mcollective/pkg/DESCR deleted file mode 100644 index 5ef48d01..00000000 --- a/ext/openbsd/port-files/mcollective/pkg/DESCR +++ /dev/null @@ -1,2 +0,0 @@ -The Marionette Collective aka. mcollective is a framework to -build server orchestration or parallel job execution systems. diff --git a/ext/openbsd/port-files/mcollective/pkg/MESSAGE b/ext/openbsd/port-files/mcollective/pkg/MESSAGE deleted file mode 100644 index 89fb014c..00000000 --- a/ext/openbsd/port-files/mcollective/pkg/MESSAGE +++ /dev/null @@ -1,3 +0,0 @@ -If you wish to have mcollective started automatically at boot time, -update your pkg_scripts variable, e.g. in /etc/rc.conf.local. -See rc.d(8) for details. diff --git a/ext/openbsd/port-files/mcollective/pkg/PLIST b/ext/openbsd/port-files/mcollective/pkg/PLIST deleted file mode 100644 index 26deecc3..00000000 --- a/ext/openbsd/port-files/mcollective/pkg/PLIST +++ /dev/null @@ -1,432 +0,0 @@ -@comment $OpenBSD$ -bin/mco -lib/ruby/ -lib/ruby/1.8/ -lib/ruby/1.8/mcollective/ -lib/ruby/1.8/mcollective.rb -lib/ruby/1.8/mcollective/agent.rb -lib/ruby/1.8/mcollective/agents.rb -lib/ruby/1.8/mcollective/aggregate/ -lib/ruby/1.8/mcollective/aggregate.rb -lib/ruby/1.8/mcollective/aggregate/base.rb -lib/ruby/1.8/mcollective/aggregate/result/ -lib/ruby/1.8/mcollective/aggregate/result.rb -lib/ruby/1.8/mcollective/aggregate/result/base.rb -lib/ruby/1.8/mcollective/aggregate/result/collection_result.rb -lib/ruby/1.8/mcollective/aggregate/result/numeric_result.rb -lib/ruby/1.8/mcollective/application.rb -lib/ruby/1.8/mcollective/applications.rb -lib/ruby/1.8/mcollective/cache.rb -lib/ruby/1.8/mcollective/client.rb -lib/ruby/1.8/mcollective/config.rb -lib/ruby/1.8/mcollective/connector/ -lib/ruby/1.8/mcollective/connector.rb -lib/ruby/1.8/mcollective/connector/base.rb -lib/ruby/1.8/mcollective/data/ -lib/ruby/1.8/mcollective/data.rb -lib/ruby/1.8/mcollective/data/base.rb -lib/ruby/1.8/mcollective/data/result.rb -lib/ruby/1.8/mcollective/ddl/ -lib/ruby/1.8/mcollective/ddl.rb -lib/ruby/1.8/mcollective/ddl/agentddl.rb -lib/ruby/1.8/mcollective/ddl/base.rb -lib/ruby/1.8/mcollective/ddl/dataddl.rb -lib/ruby/1.8/mcollective/ddl/discoveryddl.rb -lib/ruby/1.8/mcollective/ddl/validatorddl.rb -lib/ruby/1.8/mcollective/discovery.rb -lib/ruby/1.8/mcollective/facts/ -lib/ruby/1.8/mcollective/facts.rb -lib/ruby/1.8/mcollective/facts/base.rb -lib/ruby/1.8/mcollective/generators/ -lib/ruby/1.8/mcollective/generators.rb -lib/ruby/1.8/mcollective/generators/agent_generator.rb -lib/ruby/1.8/mcollective/generators/base.rb -lib/ruby/1.8/mcollective/generators/data_generator.rb -lib/ruby/1.8/mcollective/generators/templates/ -lib/ruby/1.8/mcollective/generators/templates/action_snippet.erb -lib/ruby/1.8/mcollective/generators/templates/data_input_snippet.erb -lib/ruby/1.8/mcollective/generators/templates/ddl.erb -lib/ruby/1.8/mcollective/generators/templates/plugin.erb -lib/ruby/1.8/mcollective/log.rb -lib/ruby/1.8/mcollective/logger/ -lib/ruby/1.8/mcollective/logger.rb -lib/ruby/1.8/mcollective/logger/base.rb -lib/ruby/1.8/mcollective/logger/console_logger.rb -lib/ruby/1.8/mcollective/logger/file_logger.rb -lib/ruby/1.8/mcollective/logger/syslog_logger.rb -lib/ruby/1.8/mcollective/matcher/ -lib/ruby/1.8/mcollective/matcher.rb -lib/ruby/1.8/mcollective/matcher/parser.rb -lib/ruby/1.8/mcollective/matcher/scanner.rb -lib/ruby/1.8/mcollective/message.rb -lib/ruby/1.8/mcollective/monkey_patches.rb -lib/ruby/1.8/mcollective/optionparser.rb -lib/ruby/1.8/mcollective/pluginmanager.rb -lib/ruby/1.8/mcollective/pluginpackager/ -lib/ruby/1.8/mcollective/pluginpackager.rb -lib/ruby/1.8/mcollective/pluginpackager/agent_definition.rb -lib/ruby/1.8/mcollective/pluginpackager/standard_definition.rb -lib/ruby/1.8/mcollective/registration/ -lib/ruby/1.8/mcollective/registration.rb -lib/ruby/1.8/mcollective/registration/base.rb -lib/ruby/1.8/mcollective/rpc/ -lib/ruby/1.8/mcollective/rpc.rb -lib/ruby/1.8/mcollective/rpc/actionrunner.rb -lib/ruby/1.8/mcollective/rpc/agent.rb -lib/ruby/1.8/mcollective/rpc/audit.rb -lib/ruby/1.8/mcollective/rpc/client.rb -lib/ruby/1.8/mcollective/rpc/helpers.rb -lib/ruby/1.8/mcollective/rpc/progress.rb -lib/ruby/1.8/mcollective/rpc/reply.rb -lib/ruby/1.8/mcollective/rpc/request.rb -lib/ruby/1.8/mcollective/rpc/result.rb -lib/ruby/1.8/mcollective/rpc/stats.rb -lib/ruby/1.8/mcollective/runner.rb -lib/ruby/1.8/mcollective/runnerstats.rb -lib/ruby/1.8/mcollective/security/ -lib/ruby/1.8/mcollective/security.rb -lib/ruby/1.8/mcollective/security/base.rb -lib/ruby/1.8/mcollective/shell.rb -lib/ruby/1.8/mcollective/ssl.rb -lib/ruby/1.8/mcollective/unix_daemon.rb -lib/ruby/1.8/mcollective/util.rb -lib/ruby/1.8/mcollective/validator.rb -lib/ruby/1.8/mcollective/vendor/ -lib/ruby/1.8/mcollective/vendor.rb -lib/ruby/1.8/mcollective/vendor/load_systemu.rb -lib/ruby/1.8/mcollective/vendor/require_vendored.rb -lib/ruby/1.8/mcollective/vendor/systemu/ -lib/ruby/1.8/mcollective/vendor/systemu/LICENSE -lib/ruby/1.8/mcollective/vendor/systemu/README -lib/ruby/1.8/mcollective/vendor/systemu/README.erb -lib/ruby/1.8/mcollective/vendor/systemu/Rakefile -lib/ruby/1.8/mcollective/vendor/systemu/lib/ -lib/ruby/1.8/mcollective/vendor/systemu/lib/systemu.rb -lib/ruby/1.8/mcollective/vendor/systemu/samples/ -lib/ruby/1.8/mcollective/vendor/systemu/samples/a.rb -lib/ruby/1.8/mcollective/vendor/systemu/samples/b.rb -lib/ruby/1.8/mcollective/vendor/systemu/samples/c.rb -lib/ruby/1.8/mcollective/vendor/systemu/samples/d.rb -lib/ruby/1.8/mcollective/vendor/systemu/samples/e.rb -lib/ruby/1.8/mcollective/vendor/systemu/samples/f.rb -lib/ruby/1.8/mcollective/vendor/systemu/systemu.gemspec -lib/ruby/1.8/mcollective/windows_daemon.rb -sbin/mcollectived -share/doc/mcollective/ -share/doc/mcollective/Array.html -share/doc/mcollective/COPYING.html -share/doc/mcollective/Dir.html -share/doc/mcollective/MCollective/ -share/doc/mcollective/MCollective.html -share/doc/mcollective/MCollective/Agent.html -share/doc/mcollective/MCollective/Agents.html -share/doc/mcollective/MCollective/Aggregate/ -share/doc/mcollective/MCollective/Aggregate.html -share/doc/mcollective/MCollective/Aggregate/Base.html -share/doc/mcollective/MCollective/Aggregate/Result/ -share/doc/mcollective/MCollective/Aggregate/Result.html -share/doc/mcollective/MCollective/Aggregate/Result/Base.html -share/doc/mcollective/MCollective/Aggregate/Result/CollectionResult.html -share/doc/mcollective/MCollective/Aggregate/Result/NumericResult.html -share/doc/mcollective/MCollective/Application.html -share/doc/mcollective/MCollective/Applications.html -share/doc/mcollective/MCollective/Cache.html -share/doc/mcollective/MCollective/Client.html -share/doc/mcollective/MCollective/Config.html -share/doc/mcollective/MCollective/Connector/ -share/doc/mcollective/MCollective/Connector.html -share/doc/mcollective/MCollective/Connector/Base.html -share/doc/mcollective/MCollective/DDL/ -share/doc/mcollective/MCollective/DDL.html -share/doc/mcollective/MCollective/DDL/AgentDDL.html -share/doc/mcollective/MCollective/DDL/Base.html -share/doc/mcollective/MCollective/DDL/DataDDL.html -share/doc/mcollective/MCollective/DDL/DiscoveryDDL.html -share/doc/mcollective/MCollective/DDL/ValidatorDDL.html -share/doc/mcollective/MCollective/DDLValidationError.html -share/doc/mcollective/MCollective/Data/ -share/doc/mcollective/MCollective/Data.html -share/doc/mcollective/MCollective/Data/Base.html -share/doc/mcollective/MCollective/Data/Result.html -share/doc/mcollective/MCollective/Discovery.html -share/doc/mcollective/MCollective/Facts/ -share/doc/mcollective/MCollective/Facts.html -share/doc/mcollective/MCollective/Facts/Base.html -share/doc/mcollective/MCollective/Generators/ -share/doc/mcollective/MCollective/Generators.html -share/doc/mcollective/MCollective/Generators/AgentGenerator.html -share/doc/mcollective/MCollective/Generators/Base.html -share/doc/mcollective/MCollective/Generators/DataGenerator.html -share/doc/mcollective/MCollective/InvalidRPCData.html -share/doc/mcollective/MCollective/Log.html -share/doc/mcollective/MCollective/Logger/ -share/doc/mcollective/MCollective/Logger.html -share/doc/mcollective/MCollective/Logger/Base.html -share/doc/mcollective/MCollective/Logger/Console_logger.html -share/doc/mcollective/MCollective/Logger/File_logger.html -share/doc/mcollective/MCollective/Logger/Syslog_logger.html -share/doc/mcollective/MCollective/Matcher/ -share/doc/mcollective/MCollective/Matcher.html -share/doc/mcollective/MCollective/Matcher/Parser.html -share/doc/mcollective/MCollective/Matcher/Scanner.html -share/doc/mcollective/MCollective/Message.html -share/doc/mcollective/MCollective/MissingRPCData.html -share/doc/mcollective/MCollective/MsgDoesNotMatchRequestID.html -share/doc/mcollective/MCollective/MsgTTLExpired.html -share/doc/mcollective/MCollective/NotTargettedAtUs.html -share/doc/mcollective/MCollective/Optionparser.html -share/doc/mcollective/MCollective/PluginManager.html -share/doc/mcollective/MCollective/PluginPackager/ -share/doc/mcollective/MCollective/PluginPackager.html -share/doc/mcollective/MCollective/PluginPackager/AgentDefinition.html -share/doc/mcollective/MCollective/PluginPackager/StandardDefinition.html -share/doc/mcollective/MCollective/RPC/ -share/doc/mcollective/MCollective/RPC.html -share/doc/mcollective/MCollective/RPC/ActionRunner.html -share/doc/mcollective/MCollective/RPC/Agent.html -share/doc/mcollective/MCollective/RPC/Audit.html -share/doc/mcollective/MCollective/RPC/Client.html -share/doc/mcollective/MCollective/RPC/Helpers.html -share/doc/mcollective/MCollective/RPC/Progress.html -share/doc/mcollective/MCollective/RPC/Reply.html -share/doc/mcollective/MCollective/RPC/Request.html -share/doc/mcollective/MCollective/RPC/Result.html -share/doc/mcollective/MCollective/RPC/Stats.html -share/doc/mcollective/MCollective/RPCAborted.html -share/doc/mcollective/MCollective/RPCError.html -share/doc/mcollective/MCollective/Registration/ -share/doc/mcollective/MCollective/Registration.html -share/doc/mcollective/MCollective/Registration/Base.html -share/doc/mcollective/MCollective/Runner.html -share/doc/mcollective/MCollective/RunnerStats.html -share/doc/mcollective/MCollective/SSL.html -share/doc/mcollective/MCollective/Security/ -share/doc/mcollective/MCollective/Security.html -share/doc/mcollective/MCollective/Security/Base.html -share/doc/mcollective/MCollective/SecurityValidationFailed.html -share/doc/mcollective/MCollective/Shell.html -share/doc/mcollective/MCollective/UnixDaemon.html -share/doc/mcollective/MCollective/UnknownRPCAction.html -share/doc/mcollective/MCollective/UnknownRPCError.html -share/doc/mcollective/MCollective/Util.html -share/doc/mcollective/MCollective/Validator.html -share/doc/mcollective/MCollective/ValidatorError.html -share/doc/mcollective/MCollective/WindowsDaemon.html -share/doc/mcollective/Object.html -share/doc/mcollective/README.html -share/doc/mcollective/Rakefile.html -share/doc/mcollective/String.html -share/doc/mcollective/Symbol.html -share/doc/mcollective/bin/ -share/doc/mcollective/bin/mco.html -share/doc/mcollective/bin/mcollectived.html -share/doc/mcollective/created.rid -share/doc/mcollective/etc/ -share/doc/mcollective/etc/ssl/ -share/doc/mcollective/etc/ssl/PLACEHOLDER.html -share/doc/mcollective/etc/ssl/clients/ -share/doc/mcollective/etc/ssl/clients/PLACEHOLDER.html -share/doc/mcollective/images/ -share/doc/mcollective/images/brick.png -share/doc/mcollective/images/brick_link.png -share/doc/mcollective/images/bug.png -share/doc/mcollective/images/bullet_black.png -share/doc/mcollective/images/bullet_toggle_minus.png -share/doc/mcollective/images/bullet_toggle_plus.png -share/doc/mcollective/images/date.png -share/doc/mcollective/images/find.png -share/doc/mcollective/images/loadingAnimation.gif -share/doc/mcollective/images/macFFBgHack.png -share/doc/mcollective/images/package.png -share/doc/mcollective/images/page_green.png -share/doc/mcollective/images/page_white_text.png -share/doc/mcollective/images/page_white_width.png -share/doc/mcollective/images/plugin.png -share/doc/mcollective/images/ruby.png -share/doc/mcollective/images/tag_green.png -share/doc/mcollective/images/wrench.png -share/doc/mcollective/images/wrench_orange.png -share/doc/mcollective/images/zoom.png -share/doc/mcollective/index.html -share/doc/mcollective/js/ -share/doc/mcollective/js/darkfish.js -share/doc/mcollective/js/jquery.js -share/doc/mcollective/js/quicksearch.js -share/doc/mcollective/js/thickbox-compressed.js -share/doc/mcollective/lib/ -share/doc/mcollective/lib/mcollective/ -share/doc/mcollective/lib/mcollective/agent_rb.html -share/doc/mcollective/lib/mcollective/agents_rb.html -share/doc/mcollective/lib/mcollective/aggregate/ -share/doc/mcollective/lib/mcollective/aggregate/base_rb.html -share/doc/mcollective/lib/mcollective/aggregate/result/ -share/doc/mcollective/lib/mcollective/aggregate/result/base_rb.html -share/doc/mcollective/lib/mcollective/aggregate/result/collection_result_rb.html -share/doc/mcollective/lib/mcollective/aggregate/result/numeric_result_rb.html -share/doc/mcollective/lib/mcollective/aggregate/result_rb.html -share/doc/mcollective/lib/mcollective/aggregate_rb.html -share/doc/mcollective/lib/mcollective/application_rb.html -share/doc/mcollective/lib/mcollective/applications_rb.html -share/doc/mcollective/lib/mcollective/cache_rb.html -share/doc/mcollective/lib/mcollective/client_rb.html -share/doc/mcollective/lib/mcollective/config_rb.html -share/doc/mcollective/lib/mcollective/connector/ -share/doc/mcollective/lib/mcollective/connector/base_rb.html -share/doc/mcollective/lib/mcollective/connector_rb.html -share/doc/mcollective/lib/mcollective/data/ -share/doc/mcollective/lib/mcollective/data/base_rb.html -share/doc/mcollective/lib/mcollective/data/result_rb.html -share/doc/mcollective/lib/mcollective/data_rb.html -share/doc/mcollective/lib/mcollective/ddl/ -share/doc/mcollective/lib/mcollective/ddl/agentddl_rb.html -share/doc/mcollective/lib/mcollective/ddl/base_rb.html -share/doc/mcollective/lib/mcollective/ddl/dataddl_rb.html -share/doc/mcollective/lib/mcollective/ddl/discoveryddl_rb.html -share/doc/mcollective/lib/mcollective/ddl/validatorddl_rb.html -share/doc/mcollective/lib/mcollective/ddl_rb.html -share/doc/mcollective/lib/mcollective/discovery_rb.html -share/doc/mcollective/lib/mcollective/facts/ -share/doc/mcollective/lib/mcollective/facts/base_rb.html -share/doc/mcollective/lib/mcollective/facts_rb.html -share/doc/mcollective/lib/mcollective/generators/ -share/doc/mcollective/lib/mcollective/generators/agent_generator_rb.html -share/doc/mcollective/lib/mcollective/generators/base_rb.html -share/doc/mcollective/lib/mcollective/generators/data_generator_rb.html -share/doc/mcollective/lib/mcollective/generators_rb.html -share/doc/mcollective/lib/mcollective/log_rb.html -share/doc/mcollective/lib/mcollective/logger/ -share/doc/mcollective/lib/mcollective/logger/base_rb.html -share/doc/mcollective/lib/mcollective/logger/console_logger_rb.html -share/doc/mcollective/lib/mcollective/logger/file_logger_rb.html -share/doc/mcollective/lib/mcollective/logger/syslog_logger_rb.html -share/doc/mcollective/lib/mcollective/logger_rb.html -share/doc/mcollective/lib/mcollective/matcher/ -share/doc/mcollective/lib/mcollective/matcher/parser_rb.html -share/doc/mcollective/lib/mcollective/matcher/scanner_rb.html -share/doc/mcollective/lib/mcollective/matcher_rb.html -share/doc/mcollective/lib/mcollective/message_rb.html -share/doc/mcollective/lib/mcollective/monkey_patches_rb.html -share/doc/mcollective/lib/mcollective/optionparser_rb.html -share/doc/mcollective/lib/mcollective/pluginmanager_rb.html -share/doc/mcollective/lib/mcollective/pluginpackager/ -share/doc/mcollective/lib/mcollective/pluginpackager/agent_definition_rb.html -share/doc/mcollective/lib/mcollective/pluginpackager/standard_definition_rb.html -share/doc/mcollective/lib/mcollective/pluginpackager_rb.html -share/doc/mcollective/lib/mcollective/registration/ -share/doc/mcollective/lib/mcollective/registration/base_rb.html -share/doc/mcollective/lib/mcollective/registration_rb.html -share/doc/mcollective/lib/mcollective/rpc/ -share/doc/mcollective/lib/mcollective/rpc/actionrunner_rb.html -share/doc/mcollective/lib/mcollective/rpc/agent_rb.html -share/doc/mcollective/lib/mcollective/rpc/audit_rb.html -share/doc/mcollective/lib/mcollective/rpc/client_rb.html -share/doc/mcollective/lib/mcollective/rpc/helpers_rb.html -share/doc/mcollective/lib/mcollective/rpc/progress_rb.html -share/doc/mcollective/lib/mcollective/rpc/reply_rb.html -share/doc/mcollective/lib/mcollective/rpc/request_rb.html -share/doc/mcollective/lib/mcollective/rpc/result_rb.html -share/doc/mcollective/lib/mcollective/rpc/stats_rb.html -share/doc/mcollective/lib/mcollective/rpc_rb.html -share/doc/mcollective/lib/mcollective/runner_rb.html -share/doc/mcollective/lib/mcollective/runnerstats_rb.html -share/doc/mcollective/lib/mcollective/security/ -share/doc/mcollective/lib/mcollective/security/base_rb.html -share/doc/mcollective/lib/mcollective/security_rb.html -share/doc/mcollective/lib/mcollective/shell_rb.html -share/doc/mcollective/lib/mcollective/ssl_rb.html -share/doc/mcollective/lib/mcollective/unix_daemon_rb.html -share/doc/mcollective/lib/mcollective/util_rb.html -share/doc/mcollective/lib/mcollective/validator_rb.html -share/doc/mcollective/lib/mcollective/windows_daemon_rb.html -share/doc/mcollective/lib/mcollective_rb.html -share/doc/mcollective/rdoc.css -share/examples/mcollective/ -@sample ${SYSCONFDIR}/mcollective/ -share/examples/mcollective/client.cfg.dist -@sample ${SYSCONFDIR}/mcollective/client.cfg -share/examples/mcollective/data-help.erb -share/examples/mcollective/discovery-help.erb -share/examples/mcollective/facts.yaml.dist -@sample ${SYSCONFDIR}/mcollective/facts.yaml -share/examples/mcollective/metadata-help.erb -share/examples/mcollective/rpc-help.erb -share/examples/mcollective/server.cfg.dist -@sample ${SYSCONFDIR}/mcollective/server.cfg -share/examples/mcollective/ssl/ -@sample ${SYSCONFDIR}/mcollective/ssl/ -share/examples/mcollective/ssl/clients/ -@sample ${SYSCONFDIR}/mcollective/ssl/clients/ -share/mcollective/ -share/mcollective/plugins/ -share/mcollective/plugins/mcollective/ -share/mcollective/plugins/mcollective/agent/ -share/mcollective/plugins/mcollective/agent/discovery.rb -share/mcollective/plugins/mcollective/agent/rpcutil.ddl -share/mcollective/plugins/mcollective/agent/rpcutil.rb -share/mcollective/plugins/mcollective/aggregate/ -share/mcollective/plugins/mcollective/aggregate/average.rb -share/mcollective/plugins/mcollective/aggregate/sum.rb -share/mcollective/plugins/mcollective/aggregate/summary.rb -share/mcollective/plugins/mcollective/application/ -share/mcollective/plugins/mcollective/application/completion.rb -share/mcollective/plugins/mcollective/application/facts.rb -share/mcollective/plugins/mcollective/application/find.rb -share/mcollective/plugins/mcollective/application/help.rb -share/mcollective/plugins/mcollective/application/inventory.rb -share/mcollective/plugins/mcollective/application/ping.rb -share/mcollective/plugins/mcollective/application/plugin.rb -share/mcollective/plugins/mcollective/application/rpc.rb -share/mcollective/plugins/mcollective/audit/ -share/mcollective/plugins/mcollective/audit/logfile.rb -share/mcollective/plugins/mcollective/connector/ -share/mcollective/plugins/mcollective/connector/activemq.rb -share/mcollective/plugins/mcollective/connector/rabbitmq.rb -share/mcollective/plugins/mcollective/connector/stomp.rb -share/mcollective/plugins/mcollective/data/ -share/mcollective/plugins/mcollective/data/agent_data.ddl -share/mcollective/plugins/mcollective/data/agent_data.rb -share/mcollective/plugins/mcollective/data/fstat_data.ddl -share/mcollective/plugins/mcollective/data/fstat_data.rb -share/mcollective/plugins/mcollective/discovery/ -share/mcollective/plugins/mcollective/discovery/flatfile.ddl -share/mcollective/plugins/mcollective/discovery/flatfile.rb -share/mcollective/plugins/mcollective/discovery/mc.ddl -share/mcollective/plugins/mcollective/discovery/mc.rb -share/mcollective/plugins/mcollective/facts/ -share/mcollective/plugins/mcollective/facts/yaml_facts.rb -share/mcollective/plugins/mcollective/pluginpackager/ -share/mcollective/plugins/mcollective/pluginpackager/debpackage_packager.rb -share/mcollective/plugins/mcollective/pluginpackager/ospackage_packager.rb -share/mcollective/plugins/mcollective/pluginpackager/rpmpackage_packager.rb -share/mcollective/plugins/mcollective/pluginpackager/templates/ -share/mcollective/plugins/mcollective/pluginpackager/templates/debian/ -share/mcollective/plugins/mcollective/pluginpackager/templates/debian/Makefile.erb -share/mcollective/plugins/mcollective/pluginpackager/templates/debian/changelog.erb -share/mcollective/plugins/mcollective/pluginpackager/templates/debian/compat.erb -share/mcollective/plugins/mcollective/pluginpackager/templates/debian/control.erb -share/mcollective/plugins/mcollective/pluginpackager/templates/debian/copyright.erb -share/mcollective/plugins/mcollective/pluginpackager/templates/debian/rules.erb -share/mcollective/plugins/mcollective/pluginpackager/templates/redhat/ -share/mcollective/plugins/mcollective/pluginpackager/templates/redhat/rpm_spec.erb -share/mcollective/plugins/mcollective/registration/ -share/mcollective/plugins/mcollective/registration/agentlist.rb -share/mcollective/plugins/mcollective/security/ -share/mcollective/plugins/mcollective/security/aes_security.rb -share/mcollective/plugins/mcollective/security/psk.rb -share/mcollective/plugins/mcollective/security/ssl.rb -share/mcollective/plugins/mcollective/validator/ -share/mcollective/plugins/mcollective/validator/array_validator.ddl -share/mcollective/plugins/mcollective/validator/array_validator.rb -share/mcollective/plugins/mcollective/validator/ipv4address_validator.ddl -share/mcollective/plugins/mcollective/validator/ipv4address_validator.rb -share/mcollective/plugins/mcollective/validator/ipv6address_validator.ddl -share/mcollective/plugins/mcollective/validator/ipv6address_validator.rb -share/mcollective/plugins/mcollective/validator/length_validator.ddl -share/mcollective/plugins/mcollective/validator/length_validator.rb -share/mcollective/plugins/mcollective/validator/regex_validator.ddl -share/mcollective/plugins/mcollective/validator/regex_validator.rb -share/mcollective/plugins/mcollective/validator/shellsafe_validator.ddl -share/mcollective/plugins/mcollective/validator/shellsafe_validator.rb -share/mcollective/plugins/mcollective/validator/typecheck_validator.ddl -share/mcollective/plugins/mcollective/validator/typecheck_validator.rb -@rcscript ${RCDIR}/mcollectived diff --git a/ext/openbsd/port-files/mcollective/pkg/mcollectived.rc b/ext/openbsd/port-files/mcollective/pkg/mcollectived.rc deleted file mode 100644 index 7b6de9d7..00000000 --- a/ext/openbsd/port-files/mcollective/pkg/mcollectived.rc +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh - -daemon="${TRUEPREFIX}/sbin/mcollectived" - -. /etc/rc.d/rc.subr - -pexp=".*ruby.* ${daemon}${daemon_flags:+ ${daemon_flags}}" -rc_reload=NO - -rc_cmd $1 diff --git a/ext/osx/README b/ext/osx/README deleted file mode 100644 index 72750b5f..00000000 --- a/ext/osx/README +++ /dev/null @@ -1,15 +0,0 @@ -This script will automatically build Mac packages based on your source -tarball. The only argument is the path to the untarred source directory. - -It automatically determines the MCollective version so it will only work -with 0.4.9 and forward. It loads mcollective in ruby so it requires -that the stomp rubygem be installed. - -I could just grep the mcollective.rb script but I wanted it to be future proof. -It also requires XCode be installed to have PackageMaker available. It sets -the server.cfg file daemonize setting to 0 since my launchd plist will handle -that. I also have launchd set to restart mcollectived if it stops running. - -Let me know if you run in to trouble with it. - -Carl Caum - carl _at_ carlcaum.com diff --git a/ext/osx/bldmacpkg b/ext/osx/bldmacpkg deleted file mode 100644 index cbaa5f32..00000000 --- a/ext/osx/bldmacpkg +++ /dev/null @@ -1,146 +0,0 @@ -#!/bin/bash -MPATH="$1" -BETCDIR='/etc/mcollective' -BRUBYDIR='/Library/Ruby/Site/1.8' -BSBINDIR='/usr/sbin' -BBINDIR='/usr/bin' -BLIBEXECDIR='/usr/libexec/mcollective' -BDOCDIR='/usr/share/doc/mcollective' -BLAUNCHDIR='/Library/LaunchDaemons' -BLOGDIR='/var/log/mcollective' -PACKAGEMAKER='/Applications/PackageMaker.app/Contents/MacOS/PackageMaker' - -if [ -z $MPATH ]; then - echo 'Please give the path to the MCollective source directory' - exit 1 -fi - -msg_stomp() { - echo "It is recommended to install stomp on this system using ruby gems" - exit 2 -} - -msg_xcode() { - echo 'It is required to have the latest XCode installed' - exit 3 -} - -# Make sure we have stomp so we can load mcollective -/usr/bin/ruby < $tmpdir/$BLAUNCHDIR/org.marionette-collective.mcollective.plist < - - - - EnvironmentVariables - - PATH - /sbin:/usr/sbin:/bin:/usr/bin - RUBYLIB - /Library/Ruby/Site/1.8 - - Label - org.marionette-collective.mcollective - OnDemand - - KeepAlive - - ProgramArguments - - /usr/sbin/mcollectived - --config=/etc/mcollective/server.cfg - - RunAtLoad - - ServiceDescription - MCollective Server - ServiceIPC - - - -EOF - - -#Make our Packages. This requires XCode be installed -$PACKAGEMAKER -r $tmpdir --version $mcversion --title "MCollective" -l / -o $pkgdir/MCollective_$mcversion.pkg -i org.marionette-collective.mcollective -$PACKAGEMAKER -r $common_tmpdir --version $mcversion --title "MCollective Common" -l / -o $pkgdir/MCollective-Common_$mcversion.pkg -i org.marionette-collective.mcollective-common -$PACKAGEMAKER -r $client_tmpdir --version $mcversion --title "MCollective Client" -l / -o $pkgdir/MCollective-Client_$mcversion.pkg -i org.marionette-collective.mcollective-client - -# Make sure that we install the stomp gem, this is ugly and should be part of the package -cat - > $pkgdir/MCollective-Common_$mcversion.pkg/Contents/Resources/postflight < $pkgdir/MCollective_$mcversion.pkg/Contents/Resources/postflight < e - STDERR.puts "Unable to load yaml from #{build_defs_file}:" - raise e - end - @packaging_url = @build_defaults['packaging_url'] - @packaging_repo = @build_defaults['packaging_repo'] - raise "Could not find packaging url in #{build_defs_file}" if @packaging_url.nil? - raise "Could not find packaging repo in #{build_defs_file}" if @packaging_repo.nil? - - namespace :package do - desc "Bootstrap packaging automation, e.g. clone into packaging repo" - task :bootstrap do - if File.exist?(File.join(RAKE_ROOT, "ext", @packaging_repo)) - puts "It looks like you already have ext/#{@packaging_repo}. If you don't like it, blow it away with package:implode." - else - cd File.join(RAKE_ROOT, 'ext') do - %x{git clone #{@packaging_url}} - end - end - end - desc "Remove all cloned packaging automation" - task :implode do - rm_rf File.join(RAKE_ROOT, "ext", @packaging_repo) - end - end -end - -begin - load File.join(RAKE_ROOT, 'ext', 'packaging', 'packaging.rake') -rescue LoadError -end diff --git a/ext/perl/mc-find-hosts.pl b/ext/perl/mc-find-hosts.pl deleted file mode 100644 index 15f5f4f6..00000000 --- a/ext/perl/mc-find-hosts.pl +++ /dev/null @@ -1,80 +0,0 @@ -#!/usr/bin/perl - -# A simple Perl client for mcollective that just demonstrates -# how to construct requests, send them and process results. -# -# This is in effect a mc-find-hosts equivelant, you can fill in -# filters in the request and only the matching ones will reply. -# -# For this to work you need the SSL security plugin in MCollective -# 1.0.0 set to operate in YAML mode. - -use YAML::Syck; -use Digest::MD5 qw(md5 md5_hex md5_base64); -use Crypt::OpenSSL::RSA; -use MIME::Base64; -use Net::STOMP::Client; -use Data::Dumper; - -# The topics from your activemq, /topic/mcollective_dev/... -$mcollective_prefix = "mcollective_dev"; - -# Path to your SSL private key and what it's called so the -# mcollectived will load yours -$ssl_private_key = "/path/to/your.pem"; -$ssl_private_key_name = "you"; - -# A string representing your sending host -$mcollective_client_identity = "devel.your.com-perl"; - -# Stomp connection parameters -$stomp_host = "localhost"; -$stomp_port = 6163; -$stomp_user = "your"; -$stomp_password = "secret"; - -$YAML::Syck::ImplicitTyping = 1; - -$request{":msgtime"} = time(); -$request{":filter"}{"identity"} = []; -$request{":filter"}{"fact"} = []; -$request{":filter"}{"agent"} = []; -$request{":filter"}{"cf_class"} = []; -$request{":requestid"} = md5_hex(time() . $$); -$request{":callerid"} = "cert=${ssl_private_key_name}"; -$request{":senderid"} = $mcollective_client_identity; -$request{":body"} = Dump("ping"); -$request{":msgtarget"} = "/topic/${mcollective_prefix}.discovery.command"; - -$key = ""; - -open(SSL, $ssl_private_key); - while() { - $key = $key . $_; - } -close(SSL); - -$rsa = Crypt::OpenSSL::RSA->new_private_key($key); -$request{":hash"} = encode_base64($rsa->sign($request{":body"})); - -$mcrequest = Dump(\%request); - -$stomp = Net::STOMP::Client->new(host => $stomp_host, port => $stomp_port); -$stomp->connect(login => $stomp_user, passcode => $stomp_password); - -$stomp->message_callback(sub { - my ($self, $frame) = @_; - - $mc_reply = Load($frame->body); - $mc_body = Load($mc_reply->{":body"}); - print $mc_reply->{":senderid"} . "> " . $mc_body . "\n"; - - return($self); - }); - -$stomp->subscribe(destination => "/topic/${mcollective_prefix}.discovery.reply"); -$stomp->send(destination => "/topic/${mcollective_prefix}.discovery.command", body => $mcrequest); -$stomp->wait_for_frames(callback => sub { return(0) }, timeout => 5); -$stomp->disconnect(); - - diff --git a/ext/project_data.yaml b/ext/project_data.yaml deleted file mode 100644 index 3ee69b68..00000000 --- a/ext/project_data.yaml +++ /dev/null @@ -1,41 +0,0 @@ ---- -project: 'mcollective' -author: 'Puppet Labs' -email: 'info@puppetlabs.com' -homepage: 'https://docs.puppetlabs.com/mcollective/' -summary: 'Application Server for hosting Ruby code on any capable middleware' -description: 'The Marionette Collective, e.g. mcollective, is a framework for building server orchestration or parallel job execution systems.' -# files and gem_files are space separated lists -files: 'mcollective.init COPYING doc etc lib plugins ext bin install.rb' -# List of packaging related templates to evaluate before the tarball is packed -templates: - - "ext/redhat/mcollective.spec.erb" - - "ext/debian/changelog.erb" -# The gem is only built for the mcollective client -gem_name: 'mcollective-client' -gem_summary: 'Client libraries for the Mcollective Application Server' -gem_description: 'Client libraries for the Mcollective Application Server' -gem_files: 'lib bin' -gem_test_files: 'spec/**/*' -gem_require_path: 'lib' -gem_executables: 'mco' -gem_default_executables: 'mco' -gem_excludes: "bin/mcollectived lib/mcollective/runner.rb lib/mcollective/vendor/systemu lib/mcollective/vendor/load_systemu.rb" -gem_runtime_dependencies: - systemu: - json: - stomp: -gem_rdoc_options: - - --line-numbers - - --main - - Mcollective - - --exclude - - mcollective/vendor - - --exclude - - spec - - --exclude - - ext - - --exclude - - website - - --exclude - - plugins diff --git a/ext/redhat/mcollective.init b/ext/redhat/mcollective.init deleted file mode 100755 index 9993d85c..00000000 --- a/ext/redhat/mcollective.init +++ /dev/null @@ -1,139 +0,0 @@ -#!/bin/sh -# -# mcollective Application Server for STOMP based agents -# -# chkconfig: - 24 76 -# -# description: mcollective lets you build powerful Stomp compatible middleware clients in ruby without having to worry too -# much about all the setup and management of a Stomp connection, it also provides stats, logging and so forth -# as a bonus. -# -### BEGIN INIT INFO -# Provides: mcollective -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Short-Description: Start daemon at boot time -# Description: Enable service provided by daemon. -### END INIT INFO - -mcollectived="/usr/sbin/mcollectived" -pidfile="/var/run/mcollectived.pid" -if [ -d /var/lock/subsys ]; then - # RedHat/CentOS/etc who use subsys - lockfile="/var/lock/subsys/mcollective" -else - # The rest of them - lockfile="/var/lock/mcollective" -fi - -# Check that binary exists -if ! [ -f $mcollectived ]; then - echo "mcollectived binary not found" - exit 5 -fi - -# Source function library. -. /etc/init.d/functions - -if [ -f /etc/sysconfig/mcollective ]; then - . /etc/sysconfig/mcollective -fi - -# Determine if we can use the -p option to daemon, killproc, and status. -# RHEL < 5 can't. -if status | grep -q -- '-p' 2>/dev/null; then - daemonopts="--pidfile $pidfile" - pidopts="-p $pidfile" -fi - -start() { - echo -n "Starting mcollective: " - # Only try to start if not already started - if ! rh_status_q; then - daemon ${daemonopts} ${mcollectived} --pid=${pidfile} --config="/etc/mcollective/server.cfg" --daemonize - fi - # This will be 0 if mcollective is already running - RETVAL=$? - echo - [ $RETVAL -eq 0 ] && touch ${lockfile} - return $RETVAL -} - -stop() { - echo -n "Shutting down mcollective: " - # If running, try to stop it - if rh_status_q; then - killproc ${pidopts} -d 10 ${mcollectived} - else - # Non-zero status either means lockfile and pidfile need cleanup (1 and 2) - # or the process is already stopped (3), so we can just call true to - # trigger the cleanup that happens below. - true - fi - RETVAL=$? - echo - [ $RETVAL = 0 ] && rm -f ${lockfile} ${pidfile} - return $RETVAL -} - -restart() { - stop - start -} - -reload_agents() { - echo -n "Reloading mcollective agents: " - killproc ${pidopts} ${mcollectived} -USR1 - RETVAL=$? - echo - return $RETVAL -} - -reload_loglevel() { - echo -n "Cycling mcollective logging level: " - killproc ${pidopts} ${mcollectived} -USR2 - RETVAL=$? - echo - return $RETVAL -} - -rh_status() { - status ${pidopts} ${mcollectived} - RETVAL=$? - return $RETVAL -} - -rh_status_q() { - rh_status >/dev/null 2>&1 -} - -# See how we were called. -case "$1" in - start) - start - ;; - stop) - stop - ;; - restart) - restart - ;; - condrestart) - rh_status_q || exit 0 - restart - ;; - reload-agents) - reload_agents - ;; - reload-loglevel) - reload_loglevel - ;; - status) - rh_status - ;; - *) - echo "Usage: mcollectived {start|stop|restart|condrestart|reload-agents|reload-loglevel|status}" - RETVAL=2 - ;; -esac -exit $RETVAL diff --git a/ext/redhat/mcollective.service b/ext/redhat/mcollective.service deleted file mode 100644 index 70ad570a..00000000 --- a/ext/redhat/mcollective.service +++ /dev/null @@ -1,25 +0,0 @@ -# -# Local settings can be configured without being overwritten by package upgrades, for example -# if you want to increase mcollective open-files-limit to 10000, -# you need to increase systemd's LimitNOFILE setting, so create a file named -# "/etc/systemd/system/mcollective.service.d/limits.conf" containing: -# [Service] -# LimitNOFILE=10000 -# You can confirm it worked by running systemctl daemon-reload -# then running systemctl show mcollective | grep LimitNOFILE -# -[Unit] -Description=The Marionette Collective -After=network.target - -[Service] -Type=simple -StandardOutput=syslog -StandardError=syslog -ExecStart=/usr/sbin/mcollectived --config=/etc/mcollective/server.cfg --pidfile=/var/run/mcollective.pid --no-daemonize -ExecReload=/bin/kill -USR1 $MAINPID -PIDFile=/var/run/mcollective.pid -KillMode=process - -[Install] -WantedBy=multi-user.target diff --git a/ext/redhat/mcollective.spec.erb b/ext/redhat/mcollective.spec.erb deleted file mode 100644 index d6105bbe..00000000 --- a/ext/redhat/mcollective.spec.erb +++ /dev/null @@ -1,173 +0,0 @@ -# Fedora 17 and later use vendorlibdir instead of sitelibdir (see https://fedoraproject.org/wiki/Packaging:Ruby?rd=Packaging/Ruby#Pure_Ruby_packages) -%if 0%{?fedora} >= 17 || 0%{?rhel} >= 7 -%global ruby_libdir %(ruby -rrbconfig -e 'puts RbConfig::CONFIG["vendorlibdir"]') -%else -%global ruby_libdir %(ruby -rrbconfig -e "puts RbConfig::CONFIG['sitelibdir']") -%endif - -%if 0%{?fedora} >= 17 || 0%{?rhel} >= 7 -%global _with_systemd 1 -%else -%global _with_systemd 0 -%endif - -# VERSION is subbed out during rake package:srpm process -%global realversion <%= @version %> -%global rpmversion <%= @rpmversion %> - -Summary: Application Server for hosting Ruby code on any capable middleware -Name: mcollective -Version: %{rpmversion} -Release: <%= @rpmrelease -%>%{?dist} -Group: System Environment/Daemons -License: ASL 2.0 -URL: http://puppetlabs.com/mcollective/introduction/ -Source0: http://downloads.puppetlabs.com/mcollective/%{name}-%{realversion}.tar.gz -BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) -BuildRequires: ruby >= 1.8 -Requires: mcollective-common = %{version}-%{release} -Packager: Puppet Labs -BuildArch: noarch - -%if 0%{?_with_systemd} -# Required for %%post, %%preun, %%postun -Requires: systemd -%if 0%{?fedora} >= 18 || 0%{?rhel} >= 7 -BuildRequires: systemd -%else -BuildRequires: systemd-units -%endif -%else -# Required for %%post and %%preun -Requires: chkconfig -# Required for %%preun and %%postun -Requires: initscripts -%endif - -%description -The Marionette Collective: - -Server for the mcollective Application Server - -%package common -Summary: Common libraries for the mcollective clients and servers -Group: System Environment/Libraries -Requires: ruby >= 1.8 -Requires: rubygems >= 1.3.7 -Requires: rubygem-stomp -Requires: rubygem-json - -%description common -The Marionette Collective: - -Common libraries for the mcollective clients and servers - -%package client -Summary: Client tools for the mcollective Application Server -Requires: mcollective-common = %{version}-%{release} -Group: Applications/System - -%description client -The Marionette Collective: - -Client tools for the mcollective Application Server - -%prep -%setup -q -n %{name}-%{realversion} - -%build - -%install -rm -rf %{buildroot} - -ruby install.rb --destdir=%{buildroot} --no-rdoc --sitelibdir=%{ruby_libdir} --plugindir=%{_libexecdir}/mcollective - -%if 0%{?_with_systemd} -%{__install} -d -m0755 %{buildroot}%{_unitdir} -%{__install} -m0644 ext/redhat/mcollective.service %{buildroot}%{_unitdir}/mcollective.service -%else -%{__install} -d -m0755 %{buildroot}%{_sysconfdir}/init.d -%if 0%{?suse_version} -%{__install} -m0755 mcollective.init %{buildroot}%{_sysconfdir}/init.d/mcollective -%else -%{__install} -m0755 ext/redhat/mcollective.init %{buildroot}%{_sysconfdir}/init.d/mcollective -%endif -%endif - -%{__install} -d -m0755 %{buildroot}%{_sysconfdir}/mcollective/plugin.d -%{__install} -d -m0755 %{buildroot}%{_sysconfdir}/mcollective/ssl/clients - -%clean -rm -rf %{buildroot} - -%post -%if 0%{?_with_systemd} -if [ $1 -eq 1 ] ; then - /bin/systemctl daemon-reload >/dev/null 2>&1 || : -fi -%else -/sbin/chkconfig --add mcollective || : -%endif - -%postun -%if 0%{?_with_systemd} -if [ $1 -ge 1 ] ; then - # Package upgrade, not uninstall - /bin/systemctl try-restart mcollective.service >/dev/null 2>&1 || : -fi -%else -if [ "$1" -ge 1 ]; then - /sbin/service mcollective condrestart &>/dev/null || : -fi -%endif - -%preun -%if 0%{?_with_systemd} -if [ $1 -eq 0 ] ; then - # Package removal, not upgrade - /bin/systemctl --no-reload disable mcollective.service > /dev/null 2>&1 || : - /bin/systemctl stop mcollective.service > /dev/null 2>&1 || : -fi -%else -if [ "$1" = 0 ] ; then - /sbin/service mcollective stop > /dev/null 2>&1 - /sbin/chkconfig --del mcollective || : -fi -%endif - -%files common -%defattr(-, root, root, 0755) -%doc COPYING -%doc doc -%{ruby_libdir}/mcollective.rb -%{ruby_libdir}/mcollective -%dir %{_sysconfdir}/mcollective -%dir %{_sysconfdir}/mcollective/ssl -%config %{_sysconfdir}/mcollective/*.erb - -%files client -%defattr(-, root, root, 0755) -%attr(0755, root, root)%{_bindir}/mco -%doc COPYING -%config(noreplace)%{_sysconfdir}/mcollective/client.cfg - -%files -%defattr(-, root, root, 0755) -%doc COPYING -%attr(0755, root, root)%{_sbindir}/mcollectived -%if 0%{?_with_systemd} -%{_unitdir}/mcollective.service -%else -%{_sysconfdir}/init.d/mcollective -%endif -%config(noreplace)%{_sysconfdir}/mcollective/server.cfg -%config(noreplace)%{_sysconfdir}/mcollective/facts.yaml -%dir %{_sysconfdir}/mcollective/ssl/clients -%config(noreplace)%{_sysconfdir}/mcollective/plugin.d - -%changelog -* <%= Time.now.strftime("%a %b %d %Y") %> Puppet Labs Release - <%= @rpmversion %>-<%= @rpmrelease %> -- Build for <%= @version %> - -* Tue Nov 03 2009 R.I.Pienaar -- First release diff --git a/ext/solaris/README b/ext/solaris/README deleted file mode 100644 index 4828511b..00000000 --- a/ext/solaris/README +++ /dev/null @@ -1,33 +0,0 @@ -Building --------- - -Requirements, you can get them from opencsw: -- coreuitls (CSWcoreutils) -- gmake (CSWgmake) -- ggrep (CSWggrep) - -Just run ./build on your solaris system. - -Running -------- - -Requirements, get them from opencsw: -- ruby (CSWruby) -- rubygems (CSWrubygems) - -Run requirements -- rubystomp library - https://rubygems.org/gems/stomp - Up and till version 1.0.4 it is a single file. Put in /opt/csw/lib/ruby/site_ruby/1.8/ - -Configuration -------------- - -/etc/mcollective/server.cfg - -Put the plugins in: -libdir = /opt/csw/share/mcollective/plugins - -Credits -------- -Rudy Gevaert diff --git a/ext/solaris/build b/ext/solaris/build deleted file mode 100755 index b4402230..00000000 --- a/ext/solaris/build +++ /dev/null @@ -1,68 +0,0 @@ -#!/opt/csw/bin/gmake -f -d -# -*- makefile -*- - -BUILDDIR = solaris/tmp -PKG = solaris/pkg -DESTDIR = ${CURDIR}/${BUILDDIR} -PKGDIR = ${CURDIR}/${PKG} -PKGNAME = CSWmcollective -VERSION = $(shell cd ../.. ; RUBYLIB=./lib /opt/csw/bin/ruby18 -r mcollective -e 'puts MCollective::VERSION' ) -# If we checked out from git: -ifeq ($(VERSION),@DEVELOPMENT_VERSION@) - VERSION = $(shell ggrep "PROJ_VERSION = " ../../Rakefile | cut -d' ' -f3 | sed -e 's/"//g') -endif -RELEASE = 1 -PKGVERSION = ${VERSION}\,REV=$(shell date +%Y.%m.%d) -RUBY_VERSION = 1.8 -RUBY_SITE = ${DESTDIR}/opt/csw/lib/ruby/site_ruby/${RUBY_VERSION} - -install: - # install directories - ginstall -d $(DESTDIR) - ginstall -g root -d $(DESTDIR)/opt - ginstall -g sys -d $(DESTDIR)/var $(DESTDIR)/var/lock $(DESTDIR)/etc $(DESTDIR)/etc/opt - ginstall -g bin -d $(DESTDIR)/var/opt $(DESTDIR)/var/opt/csw $(DESTDIR)/var/opt/csw/svc $(DESTDIR)/var/opt/csw/svc/manifest $(DESTDIR)/var/opt/csw/svc/manifest/network - ginstall -g bin -d $(DESTDIR)/opt/csw/lib $(DESTDIR)/opt/csw/lib/svc $(DESTDIR)/opt/csw/lib/svc/method - ginstall -g bin -d $(DESTDIR)/opt/csw $(DESTDIR)/opt/csw/lib $(DESTDIR)/opt/csw/sbin $(DESTDIR)/opt/csw/bin - ginstall -g bin -d $(DESTDIR)/opt/csw/lib/ruby $(DESTDIR)/opt/csw/lib/ruby/site_ruby $(DESTDIR)/opt/csw/lib/ruby/site_ruby/$(RUBY_VERSION) - ginstall -g bin -d $(DESTDIR)/etc/opt/csw $(DESTDIR)/etc/opt/csw/mcollective - ginstall -g bin -d $(DESTDIR)/opt/csw/share $(DESTDIR)/opt/csw/share/mcollective - - # install binaries - ginstall -g bin $(CURDIR)/../../bin/mc-* $(DESTDIR)/opt/csw/sbin/ - ginstall -g bin $(CURDIR)/../../bin/mco $(DESTDIR)/opt/csw/sbin/ - ginstall -g bin $(CURDIR)/../../bin/mcollectived $(DESTDIR)/opt/csw/sbin/mcollectived - # install libraries - gcp -a $(CURDIR)/../../lib/* $(RUBY_SITE)/ - chgrp -R bin $(RUBY_SITE)/ - # install example config files - gcp -a $(CURDIR)/../../etc/* $(DESTDIR)/etc/opt/csw/mcollective/ - grm $(DESTDIR)/etc/opt/csw/mcollective/ssl/PLACEHOLDER - grm $(DESTDIR)/etc/opt/csw/mcollective/ssl/clients/PLACEHOLDER - chgrp -R bin $(DESTDIR)/etc/opt/csw/mcollective/ - # install plugins - gcp -a $(CURDIR)/../../plugins $(DESTDIR)/opt/csw/share/mcollective/ - # install docs - #ginstall -d $(DESTDIR)/opt/csw/doc $(DESTDIR)/opt/csw/doc/mcollective/ - #gcp -a $(CURDIR)/../../doc/ $(DESTDIR)/opt/cs/doc/mcollective - - ginstall -g bin $(CURDIR)/mcollective.init $(DESTDIR)/opt/csw/lib/svc/method/svc-cswmcollectived - ginstall -g bin $(CURDIR)/cswmcollectived.xml $(DESTDIR)/var/opt/csw/svc/manifest/network - - (cat prototype.head; pkgproto $(DESTDIR)=/ ) > solaris/prototype - mkdir $(PKGDIR) || true - ginstall postinstall solaris/ - ginstall postremove solaris/ - ginstall preremove solaris/ - - ginstall pkginfo solaris/ - (echo PKG=${PKGNAME} ) >> solaris/pkginfo - (echo VERSION=${PKGVERSION} ) >> solaris/pkginfo - (cd solaris/ ; pkgmk -o -d $(PKGDIR)) - pkgtrans -s $(PKGDIR) $(CURDIR)/$(PKGNAME)-$(PKGVERSION)-`uname -s``uname -r`-all-CSW.pkg $(PKGNAME) - -clean: - grm -rf $(DESTDIR) - grm -rf $(PKGDIR) - grm -f solaris/prototype - grm -f $(PKGNAME)-$(SOLARIS_VERSION)-`uname -s``uname -r`-all-CSW.pkg diff --git a/ext/solaris/cswmcollectived.xml b/ext/solaris/cswmcollectived.xml deleted file mode 100644 index 8f0b5c83..00000000 --- a/ext/solaris/cswmcollectived.xml +++ /dev/null @@ -1,100 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/ext/solaris/depend b/ext/solaris/depend deleted file mode 100644 index 6abc6432..00000000 --- a/ext/solaris/depend +++ /dev/null @@ -1,3 +0,0 @@ -P CWSruby ruby -P CWSrubystomp rubystomp -P CSWrubygems rubygems diff --git a/ext/solaris/mcollective.init b/ext/solaris/mcollective.init deleted file mode 100755 index 659a6560..00000000 --- a/ext/solaris/mcollective.init +++ /dev/null @@ -1,96 +0,0 @@ -#!/bin/sh -# -# mcollective Application Server for STOMP based agents -# -# description: mcollective lets you build powerful Stomp compatible middleware clients in ruby without having to worry too -# much about all the setup and management of a Stomp connection, it also provides stats, logging and so forth -# as a bonus. -# - -RUBYLIB=/opt/csw/lib/ruby/site_ruby/1.8:$RUBYLIB -export RUBYLIB - -mcollectived="/opt/csw/sbin/mcollectived" - -lock="/var/lock/mcollective" - -# PID directory -pidfile="/var/run/mcollectived.pid" - -# Check that binary exists -if [ ! -f $mcollectived ] -then - echo "mcollectived binary not found" - exit 1 -fi - -# See how we were called. -case "$1" in - start) - if [ -f ${lock} ]; then - # we were not shut down correctly - if [ -s ${pidfile} ]; then - kill `cat ${pidfile}` >/dev/null 2>&1 - fi - rm -f ${pidfile} - - rm -f ${lock} - sleep 2 - fi - - rm -f ${pidfile} - - ${mcollectived} --pid=${pidfile} --config="/etc/mcollective/server.cfg" --daemonize - if [ $? = 0 ]; then - touch $lock - exit 0 - else - exit 1 - fi - ;; - stop) - if [ -s ${pidfile} ]; then - kill `cat ${pidfile}` >/dev/null 2>&1 - fi - rm -f ${pidfile} - - rm -f $lock - ;; - restart) - $0 stop - sleep 2 - $0 start - ;; - condrestart) - if [ -f $lock ]; then - $0 stop - # avoid race - sleep 2 - $0 start - fi - ;; - status) - if [ -f ${lock} ]; then - if [ -s ${pidfile} ]; then - if [ -d /proc/`cat ${pidfile}` ]; then - echo "mcollectived (`cat ${pidfile}`) is running" - exit 0 - else - echo "mcollectived (`cat ${pidfile}`) is NOT running" - exit 1 - fi - fi - else - echo "mcollectived: service not started" - exit 1 - fi - ;; - force-reload) - echo "not implemented" - ;; - *) - echo "Usage: $0 {start|stop|restart|condrestart|status}" - exit 1 - ;; -esac -exit 0 diff --git a/ext/solaris/pkginfo b/ext/solaris/pkginfo deleted file mode 100644 index dda08648..00000000 --- a/ext/solaris/pkginfo +++ /dev/null @@ -1,6 +0,0 @@ -NAME=mcollective -CATEGORY=network -DESC=The Marionette Collective aka. mcollective is a framework to build server orchestration or parallel job execution systems. -ARCH=all -EMAIL=rudy.gevaert@ugent.be -VENDOR=Puppetlabs diff --git a/ext/solaris/postinstall b/ext/solaris/postinstall deleted file mode 100644 index 1f2d0934..00000000 --- a/ext/solaris/postinstall +++ /dev/null @@ -1,6 +0,0 @@ -#! /bin/bash - -PKG_INSTALL_ROOT=${PKG_INSTALL_ROOT:-/} - -/usr/bin/test -d $PKG_INSTALL_ROOT/etc/mcollective || /usr/sbin/chroot $PKG_INSTALL_ROOT /usr/bin/ln -s /etc/opt/csw/mcollective /etc/mcollective -/usr/sbin/chroot $PKG_INSTALL_ROOT /usr/sbin/svccfg import /var/opt/csw/svc/manifest/network/cswmcollectived.xml || /bin/true diff --git a/ext/solaris/postremove b/ext/solaris/postremove deleted file mode 100644 index 4b656732..00000000 --- a/ext/solaris/postremove +++ /dev/null @@ -1,3 +0,0 @@ -/usr/sbin/svcadm disable svc:network/cswmcollectived 2>/dev/null || /bin/true -/usr/sbin/svccfg delete svc:network/cswmcollectived 2>/dev/null || /bin/true -rm /etc/mcollective || /bin/true diff --git a/ext/solaris/preremove b/ext/solaris/preremove deleted file mode 100644 index 170584d6..00000000 --- a/ext/solaris/preremove +++ /dev/null @@ -1,2 +0,0 @@ -/usr/sbin/svcadm disable svc:network/cswmcollectived 2>/dev/null || /bin/true -/usr/sbin/svccfg delete svc:network/cswmcollectived 2>/dev/null || /bin/true diff --git a/ext/solaris/prototype.head b/ext/solaris/prototype.head deleted file mode 100644 index db902893..00000000 --- a/ext/solaris/prototype.head +++ /dev/null @@ -1,4 +0,0 @@ -i pkginfo -i postinstall -i preremove -i postremove diff --git a/ext/solaris/setversion b/ext/solaris/setversion deleted file mode 100755 index 8829f0ef..00000000 --- a/ext/solaris/setversion +++ /dev/null @@ -1,9 +0,0 @@ -#! /bin/bash - -IN=$1 -OUT=$2 - -SOLARIS_VERSION=`ggrep ^VERSION solaris/pkginfo | cut -d = -f2` - -sed 's/VERSION="none"/VERSION="'"$SOLARIS_VERSION"'"/' $IN > $OUT -chmod 755 $OUT diff --git a/ext/solaris11/Makefile b/ext/solaris11/Makefile deleted file mode 100644 index a1e6f0ec..00000000 --- a/ext/solaris11/Makefile +++ /dev/null @@ -1,46 +0,0 @@ -#!/usr/bin/make -f - -DESTDIR= - -build: - -clean: - -install: install-bin install-lib install-conf install-plugins # install-doc - -install-bin: - install -d $(DESTDIR)/usr/sbin - install -d $(DESTDIR)/usr/bin - cp bin/mc-* $(DESTDIR)/usr/sbin - cp bin/mco $(DESTDIR)/usr/bin - cp bin/mcollectived $(DESTDIR)/usr/sbin/mcollectived - cp COPYING $(DESTDIR)/ - -install-lib: - install -d $(DESTDIR)/usr/ruby/1.8/lib/ruby/site_ruby/1.8/ - cp -rp lib/* $(DESTDIR)/usr/ruby/1.8/lib/ruby/site_ruby/1.8/ - -install-conf: - install -d $(DESTDIR)/etc/mcollective/ - install -d $(DESTDIR)/etc/init.d - cp -r etc/* $(DESTDIR)/etc/mcollective/ - cp mcollective.init $(DESTDIR)/etc/init.d/mcollective - rm $(DESTDIR)/etc/mcollective/ssl/PLACEHOLDER - rm $(DESTDIR)/etc/mcollective/ssl/clients/PLACEHOLDER - -install-plugins: - install -d $(DESTDIR)/usr/share/mcollective/ - cp -rp plugins $(DESTDIR)/usr/share/mcollective/ - -install-doc: - install -d $(DESTDIR)/usr/share/doc/ - cp -rp doc $(DESTDIR)/usr/share/doc/mcollective - -uninstall: - rm -f $(DESTDIR)/usr/sbin/mcollectived - rm -rf $(DESTDIR)/usr/ruby/1.8/lib/ruby/site_ruby/1.8/mcollective* - rm -rf $(DESTDIR)/usr/share/mcollective - rm -rf $(DESTDIR)/etc/mcollective - -.PHONY: build clean install uninstall - diff --git a/ext/solaris11/README.md b/ext/solaris11/README.md deleted file mode 100644 index 8ef1251e..00000000 --- a/ext/solaris11/README.md +++ /dev/null @@ -1,103 +0,0 @@ -Requirements ------------- - -- Ruby (pkg install pkg:/runtime/ruby-18) -- Header files (pkg install system/header) -- GCC to install JSON (pkg install developer/gcc-3) -- Stomp: gem install stomp -- Ruby-JSON: gem install json - -Installation ------------- - -Clone the github repository and install as root: - - $ cd marionette-collective - $ make -f ext/solaris11/Makefile install - -This will use / as a default destination root directory. - -IPS package ------------ - -To create an IPS package, follow the excellent guide at: -http://www.neuhalfen.name/2011/07/02/Solaris11-Packaging-IPS_simple_packages/ - -To create a basic IPS repository (and start the associated services): - - # zfs create rpool/IPS - # zfs set atime=off rpool/IPS - # zfs set mountpoint=/IPS rpool/IPS - # mkdir /IPS/Solaris11 - # svcadm enable application/pkg/server - # svccfg -s application/pkg/server setprop pkg/inst_root=/IPS/Solaris11 - # svccfg -s application/pkg/server setprop pkg/readonly=false - # pkgrepo create /IPS/Solaris11/ - # pkgrepo set -s /IPS/Solaris11 publisher/prefix=legrand.im - # pkgrepo -s /IPS/Solaris11 refresh - # svcadm refresh application/pkg/server - # svcadm enable application/pkg/server - # pkg set-publisher -O http://localhost:80 legrand.im - -To create and send the package itself, from the guide above: - - # mkdir ~/package - # cd /marionette-collective - # cat Makefile | sed 's/DESTDIR=$/DESTDIR=~\/package/' > Makefile.package - # make -f ext/solaris11/Makefile.package install - # pkg install pkg:/file/gnu-findutils - # export ROOT=/ - # export description="MCollective" - # export user="root" - # export group="root" - # cd ~/package - # cat > ../send.sh << "EOF" - #!/bin/sh - export PKGSEND="pkgsend -s http://localhost:80" - eval `$PKGSEND open mcollective@1.1-1` - $PKGSEND add license ./COPYING license=lorem_ipsum - $PKGSEND add set name=description value="${description}" - EOF - # gfind . -type d -not -name . -printf "\$PKGSEND add dir mode=%m owner=${user} group=${group} path=$ROOT/%h/%f \n" >> ../send.sh - # gfind . -type f -not -name LICENSE -printf "\$PKGSEND add file %h/%f mode=%m owner=${user} group=${group} path=$ROOT/%h/%f \n" >> ../send.sh - # gfind . -type l -not -name LICENSE -printf "\$PKGSEND add link path=%h/%f target=%l \n" >> ../send.sh - # echo '$PKGSEND close' >> ../send.sh - # sh -x ../send.sh - -The package can then be installed with: - - # pkg install pkg://legrand.im/mcollective - -Configuration -------------- - -There is no packaged configuration; you can use the following example: - - # cat > /etc/mcollective/client.cfg << "EOF" - main_collective = mcollective - collectives = mcollective - libdir = /usr/share/mcollective/plugins - logfile = /dev/null - loglevel = info - # Plugins - securityprovider = psk - plugin.psk = unset - connector = stomp - plugin.stomp.host = mqserver - plugin.stomp.port = 6163 - plugin.stomp.user = mcollective - plugin.stomp.password = changeme - # Facts - factsource = yaml - plugin.yaml = /etc/mcollective/facts.yaml - EOF - -License ------- - -http://creativecommons.org/publicdomain/zero/1.0/ - -To the extent possible under law, Mathieu Legrand has waived all copyright and related or -neighboring rights to this work. This work is published from: Singapore. - - diff --git a/ext/stompclient b/ext/stompclient deleted file mode 100755 index a2b4d92c..00000000 --- a/ext/stompclient +++ /dev/null @@ -1,156 +0,0 @@ -#!/usr/bin/env ruby -# == Synopsis -# -# stompclient: Generic client to consume and produce STOMP queues and topics, tested against -# Apache Active MQ -# -# == Description -# A simple client that can connect to an STOMP server, subscribe to topics and queues and also -# send to topics and queues. -# -# == Usage -# stompclient [OPTIONS] -# -# --help, -h: -# Show Help -# -# --server, -s -# The server to connect to, can also be set in STOMP_SERVER environment variable -# -# --port, -p -# The port to connect to, default to 6163 -# -# --user, -u -# The user to connect as, can also be set in STOMP_USER environment variable -# -# --password, -P -# The password to use, can also be set in STOMP_PASSWORD environment variable -# -# When connected to a server, use the 'help' command to see further information about -# using the client, common commands that can be issued are: -# -# - subscribe /topic/foo: Subscribes to topic 'foo' -# - /topic/foo bar: Sends 'bar' to the topic 'foo' -# - details: Toggle the display or timestamp and topic or queue information for each message -# -# -# == Changelog -# - 20 December 2009 Include into MCollective -# - 17 March 2009 Initial release -# -# R.I.Pienaar more information at www.devco.net -# -# Licensed under the Apache License, Version 2.0 - -require 'rubygems' -require 'stomp' -require 'readline' -require 'thread' -require 'getoptlong' - -opts = GetoptLong.new( - [ '--server', '-s', GetoptLong::REQUIRED_ARGUMENT], - [ '--port', '-p', GetoptLong::REQUIRED_ARGUMENT], - [ '--user', '-u', GetoptLong::REQUIRED_ARGUMENT], - [ '--password', '-P', GetoptLong::REQUIRED_ARGUMENT], - [ '--help', '-h', GetoptLong::NO_ARGUMENT] -) - -@user = ENV["STOMP_USER"]; -@password = ENV["STOMP_PASSWORD"] -@server = ENV["STOMP_SERVER"] -@port = ENV["STOMP_PORT"] || 6163 - -opts.each { |opt, arg| - case opt - when '--help' - begin - require 'rdoc/ri/ri_paths' - require 'rdoc/usage' - RDoc::usage - exit - rescue Exception => e - puts("Install RDoc::usage or view the comments in the top of the script to get detailed help") if e.to_str != "exit" - end - - exit - when '--server' - @server = arg - when '--port' - @port = arg - when '--user' - @user = arg - when '--password' - @password = arg - end -} - -@conn = Stomp::Connection.open(@user, @password, @server, @port, true) - -STDOUT.sync = true - -def showhelp - puts("List of commands:") - puts("\n\t- subscribe /(topic|queue)/foo subscribes to topic of queue 'foo'") - puts("\t- /(topic|queue|/foo bar sends msg 'bar' to topic of queue 'foo'") - puts("\t- quit|exit|q|^d exit") - puts("\t- detail show/dont show time and topic a msg was received on") - puts("\t- help show this help") -end - -@showdetails = true - -Thread.new(@conn) do |amq| - loop do - msg = amq.receive - dest = msg.headers["destination"] - time = Time.now.strftime('%H:%M:%S') - - if @showdetails - msg = "\r#{time}:#{dest} > #{msg.body.chomp}\n" - else - msg = "\r#{msg.body.chomp}\n" - end - - puts (msg) - end -end - -loop do - line = Readline::readline('AMQ> ') - if line - Readline::HISTORY.push(line) if line != "" - else - exit - end - - if (line =~ /^(\/(topic|queue)\/\S+)\s+(.+)$/) - puts("Sending '#{$3}' to #{$1}") - - if @conn.respond_to?("publish") - @conn.publish($1, $3) - else - @conn.send($1, $3) - end - - elsif (line =~ /^sub\S* (\/(topic|queue)\/\S+)$/) - puts("Subscribing to #{$1}") - - @conn.subscribe($1) - elsif (line =~ /^det(ail)*$/) - if @showdetails - @showdetails = false - puts("No longer showing details") - else - @showdetails = true - puts("Showing time and topic for each msg") - end - elsif (line =~ /^(quit|exit|q)$/) - exit - elsif (line =~ /^(help|h|\?)$/) - showhelp - elsif (line =~ /^$/) - else - puts("ERROR: unrecognised input: #{line}") - end -end diff --git a/ext/vim/_.snippets b/ext/vim/_.snippets deleted file mode 100644 index 8ba2f3ec..00000000 --- a/ext/vim/_.snippets +++ /dev/null @@ -1,10 +0,0 @@ -snippet mcagent - module MCollective - module Agent - class ${1:Agentname} for additions and feedback, -snippet meta - metadata :name => "${1:`Filename('', 'name')`}", - :description => "${2:description}", - :author => "${3:`g:snips_author`}", - :license => "${4:license}", - :version => "${5:version}", - :url => "${6:homepage}", - :timeout => ${7:run timeout} - - ${8} -snippet discovery - discovery do - capabilities ${1:capability list} - end -snippet dataquery - dataquery :description => "${1:data query description}" do - ${2} - end -snippet action - action "${1:action name}", :description => "${2:action description}" do - ${3} - end -snippet input String - input :${1:input name}, - :prompt => "${2:prompt when asking for information}", - :description => "${3:description of the input}", - :type => :string, - :validation => '${4:^.+$}', - :optional => ${5:false}, - :maxlength => ${6:20} - - ${7} -snippet input List - input :${1:input name}, - :prompt => "${2:prompt when asking for information}", - :description => "${3:description of the input}", - :type => :list, - :optional => ${4:false}, - :list => [${5:list members}] - - ${6} -snippet input Numeric - input :${1:input name}, - :prompt => "${2:prompt when asking for information}", - :description => "${3:description of the input}", - :type => :number, - :optional => ${4:false} - - ${5} -snippet input Boolean - input :${1:input name}, - :prompt => "${2:prompt when asking for information}", - :description => "${3:description of the input}", - :type => :boolean, - :optional => ${4:false} - - ${5} -snippet output - output ${1:output name}, - :description => "${2:description of this output data}", - :display_as => "${3:what do display}", - :default => ${4:nil} - - ${5} -snippet display Always - display :always - - -snippet display Only OK results - display :ok - - -snippet display Only failed results - display :failed - - diff --git a/ext/windows/README.md b/ext/windows/README.md deleted file mode 100644 index 75b0c640..00000000 --- a/ext/windows/README.md +++ /dev/null @@ -1,33 +0,0 @@ -These files support installing and using MCollective on MS Windows. - -Here are a few instructions for people who wish to do early adopter testing. At some point we hope to have this packaged into an MSI installer, but your early feedback will help. This guide doesn't include installing a message broker and the process may or may not work as described. Some additional troubleshooting/experimentation will probably be necessary. - -Assuming you are installing MCollective into `C:\marionette-collective`: - - * Install Ruby 1.9.3 from - * check the boxes for "Add Ruby executables to your PATH" and "Associate .rb and .rbw files with the Ruby installation" - * Run the following commands to install the required gems: - * `gem install --no-rdoc --no-ri stomp win32-service sys-admin windows-api` - * `gem install --no-rdoc --no-ri win32-dir -v 0.3.7` - * `gem install --no-rdoc --no-ri win32-process -v 0.5.5` - * Extract the zip file or clone the git repository into `C:\marionette-collective` - * Copy the files from `C:\marionette-collective\ext\windows\` into `C:\marionette-collective\bin` - * Install any plugins and their dependencies into `C:\marionette-collective\plugins` - specifically for the package and service agents. You can install Puppet via gems. - * Edit the configuration files in `C:\marionette-collective\etc\`: - * Rename `server.cfg.dist` to `server.cfg` and change the following settings: - * `libdir = C:\marionette-collective\plugins` - * `logfile = C:\marionette-collective\mcollective.log` - * `plugin.yaml = C:\marionette-collective\etc\facts.yaml` - * Rename `client.cfg.dist` to `client.cfg` and rename `facts.yaml.dist` to `facts.yaml` - * Register and start the service - * Enter the `C:\marionette-collective\bin\` directory and run `register_service.bat` - * Right click on "My Computer," select "Manage" - * Under "Services and Applications," expand "Services" - * Find "The Marionette Collective" and start the service - -If it does not run: - - * Look in the log files. Edit `server.cfg` to set `loglevel` to `debug`. - * If the log files are empty, look at the command the service wrapper runs and run it by hand. This will show you any early exceptions preventing it from running. It wont succesfully start, but you should see why it does not get far enough to start writing logs. - diff --git a/ext/windows/daemon.bat b/ext/windows/daemon.bat deleted file mode 100644 index f9d050f5..00000000 --- a/ext/windows/daemon.bat +++ /dev/null @@ -1,6 +0,0 @@ -@echo off -SETLOCAL - -call "%~dp0..\bin\environment.bat" %0 %* - -rubyw -rubygems "%MCOLLECTIVE_DIR%\bin\mcollectived" %* diff --git a/ext/windows/environment.bat b/ext/windows/environment.bat deleted file mode 100644 index a0a372eb..00000000 --- a/ext/windows/environment.bat +++ /dev/null @@ -1,16 +0,0 @@ -SET BASEDIR=%~dp0.. -SET BASEDIR=%BASEDIR:\bin\..=% - -SET SERVER_CONFIG=%BASEDIR%\etc\server.cfg -SET CLIENT_CONFIG=%BASEDIR%\etc\client.cfg - -SET MCOLLECTIVED=%BASEDIR%\bin\mcollectived -SET MC_STARTTYPE=manual -REM SET MC_STARTTYPE=auto - -SET PATH=%BASEDIR%\bin;%PATH% - -SET RUBYLIB=%BASEDIR%\lib;%RUBYLIB% -SET RUBYLIB=%RUBYLIB:\=/% - -SET RUBY="ruby" diff --git a/ext/windows/mco.bat b/ext/windows/mco.bat deleted file mode 100644 index 100d6939..00000000 --- a/ext/windows/mco.bat +++ /dev/null @@ -1,9 +0,0 @@ -@echo off -SETLOCAL -call "%~dp0environment.bat" %0 %* - -if [%CLIENT_CONFIG%] == [] ( - ruby.exe -S -- mco %* -) else ( - ruby.exe -S -- mco %* --config "%CLIENT_CONFIG%" -) diff --git a/ext/windows/register_service.bat b/ext/windows/register_service.bat deleted file mode 100644 index b5b15d64..00000000 --- a/ext/windows/register_service.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off - -SETLOCAL - -call "%~dp0environment.bat" %0 %* - -%RUBY% -S -- service_manager.rb --install diff --git a/ext/windows/service_manager.rb b/ext/windows/service_manager.rb deleted file mode 100644 index 1a6d947f..00000000 --- a/ext/windows/service_manager.rb +++ /dev/null @@ -1,101 +0,0 @@ -require 'optparse' - -opt = OptionParser.new - -ruby_path = ENV["RUBY"].gsub('"','') -libdir = ENV["RUBYLIB"] -mcollectived = ENV["MCOLLECTIVED"] -configfile = ENV["SERVER_CONFIG"] - -# Find ruby in $PATH -# @return [String, nil] -def find_ruby_in_path - ENV["PATH"].split(File::PATH_SEPARATOR).each do |path| - ruby = File.join(path, "rubyw.exe") - - if File.exist?(ruby) - return ruby - end - end - return nil -end - -unless File.exist?(ruby_path) - ruby_path = find_ruby_in_path - - unless ruby_path - abort("Can't find ruby.exe in the path") - end -end - -options = {:name => "mcollectived", - :display_name => "The Marionette Collective", - :description => "Puppet Labs server orchestration framework", - :command => '%s -I"%s" -- "%s" --config "%s" --daemonize' % [ ruby_path, libdir, mcollectived, configfile ]} - -action = false - -opt.on("--install", "Install service") do - action = :install -end - -opt.on("--uninstall", "Remove service") do - action = :uninstall -end - -opt.on("--name NAME", String, "Service name (#{options[:name]})") do |n| - options[:name] = n -end - -opt.on("--description DESCRIPTION", String, "Service description (#{options[:description]})") do |v| - options[:description] = v -end - -opt.on("--display NAME", String, "Service display name (#{options[:display_name]})") do |n| - options[:display_name] = n -end - -opt.on("--command COMMAND", String, "Service command (#{options[:command]})") do |c| - options[:command] = c -end - -opt.parse! - -abort "Please choose an action with --install or --uninstall" unless action - -require 'rubygems' -require 'win32/service' - -include Win32 - -case action - when :install - if ENV["MC_STARTTYPE"] =~ /auto/i - start_type = Service::AUTO_START - else - start_type = Service::DEMAND_START - end - - Service.new( - :service_name => options[:name], - :display_name => options[:display_name], - :description => options[:description], - :binary_path_name => options[:command], - :service_type => Service::SERVICE_WIN32_OWN_PROCESS, - :start_type => start_type - ) - - puts "Service %s installed" % [options[:name]] - - when :uninstall - Service.stop(options[:name]) unless Service.status(options[:name]).current_state == 'stopped' - - while Service.status(options[:name]).current_state != 'stopped' - puts "Waiting for service %s to stop" % [options[:name]] - sleep 1 - end - - Service.delete(options[:name]) - - puts "Service %s removed" % [options[:name]] -end diff --git a/ext/windows/unregister_service.bat b/ext/windows/unregister_service.bat deleted file mode 100644 index cf78ad7b..00000000 --- a/ext/windows/unregister_service.bat +++ /dev/null @@ -1,7 +0,0 @@ -@echo off - -SETLOCAL - -call "%~dp0environment.bat" %0 %* - -%RUBY% -S -- service_manager.rb --uninstall diff --git a/ext/zsh/_mco b/ext/zsh/_mco deleted file mode 100644 index 2b2fb0a0..00000000 --- a/ext/zsh/_mco +++ /dev/null @@ -1,94 +0,0 @@ -#compdef mco - -# completion for the mcollective cli. -# -# for the main mco application it will complete -# the list of available applications -# -# for the rpc application it will complete first -# the list of agents, then actions and then each -# input. -# -# For all other applications it will just complete -# the common command line options, to add another -# application simply define a function called -# _mco_application_foo() for the foo application - -_mco() { - if (( CURRENT > 2 )); then - local application=${words[2]} - - shift words - - args=({-W,--with}'[Combined class and fact filter]' \ - {-S,--select}'[Select filter]' \ - {-F,--wf,--with-fact}'[Fact filter]' \ - {-C,--wc,--with-class}'[Class filter]' \ - {-A,--wa,--with-agent}'[Agent filter]' \ - {-I,--wi,--with-identity}'[Identity filter]' \ - {-T,--target}'[Target collective]' \ - {--dm,--disc-method}'[Which discovery method to use]' \ - {--do,--disc-option}'[Options to pass to the discovery method]' \ - {--dt,--discovery-timeout}'[Discovery timeout]' \ - {-t,--timeout}'[Command Timeout]' \ - {-q,--quiet}'[Surpress verbose output]' \ - {-c,--config}'[Path to the config file]' \ - {-v,--verbose}'[Be verbose]' \ - {-h,--help}'[Show complete help message]' \ - '--nodes[List of nodes to address]' \ - '--ttl[Time To Live for the request]' \ - '--reply-to[Custom reply target]') - - curcontext="${curcontext%:*:*}:mco-${application}" - - if (( $+functions[_mco_application_$application] > 0 ));then - _mco_application_$application - fi - - _arguments -s : $args - else - local -a cmdlist - _call_program mco-list-applications mco completion --list-applications -v | while read -A hline; do - cmdlist=($cmdlist "${hline}") - done - - curcontext="${curcontext%:*:*}:mco-applications" - - _describe -t mco-application 'MCollective applications' cmdlist - fi -} - -_mco_application_rpc() { - local -a clist - - if (( CURRENT == 3 )); then - _call_program mco-list-agents mco completion --list-agents -v | while read -A hline; do - clist=($clist "${hline}") - done - - _describe -t mco-agents "MCollective agents" clist - elif (( CURRENT == 4 )); then - _call_program mco-list-actions mco completion --list-actions --agent=${words[2]} -v | while read -A hline; do - clist=($clist "${hline}") - done - - _describe -t mco-actions "${words[2]} actions" clist - - elif (( CURRENT > 4 )); then - _call_program mco-list-inputs mco completion --list-inputs --action=${words[3]} --agent=${words[2]} -v | while read hline; do - clist=($clist $hline) - done - - _describe -t mco-inputs "${words[3]} inputs" clist -S = - fi - - args+=( - {--np,--no-progress}'[Do not show the progress bar]' \ - {--nr,--no-results}'[Do not process results, just send request]' \ - {-1,--one}'[Send request to only one discovered node]' \ - '--batch[Do request in batches]' \ - '--batch-sleep[Sleep time between batches]' \ - {--ln,--limit-nodes}'[Only send the request to a certain number of discovered nodes]' \ - {-j,--json}'[Output result as JSON data]' - ) -} diff --git a/install.rb b/install.rb deleted file mode 100755 index 902d5e8b..00000000 --- a/install.rb +++ /dev/null @@ -1,326 +0,0 @@ -#! /usr/bin/env ruby -#-- -# Copyright 2004 Austin Ziegler -# Install utility. Based on the original installation script for rdoc by the -# Pragmatic Programmers. -# -# This program is free software. It may be redistributed and/or modified under -# the terms of the GPL version 2 (or later) or the Ruby licence. -# -# Usage -# ----- -# In most cases, if you have a typical project layout, you will need to do -# absolutely nothing to make this work for you. This layout is: -# -# bin/ # executable files -- "commands" -# lib/ # the source of the library -# -# The default behaviour: -# 1) Build Rdoc documentation from all files in bin/ (excluding .bat and .cmd), -# all .rb files in lib/, ./README, ./ChangeLog, and ./Install. -# and all .rb files in lib/. -# 2) Install configuration files in etc/. -# 3) Install commands from bin/ into the Ruby bin directory. -# 4) Install system commands from bin/ into the Ruby sbin directory. -# 5) Install all library files from lib/ into Ruby's site_lib/version -# directory. -# 6) Install all plugins from plugins/ into the plugins directory -# (usually $libexecdir/mcollective). -# -#++ - -require 'rbconfig' -require 'find' -require 'fileutils' -require 'tempfile' -require 'optparse' -require 'ostruct' -include FileUtils - -begin - require 'rdoc/rdoc' - $haverdoc = true -rescue LoadError - puts "Missing rdoc; skipping documentation" - $haverdoc = false -end - -if (defined?(RbConfig) ? RbConfig : Config)::CONFIG['host_os'] =~ /mswin|win32|dos|mingw|cygwin/i - WINDOWS = TRUE -else - WINDOWS = FALSE -end - -PREREQS = %w{} - -InstallOptions = OpenStruct.new - -def glob(list) - g = list.map { |i| Dir.glob(i) } - g.flatten! - g.compact! - g.uniq! - g -end - -def check_prereqs - PREREQS.each do |pre| - begin - require pre - rescue LoadError - puts "Could not load #{pre} Ruby library; cannot install" - exit(-1) - end - end -end - -def do_configs(configs, target, strip = 'etc/') - Dir.mkdir(target) unless File.directory? target - configs.each do |cf| - ocf = File.join(target, cf.gsub(Regexp.new(strip), '')) - oc = File.dirname(ocf) - makedirs(oc, {:mode => 0755, :verbose => true}) - install(cf, ocf, {:mode => 0644, :preserve => true, :verbose => true}) - end -end - -def do_bins(bins, target, strip = 's?bin/') - Dir.mkdir(target) unless File.directory? target - bins.each do |bf| - obf = bf.gsub(/#{strip}/, '') - install_binfile(bf, obf, target) - end -end - -def do_libs(libs, target, strip = 'lib/') - libs.each do |lf| - olf = File.join(target, lf.sub(/^#{strip}/, '')) - op = File.dirname(olf) - if File.directory?(lf) - makedirs(olf, {:mode => 0755, :verbose => true}) - else - makedirs(op, {:mode => 0755, :verbose => true}) - install(lf, olf, {:mode => 0644, :preserve => true, :verbose => true}) - end - end -end - -## -# Prepare the file installation. -# -def prepare_installation - InstallOptions.configs = true - InstallOptions.batch_files = true - # Only try to do docs if we're sure they have rdoc - if $haverdoc - InstallOptions.rdoc = true - else - InstallOptions.rdoc = false - end - - - ARGV.options do |opts| - opts.banner = "Usage: #{File.basename($0)} [options]" - opts.separator "" - opts.on('--[no-]rdoc', 'Creation of RDoc output.', 'Default is create rdoc.') do |onrdoc| - InstallOptions.rdoc = onrdoc - end - opts.on('--[no-]configs', 'Installation of config files', 'Default is install configs.') do |onconfigs| - InstallOptions.configs = onconfigs - end - opts.on('--destdir[=OPTIONAL]', 'Installation prefix for all targets', 'Default essentially /') do |destdir| - InstallOptions.destdir = destdir - end - opts.on('--configdir[=OPTIONAL]', 'Installation directory for config files', 'Default /etc/mcollective') do |configdir| - InstallOptions.configdir = configdir - end - opts.on('--bindir[=OPTIONAL]', 'Installation directory for binaries', 'overrides RbConfig::CONFIG["bindir"]') do |bindir| - InstallOptions.bindir = bindir - end - opts.on('--sbindir[=OPTIONAL]', 'Installation directory for system binaries', 'overrides RbConfig::CONFIG["sbindir"]') do |sbindir| - InstallOptions.sbindir = sbindir - end - opts.on('--ruby[=OPTIONAL]', 'Ruby interpreter to use with installation', 'overrides ruby used to call install.rb') do |ruby| - InstallOptions.ruby = ruby - end - opts.on('--sitelibdir[=OPTIONAL]', 'Installation directory for libraries', 'overrides RbConfig::CONFIG["sitelibdir"]') do |sitelibdir| - InstallOptions.sitelibdir = sitelibdir - end - opts.on('--plugindir[=OPTIONAL]', 'Installation directory for plugins', 'Default /usr/libexec/mcollective') do |plugindir| - InstallOptions.plugindir = plugindir - end - opts.on('--no-batch-files', 'Prevents installation of batch files for windows', 'Default off') do |batch_files| - InstallOptions.batch_files = false - end - opts.on('--quick', 'Performs a quick installation. Only the', 'installation is done.') do |quick| - InstallOptions.rdoc = false - InstallOptions.ri = false - InstallOptions.configs = true - end - opts.on('--full', 'Performs a full installation. All', 'optional installation steps are run.') do |full| - InstallOptions.rdoc = true - InstallOptions.ri = true - InstallOptions.configs = true - end - if WINDOWS - InstallOptions.service_files = true - opts.on('--[no-]service-files', 'Installation of windows service files', 'Default is to install the windows service files') do |service_files| - InstallOptions.service_files = service_files - end - end - opts.separator("") - opts.on_tail('--help', "Shows this help text.") do - $stderr.puts opts - exit - end - - opts.parse! - end - - version = [RbConfig::CONFIG["MAJOR"], RbConfig::CONFIG["MINOR"]].join(".") - libdir = File.join(RbConfig::CONFIG["libdir"], "ruby", version) - - # Mac OS X 10.5 and higher declare bindir - # /System/Library/Frameworks/Ruby.framework/Versions/1.8/usr/bin - # which is not generally where people expect executables to be installed - # These settings are appropriate defaults for all OS X versions. - if RUBY_PLATFORM =~ /^universal-darwin[\d\.]+$/ - RbConfig::CONFIG['bindir'] = "/usr/bin" - RbConfig::CONFIG['sbindir'] = "/usr/sbin" - end - - if InstallOptions.configdir - configdir = InstallOptions.configdir - else - configdir = "/etc/mcollective" - end - - if InstallOptions.bindir - bindir = InstallOptions.bindir - else - bindir = RbConfig::CONFIG['bindir'] - end - - if InstallOptions.sbindir - sbindir = InstallOptions.sbindir - else - sbindir = RbConfig::CONFIG['sbindir'] - end - - if InstallOptions.sitelibdir - sitelibdir = InstallOptions.sitelibdir - else - sitelibdir = RbConfig::CONFIG["sitelibdir"] - if sitelibdir.nil? - sitelibdir = $LOAD_PATH.find { |x| x =~ /site_ruby/ } - if sitelibdir.nil? - sitelibdir = File.join(libdir, "site_ruby") - elsif sitelibdir !~ Regexp.quote(version) - sitelibdir = File.join(sitelibdir, version) - end - end - end - - if InstallOptions.plugindir - plugindir = InstallOptions.plugindir - else - plugindir = "/usr/libexec/mcollective" - end - - if InstallOptions.destdir - destdir = InstallOptions.destdir - else - destdir = '' - end - - unless destdir.empty? - configdir = File.join(destdir, configdir) - bindir = File.join(destdir, bindir) - sbindir = File.join(destdir, sbindir) - sitelibdir = File.join(destdir, sitelibdir) - plugindir = File.join(destdir, plugindir) - end - - makedirs(configdir) if InstallOptions.configs - makedirs(bindir) - makedirs(sbindir) - makedirs(sitelibdir) - makedirs(plugindir) - - InstallOptions.sitelibdir = sitelibdir - InstallOptions.configdir = configdir - InstallOptions.bindir = bindir - InstallOptions.sbindir = sbindir - InstallOptions.plugindir = plugindir -end - -## -# Build the rdoc documentation. -# -def build_rdoc(files) - return unless $haverdoc - begin - r = RDoc::RDoc.new - r.document(["--main", "MCollective", "--line-numbers"] + files) - rescue RDoc::RDocError => e - $stderr.puts e.message - rescue Exception => e - $stderr.puts "Couldn't build RDoc documentation\n#{e.message}" - end -end - -## -# Install file(s) from ./bin to RbConfig::CONFIG['bindir']. Patch it on the way -# to insert a #! line; on a Unix install, the command is named as expected -def install_binfile(from, op_file, target) - tmp_file = Tempfile.new('mcollective-binfile') - - if InstallOptions.ruby - ruby = InstallOptions.ruby - else - ruby = File.join(RbConfig::CONFIG['bindir'], RbConfig::CONFIG['ruby_install_name']) - end - - File.open(from) do |ip| - File.open(tmp_file.path, "w") do |op| - op.puts "#!#{ruby}" unless WINDOWS - contents = ip.readlines - contents.shift if contents[0] =~ /^#!/ - op.write contents.join - end - end - - install(tmp_file.path, File.join(target, op_file), :mode => 0755, :preserve => true, :verbose => true) - tmp_file.unlink -end - -# Change directory into the mcollective root so we don't get the wrong files for install. -cd File.dirname(__FILE__) do - prepare_installation - # Set these values to what you want installed. - configs = glob(%w{etc/*.dist}) - erbs = glob(%w{etc/*.erb}) - bins = glob(%w{bin/mco}) - sbins = glob(%w{bin/mcollectived}) - rdoc = glob(%w{bin/* lib/**/*.rb README* }) - libs = glob(%w{lib/**/*}) - plugins = glob(%w{plugins/**/*}) - - if WINDOWS && InstallOptions.batch_files - if InstallOptions.service_files - windows_bins = glob(%w{ext/windows/*.bat ext/windows/*.rb}) - else - windows_bins = glob(%w{ext/windows/mco.bat}) - end - end - - check_prereqs - build_rdoc(rdoc) if InstallOptions.rdoc - do_configs(configs, InstallOptions.configdir, 'etc/|\.dist') if InstallOptions.configs - do_configs(erbs, InstallOptions.configdir) if InstallOptions.configs - do_bins(bins, InstallOptions.bindir) - do_bins(sbins, InstallOptions.sbindir) - do_bins(windows_bins, InstallOptions.bindir, 'ext/windows/') if WINDOWS && InstallOptions.batch_files - do_libs(libs, InstallOptions.sitelibdir) - do_libs(plugins, InstallOptions.plugindir, 'plugins/') -end diff --git a/lib/mcollective.rb b/lib/mcollective.rb index 97af85cf..9f484283 100644 --- a/lib/mcollective.rb +++ b/lib/mcollective.rb @@ -1,6 +1,5 @@ require 'rubygems' require 'json' -require 'stomp' require 'timeout' require 'digest/md5' require 'optparse' @@ -15,6 +14,7 @@ require 'mcollective/monkey_patches' require 'mcollective/cache' require 'mcollective/exceptions' +require 'systemu' # == The Marionette Collective # @@ -47,7 +47,6 @@ module MCollective require "mcollective/generators" require "mcollective/pluginmanager" require "mcollective/pluginpackager" - require "mcollective/registration" require "mcollective/rpc" require "mcollective/runnerstats" require "mcollective/security" @@ -55,9 +54,6 @@ module MCollective require "mcollective/ssl" require "mcollective/util" require "mcollective/validator" - require "mcollective/vendor" - - MCollective::Vendor.load_vendored VERSION="2.12.0" diff --git a/lib/mcollective/agent/discovery.rb b/lib/mcollective/agent/discovery.rb deleted file mode 100644 index 9d59a738..00000000 --- a/lib/mcollective/agent/discovery.rb +++ /dev/null @@ -1,37 +0,0 @@ -module MCollective - module Agent - # Discovery agent for The Marionette Collective - # - # Released under the Apache License, Version 2 - class Discovery - attr_reader :timeout, :meta - - def initialize - config = Config.instance.pluginconf - - @timeout = 5 - @meta = {:license => "Apache License, Version 2", - :author => "R.I.Pienaar ", - :timeout => @timeout, - :name => "Discovery Agent", - :version => MCollective.version, - :url => "https://docs.puppetlabs.com/mcollective/", - :description => "MCollective Discovery Agent"} - end - - def handlemsg(msg, stomp) - reply = "unknown request" - - case msg[:body] - when "ping" - reply = "pong" - - else - reply = "Unknown Request: #{msg[:body]}" - end - - reply - end - end - end -end diff --git a/lib/mcollective/agent/rpcutil.ddl b/lib/mcollective/agent/rpcutil.ddl deleted file mode 100644 index 6c632ae9..00000000 --- a/lib/mcollective/agent/rpcutil.ddl +++ /dev/null @@ -1,220 +0,0 @@ -metadata :name => "rpcutil", - :description => "General helpful actions that expose stats and internals to SimpleRPC clients", - :author => "R.I.Pienaar ", - :license => "Apache License, Version 2.0", - :version => "1.0", - :url => "https://docs.puppetlabs.com/mcollective/", - :timeout => 10 - -action "collective_info", :description => "Info about the main and sub collectives" do - display :always - - output :main_collective, - :description => "The main Collective", - :display_as => "Main Collective" - - output :collectives, - :description => "All Collectives", - :display_as => "All Collectives" - - summarize do - aggregate summary(:collectives) - end -end - -action "inventory", :description => "System Inventory" do - display :always - - output :agents, - :description => "List of agent names", - :display_as => "Agents" - - output :facts, - :description => "List of facts and values", - :display_as => "Facts" - - output :classes, - :description => "List of classes on the system", - :display_as => "Classes" - - output :version, - :description => "MCollective Version", - :display_as => "Version" - - output :main_collective, - :description => "The main Collective", - :display_as => "Main Collective" - - output :collectives, - :description => "All Collectives", - :display_as => "All Collectives" - - output :data_plugins, - :description => "List of data plugin names", - :display_as => "Data Plugins" -end - -action "get_fact", :description => "Retrieve a single fact from the fact store" do - display :always - - input :fact, - :prompt => "The name of the fact", - :description => "The fact to retrieve", - :type => :string, - :validation => '^[\w\-\.]+$', - :optional => false, - :maxlength => 40 - - output :fact, - :description => "The name of the fact being returned", - :display_as => "Fact" - - output :value, - :description => "The value of the fact", - :display_as => "Value" - - summarize do - aggregate summary(:value) - end -end - -action "get_facts", :description => "Retrieve multiple facts from the fact store" do - display :always - - input :facts, - :prompt => "Comma-separated list of facts", - :description => "Facts to retrieve", - :type => :string, - :validation => '^\s*[\w\.\-]+(\s*,\s*[\w\.\-]+)*$', - :optional => false, - :maxlength => 200 - - output :values, - :description => "List of values of the facts", - :display_as => "Values" -end - -action "daemon_stats", :description => "Get statistics from the running daemon" do - display :always - - output :threads, - :description => "List of threads active in the daemon", - :display_as => "Threads" - - output :agents, - :description => "List of agents loaded", - :display_as => "Agents" - - output :pid, - :description => "Process ID of the daemon", - :display_as => "PID" - - output :times, - :description => "Processor time consumed by the daemon", - :display_as => "Times" - - output :validated, - :description => "Messages that passed security validation", - :display_as => "Security Validated" - - output :unvalidated, - :description => "Messages that failed security validation", - :display_as => "Failed Security" - - output :passed, - :description => "Passed filter checks", - :display_as => "Passed Filter" - - output :filtered, - :description => "Didn't pass filter checks", - :display_as => "Failed Filter" - - output :starttime, - :description => "Time the server started", - :display_as => "Start Time" - - output :total, - :description => "Total messages received", - :display_as => "Total Messages" - - output :replies, - :description => "Replies sent back to clients", - :display_as => "Replies" - - output :configfile, - :description => "Config file used to start the daemon", - :display_as => "Config File" - - output :version, - :description => "MCollective Version", - :display_as => "Version" - - output :ttlexpired, - :description => "Messages that did pass TTL checks", - :display_as => "TTL Expired" - - summarize do - aggregate summary(:version) - aggregate summary(:agents) - end -end - -action "agent_inventory", :description => "Inventory of all agents on the server" do - display :always - - output :agents, - :description => "List of agents on the server", - :display_as => "Agents" -end - -action "get_config_item", :description => "Get the active value of a specific config property" do - display :always - - input :item, - :prompt => "Configuration Item", - :description => "The item to retrieve from the server", - :type => :string, - :validation => '^.+$', - :optional => false, - :maxlength => 50 - - output :item, - :description => "The config property being retrieved", - :display_as => "Property" - - output :value, - :description => "The value that is in use", - :display_as => "Value" - - summarize do - aggregate summary(:value) - end -end - -action "get_data", :description => "Get data from a data plugin" do - display :always - - input :source, - :prompt => "Data Source", - :description => "The data plugin to retrieve information from", - :type => :string, - :validation => '^\w+$', - :optional => false, - :maxlength => 50 - - input :query, - :prompt => "Query", - :description => "The query argument to supply to the data plugin", - :type => :string, - :validation => '^.+$', - :optional => true, - :maxlength => 200 -end - -action "ping", :description => "Responds to requests for PING with PONG" do - display :always - - output :pong, - :description => "The local timestamp", - :display_as => "Timestamp" -end diff --git a/lib/mcollective/agent/rpcutil.rb b/lib/mcollective/agent/rpcutil.rb deleted file mode 100644 index 04222868..00000000 --- a/lib/mcollective/agent/rpcutil.rb +++ /dev/null @@ -1,108 +0,0 @@ -module MCollective - module Agent - class Rpcutil target_agent, - :license => "unknown", - :timeout => agent.timeout, - :description => "unknown", - :name => target_agent, - :url => "unknown", - :version => "unknown", - :author => "unknown"} - - agent_data.merge!(agent.meta) - - reply[:agents] << agent_data - end - end - - # Retrieves a single config property that is in effect - action "get_config_item" do - reply.fail! "Unknown config property #{request[:item]}" unless config.respond_to?(request[:item]) - - reply[:item] = request[:item] - reply[:value] = config.send(request[:item]) - end - - # Responds to PING requests with the local timestamp - action "ping" do - reply[:pong] = Time.now.to_i - end - - # Returns all configured collectives - action "collective_info" do - config = Config.instance - reply[:main_collective] = config.main_collective - reply[:collectives] = config.collectives - end - - action "get_data" do - if request[:query] - query = Data.ddl_transform_input(Data.ddl(request[:source]), request[:query].to_s) - else - query = nil - end - - data = Data[ request[:source] ].lookup(query) - - data.keys.each do |key| - reply[key] = data[key] - end - end - end - end -end diff --git a/lib/mcollective/audit/logfile.rb b/lib/mcollective/audit/logfile.rb deleted file mode 100644 index 96a715d9..00000000 --- a/lib/mcollective/audit/logfile.rb +++ /dev/null @@ -1,26 +0,0 @@ -module MCollective - module RPC - # An audit plugin that just logs to a file - # - # You can configure which file it logs to with the setting - # - # plugin.rpcaudit.logfile - - class Logfile "global_stats", :class => RunnerStats.new} Log.info("The Marionette Collective version #{MCollective::VERSION} started by #{$0} using config file #{configfile}") @@ -178,11 +176,11 @@ def set_config_defaults(configfile) @stomp = Hash.new @subscribe = Array.new @pluginconf = Hash.new - @connector = "activemq" - @securityprovider = "Psk" + @connector = "base" + @securityprovider = "Base" @factsource = "Yaml" @identity = Socket.gethostname - @registration = "Agentlist" + @registration = "Base" @registerinterval = 0 @registration_collective = nil @registration_splay = false diff --git a/lib/mcollective/connector/activemq.ddl b/lib/mcollective/connector/activemq.ddl deleted file mode 100644 index 07d4f8b6..00000000 --- a/lib/mcollective/connector/activemq.ddl +++ /dev/null @@ -1,9 +0,0 @@ -metadata :name => "ActiveMQ Connector", - :description => "Connector plugin for ActiveMQ middleware", - :author => "Puppet Labs", - :license => "ASL 2.0", - :version => "1.0.0", - :url => "https://docs.puppetlabs.com/mcollective/plugin_directory/", - :timeout => 60 - -requires :mcollective => "2.6.0" diff --git a/lib/mcollective/connector/activemq.rb b/lib/mcollective/connector/activemq.rb deleted file mode 100644 index ce18e189..00000000 --- a/lib/mcollective/connector/activemq.rb +++ /dev/null @@ -1,609 +0,0 @@ -require 'stomp' - -module MCollective - module Connector - # Handles sending and receiving messages over the Stomp protocol for ActiveMQ - # servers specifically, we take advantages of ActiveMQ specific features and - # enhancements to the Stomp protocol. For best results in a clustered environment - # use ActiveMQ 5.5.0 at least. - # - # This plugin takes an entirely different approach to dealing with ActiveMQ - # from the more generic stomp connector. - # - # - Agents use /topic/..agent - # - Replies use temp-topics so they are private and transient. - # - Point to Point messages using topics are supported by subscribing to - # /queue/.nodes with a selector "mc_identity = 'identity' - # - # The use of temp-topics for the replies is a huge improvement over the old style. - # In the old way all clients got replies for all clients that were active at that - # time, this would mean that they would need to decrypt, validate etc in order to - # determine if they need to ignore the message, this was computationally expensive - # and on large busy networks the messages were being sent all over the show cross - # broker boundaries. - # - # The new way means the messages go point2point back to only whoever requested the - # message, they only get their own replies and this is ap private channel that - # casual observers cannot just snoop into. - # - # This plugin supports 1.1.6 and newer of the Stomp rubygem. - # - # connector = activemq - # plugin.activemq.pool.size = 2 - # - # plugin.activemq.pool.1.host = stomp1.your.net - # plugin.activemq.pool.1.port = 61613 - # plugin.activemq.pool.1.user = you - # plugin.activemq.pool.1.password = secret - # plugin.activemq.pool.1.ssl = true - # plugin.activemq.pool.1.ssl.cert = /path/to/your.cert - # plugin.activemq.pool.1.ssl.key = /path/to/your.key - # plugin.activemq.pool.1.ssl.ca = /path/to/your.ca - # plugin.activemq.pool.1.ssl.fallback = true - # plugin.activemq.pool.1.ssl.ciphers = TLSv1:!MD5:!LOW:!EXPORT - # - # plugin.activemq.pool.2.host = stomp2.your.net - # plugin.activemq.pool.2.port = 61613 - # plugin.activemq.pool.2.user = you - # plugin.activemq.pool.2.password = secret - # plugin.activemq.pool.2.ssl = false - # - # Using this method you can supply just STOMP_USER and STOMP_PASSWORD. The port will - # default to 61613 if not specified. - # - # The ssl options are only usable in version of the Stomp gem newer than 1.2.2 where these - # will imply full SSL validation will be done and you'll only be able to connect to a - # ActiveMQ server that has a cert signed by the same CA. If you only set ssl = true - # and do not supply the cert, key and ca properties or if you have an older gem it - # will fall back to unverified mode only if ssl.fallback is true - # - # In addition you can set the following options for the rubygem: - # - # plugin.activemq.initial_reconnect_delay = 0.01 - # plugin.activemq.max_reconnect_delay = 30.0 - # plugin.activemq.use_exponential_back_off = true - # plugin.activemq.back_off_multiplier = 2 - # plugin.activemq.max_reconnect_attempts = 0 - # plugin.activemq.randomize = false - # plugin.activemq.timeout = -1 - # - # You can set the initial connetion timeout - this is when your stomp server is simply - # unreachable - after which it would failover to the next in the pool: - # - # plugin.activemq.connect_timeout = 30 - # - # ActiveMQ JMS message priorities can be set: - # - # plugin.activemq.priority = 4 - # - # This plugin supports Stomp protocol 1.1 when combined with the stomp gem version - # 1.2.10 or newer. To enable network heartbeats which will help keep the connection - # alive over NAT connections and aggresive session tracking firewalls you can set: - # - # plugin.activemq.heartbeat_interval = 30 - # - # which will cause a heartbeat to be sent on 30 second intervals and one to be expected - # from the broker every 30 seconds. The shortest supported period is 30 seconds, if - # you set it lower it will get forced to 30 seconds. - # - # After 2 failures to receive a heartbeat the connection will be reset via the normal - # failover mechanism. - # - # By default if heartbeat_interval is set it will request Stomp 1.1 but support fallback - # to 1.0, but you can enable strict Stomp 1.1 only operation - # - # plugin.activemq.stomp_1_0_fallback = 0 - class Activemq= params[:max_hbrlck_fails] - # we're about to force a disconnect - Log.error("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect]) - else - Log.warn("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect]) - end - else - if params[:max_hbread_fails] == 0 - # failure is disabled - Log.debug("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect]) - elsif ticker_data['read_fail_count'] >= params[:max_hbread_fails] - # we're about to force a reconnect - Log.error("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect]) - else - Log.warn("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect]) - end - end - rescue Exception => e - end - - # Stomp 1.1+ - heart beat send (transmit) failed. - def on_hbwrite_fail(params, ticker_data) - Log.error("Heartbeat write failed from '%s': %s" % [stomp_url(params), ticker_data.inspect]) - rescue Exception => e - end - - # Log heart beat fires - def on_hbfire(params, srind, curt) - case srind - when "receive_fire" - Log.debug("Received heartbeat from %s: %s, %s" % [stomp_url(params), srind, curt]) - when "send_fire" - Log.debug("Publishing heartbeat to %s: %s, %s" % [stomp_url(params), srind, curt]) - end - rescue Exception => e - end - - def stomp_url(params) - "%s://%s@%s:%d" % [ params[:cur_ssl] ? "stomp+ssl" : "stomp", params[:cur_login], params[:cur_host], params[:cur_port]] - end - end - - def initialize - @config = Config.instance - @subscriptions = [] - @msgpriority = 0 - @base64 = false - @use_exponential_back_off = get_bool_option("activemq.use_exponential_back_off", "true") - @initial_reconnect_delay = Float(get_option("activemq.initial_reconnect_delay", 0.01)) - @back_off_multiplier = Integer(get_option("activemq.back_off_multiplier", 2)) - @max_reconnect_delay = Float(get_option("activemq.max_reconnect_delay", 30.0)) - @reconnect_delay = @initial_reconnect_delay - - Log.info("ActiveMQ connector initialized. Using stomp-gem #{stomp_version}") - end - - # Connects to the ActiveMQ middleware - def connect(connector = ::Stomp::Connection) - if @connection - Log.debug("Already connection, not re-initializing connection") - return - end - - begin - @base64 = get_bool_option("activemq.base64", "false") - @msgpriority = get_option("activemq.priority", 0).to_i - - pools = Integer(get_option("activemq.pool.size")) - hosts = [] - middleware_user = '' - middleware_password = '' - prompt_for_username = get_bool_option("activemq.prompt_user", "false") - prompt_for_password = get_bool_option("activemq.prompt_password", "false") - - if prompt_for_username - Log.debug("No previous user exists and activemq.prompt-user is set to true") - print "Please enter user to connect to middleware: " - middleware_user = STDIN.gets.chomp - end - - if prompt_for_password - Log.debug("No previous password exists and activemq.prompt-password is set to true") - middleware_password = MCollective::Util.get_hidden_input("Please enter password: ") - print "\n" - end - - 1.upto(pools) do |poolnum| - host = {} - - host[:host] = get_option("activemq.pool.#{poolnum}.host") - host[:port] = get_option("activemq.pool.#{poolnum}.port", 61613).to_i - host[:ssl] = get_bool_option("activemq.pool.#{poolnum}.ssl", "false") - - # read user from config file - host[:login] = get_env_or_option("STOMP_USER", "activemq.pool.#{poolnum}.user", middleware_user) - if prompt_for_username and host[:login] != middleware_user - Log.info("Using #{host[:login]} from config file to connect to #{host[:host]}. "+ - "plugin.activemq.prompt_user should be set to false to remove the prompt.") - end - - # read user from config file - host[:passcode] = get_env_or_option("STOMP_PASSWORD", "activemq.pool.#{poolnum}.password", middleware_password) - if prompt_for_password and host[:passcode] != middleware_password - Log.info("Using password from config file to connect to #{host[:host]}. "+ - "plugin.activemq.prompt_password should be set to false to remove the prompt.") - end - - # if ssl is enabled set :ssl to the hash of parameters - if host[:ssl] - host[:ssl] = ssl_parameters(poolnum, get_bool_option("activemq.pool.#{poolnum}.ssl.fallback", "false")) - end - - Log.debug("Adding #{host[:host]}:#{host[:port]} to the connection pool") - hosts << host - end - - raise "No hosts found for the ActiveMQ connection pool" if hosts.size == 0 - - connection = {:hosts => hosts} - - # Various STOMP gem options, defaults here matches defaults for 1.1.6 the meaning of - # these can be guessed, the documentation isn't clear - connection[:use_exponential_back_off] = @use_exponential_back_off - connection[:initial_reconnect_delay] = @initial_reconnect_delay - connection[:back_off_multiplier] = @back_off_multiplier - connection[:max_reconnect_delay] = @max_reconnect_delay - connection[:max_reconnect_attempts] = Integer(get_option("activemq.max_reconnect_attempts", 0)) - connection[:randomize] = get_bool_option("activemq.randomize", "false") - connection[:backup] = get_bool_option("activemq.backup", "false") - connection[:timeout] = Integer(get_option("activemq.timeout", -1)) - connection[:connect_timeout] = Integer(get_option("activemq.connect_timeout", 30)) - connection[:reliable] = true - connection[:connect_headers] = connection_headers - connection[:max_hbrlck_fails] = Integer(get_option("activemq.max_hbrlck_fails", 0)) - connection[:max_hbread_fails] = Integer(get_option("activemq.max_hbread_fails", 2)) - - connection[:logger] = EventLogger.new - - @connection = connector.new(connection) - - rescue ClientTimeoutError => e - raise e - rescue Exception => e - raise("Could not connect to ActiveMQ Server: #{e}") - end - end - - def stomp_version - ::Stomp::Version::STRING - end - - def stomp_version_supports_heartbeat? - return Util.versioncmp(stomp_version, "1.2.10") >= 0 - end - - def connection_headers - headers = {:"accept-version" => "1.0"} - - heartbeat_interval = Integer(get_option("activemq.heartbeat_interval", 0)) - stomp_1_0_fallback = get_bool_option("activemq.stomp_1_0_fallback", true) - - headers[:host] = get_option("activemq.vhost", "mcollective") - - if heartbeat_interval > 0 - unless stomp_version_supports_heartbeat? - raise("Setting STOMP 1.1 properties like heartbeat intervals require at least version 1.2.10 of the STOMP gem") - end - - if heartbeat_interval < 30 - Log.warn("Connection heartbeat is set to %d, forcing to minimum value of 30s") - heartbeat_interval = 30 - end - - heartbeat_interval = heartbeat_interval * 1000 - headers[:"heart-beat"] = "%d,%d" % [heartbeat_interval + 500, heartbeat_interval - 500] - - if stomp_1_0_fallback - headers[:"accept-version"] = "1.1,1.0" - else - headers[:"accept-version"] = "1.1" - end - else - if stomp_version_supports_heartbeat? - Log.info("Connecting without STOMP 1.1 heartbeats, if you are using ActiveMQ 5.8 or newer consider setting plugin.activemq.heartbeat_interval") - end - end - - headers - end - - # Sets the SSL paramaters for a specific connection - def ssl_parameters(poolnum, fallback) - params = { - :cert_file => get_cert_file(poolnum), - :key_file => get_key_file(poolnum), - :ts_files => get_option("activemq.pool.#{poolnum}.ssl.ca", false), - :ciphers => get_option("activemq.pool.#{poolnum}.ssl.ciphers", false), - } - - raise "cert, key and ca has to be supplied for verified SSL mode" unless params[:cert_file] && params[:key_file] && params[:ts_files] - - raise "Cannot find certificate file #{params[:cert_file]}" unless File.exist?(params[:cert_file]) - raise "Cannot find key file #{params[:key_file]}" unless File.exist?(params[:key_file]) - - params[:ts_files].split(",").each do |ca| - raise "Cannot find CA file #{ca}" unless File.exist?(ca) - end - - begin - ::Stomp::SSLParams.new(params) - rescue NameError - raise "Stomp gem >= 1.2.2 is needed" - end - - rescue Exception => e - if fallback - Log.warn("Failed to set full SSL verified mode, falling back to unverified: #{e.class}: #{e}") - return true - else - Log.error("Failed to set full SSL verified mode: #{e.class}: #{e}") - raise(e) - end - end - - # Returns the name of the private key file used by ActiveMQ - # Will first check if an environment variable MCOLLECTIVE_ACTIVEMQ_POOLX_SSL_KEY exists, - # where X is the ActiveMQ pool number. - # If the environment variable doesn't exist, it will try and load the value from the config. - def get_key_file(poolnum) - ENV["MCOLLECTIVE_ACTIVEMQ_POOL%s_SSL_KEY" % poolnum] || get_option("activemq.pool.#{poolnum}.ssl.key", false) - end - - # Returns the name of the certficate file used by ActiveMQ - # Will first check if an environment variable MCOLLECTIVE_ACTIVEMQ_POOLX_SSL_CERT exists, - # where X is the ActiveMQ pool number. - # If the environment variable doesn't exist, it will try and load the value from the config. - def get_cert_file(poolnum) - ENV["MCOLLECTIVE_ACTIVEMQ_POOL%s_SSL_CERT" % poolnum] || get_option("activemq.pool.#{poolnum}.ssl.cert", false) - end - - # Calculate the exponential backoff needed - def exponential_back_off - if !@use_exponential_back_off - return nil - end - - backoff = @reconnect_delay - - # calculate next delay - @reconnect_delay = @reconnect_delay * @back_off_multiplier - - # cap at max reconnect delay - if @reconnect_delay > @max_reconnect_delay - @reconnect_delay = @max_reconnect_delay - end - - return backoff - end - - # Receives a message from the ActiveMQ connection - def receive - Log.debug("Waiting for a message from ActiveMQ") - - # When the Stomp library > 1.2.0 is mid reconnecting due to its reliable connection - # handling it sets the connection to closed. If we happen to be receiving at just - # that time we will get an exception warning about the closed connection so handling - # that here with a sleep and a retry. - begin - msg = @connection.receive - rescue ::Stomp::Error::NoCurrentConnection - sleep 1 - retry - end - - # In older stomp gems an attempt to receive after failed authentication can return nil - if msg.nil? - raise MessageNotReceived.new(exponential_back_off), "No message received from ActiveMQ." - - end - - # We expect all messages we get to be of STOMP frame type MESSAGE, raise on unexpected types - if msg.command != 'MESSAGE' - Log.debug("Unexpected '#{msg.command}' frame. Headers: #{msg.headers.inspect} Body: #{msg.body.inspect}") - raise UnexpectedMessageType.new(exponential_back_off), - "Received frame of type '#{msg.command}' expected 'MESSAGE'" - end - - Message.new(msg.body, msg, :base64 => @base64, :headers => msg.headers) - end - - # Sends a message to the ActiveMQ connection - def publish(msg) - msg.base64_encode! if @base64 - - target = target_for(msg) - - if msg.type == :direct_request - msg.discovered_hosts.each do |node| - target[:headers] = headers_for(msg, node) - - Log.debug("Sending a direct message to ActiveMQ target '#{target[:name]}' with headers '#{target[:headers].inspect}'") - - @connection.publish(target[:name], msg.payload, target[:headers]) - end - else - target[:headers].merge!(headers_for(msg)) - - Log.debug("Sending a broadcast message to ActiveMQ target '#{target[:name]}' with headers '#{target[:headers].inspect}'") - - @connection.publish(target[:name], msg.payload, target[:headers]) - end - end - - # Subscribe to a topic or queue - def subscribe(agent, type, collective) - source = make_target(agent, type, collective) - - unless @subscriptions.include?(source[:id]) - Log.debug("Subscribing to #{source[:name]} with headers #{source[:headers].inspect.chomp}") - @connection.subscribe(source[:name], source[:headers], source[:id]) - @subscriptions << source[:id] - end - rescue ::Stomp::Error::DuplicateSubscription - Log.error("Received subscription request for #{source.inspect.chomp} but already had a matching subscription, ignoring") - end - - # UnSubscribe to a topic or queue - def unsubscribe(agent, type, collective) - source = make_target(agent, type, collective) - - Log.debug("Unsubscribing from #{source[:name]}") - @connection.unsubscribe(source[:name], source[:headers], source[:id]) - @subscriptions.delete(source[:id]) - end - - def target_for(msg) - if msg.type == :reply - target = {:name => msg.request.headers["reply-to"], :headers => {}} - elsif [:request, :direct_request].include?(msg.type) - target = make_target(msg.agent, msg.type, msg.collective) - else - raise "Don't now how to create a target for message type #{msg.type}" - end - - return target - end - - # Disconnects from the ActiveMQ connection - def disconnect - Log.debug("Disconnecting from ActiveMQ") - @connection.disconnect - @connection = nil - end - - def headers_for(msg, identity=nil) - headers = {} - - headers = {"priority" => @msgpriority} if @msgpriority > 0 - - headers["timestamp"] = (Time.now.utc.to_i * 1000).to_s - - # set the expires header based on the TTL, we build a small additional - # timeout of 10 seconds in here to allow for network latency etc - headers["expires"] = ((Time.now.utc.to_i + msg.ttl + 10) * 1000).to_s - - if [:request, :direct_request].include?(msg.type) - target = make_target(msg.agent, :reply, msg.collective) - - if msg.reply_to - headers["reply-to"] = msg.reply_to - else - headers["reply-to"] = target[:name] - end - - headers["mc_identity"] = identity if msg.type == :direct_request - end - - headers["mc_sender"] = Config.instance.identity - - return headers - end - - def make_target(agent, type, collective) - raise("Unknown target type #{type}") unless [:directed, :broadcast, :reply, :request, :direct_request].include?(type) - raise("Unknown collective '#{collective}' known collectives are '#{@config.collectives.join ', '}'") unless @config.collectives.include?(collective) - - agents_multiplex = get_bool_option("activemq.agents_multiplex", "false") - target = {:name => nil, :headers => {}} - - case type - when :reply - target[:name] = ["/queue/" + collective, :reply, "#{Config.instance.identity}_#{$$}", Client.request_sequence].join(".") - - when :broadcast - if agents_multiplex - target[:name] = ["/topic/" + collective, :agents].join(".") - else - target[:name] = ["/topic/" + collective, agent, :agent].join(".") - end - - when :request - if agents_multiplex - target[:name] = ["/topic/" + collective, :agents].join(".") - else - target[:name] = ["/topic/" + collective, agent, :agent].join(".") - end - - when :direct_request - target[:name] = ["/queue/" + collective, :nodes].join(".") - - when :directed - target[:name] = ["/queue/" + collective, :nodes].join(".") - target[:headers]["selector"] = "mc_identity = '#{@config.identity}'" - target[:id] = "%s_directed_to_identity" % collective - end - - target[:id] = target[:name] unless target[:id] - - target - end - - # looks in the environment first then in the config file - # for a specific option, accepts an optional default. - # - # raises an exception when it cant find a value anywhere - def get_env_or_option(env, opt, default=nil) - return ENV[env] if ENV.include?(env) - return @config.pluginconf[opt] if @config.pluginconf.include?(opt) - return default if default - - raise("No #{env} environment or plugin.#{opt} configuration option given") - end - - # looks for a config option, accepts an optional default - # - # raises an exception when it cant find a value anywhere - def get_option(opt, default=nil) - return @config.pluginconf[opt] if @config.pluginconf.include?(opt) - return default unless default.nil? - - raise("No plugin.#{opt} configuration option given") - end - - # looks up a boolean value in the config - def get_bool_option(val, default) - Util.str_to_bool(@config.pluginconf.fetch(val, default)) - end - end - end -end - -# vi:tabstop=4:expandtab:ai diff --git a/lib/mcollective/connector/rabbitmq.ddl b/lib/mcollective/connector/rabbitmq.ddl deleted file mode 100644 index 9d7a4559..00000000 --- a/lib/mcollective/connector/rabbitmq.ddl +++ /dev/null @@ -1,9 +0,0 @@ -metadata :name => "RabbitMQ Connector", - :description => "Connector plugin for RabbitMQ middleware", - :author => "Puppet Labs", - :license => "ASL 2.0", - :version => "1.0.0", - :url => "https://docs.puppetlabs.com/mcollective/plugin_directory/", - :timeout => 60 - -requires :mcollective => "2.6.0" diff --git a/lib/mcollective/connector/rabbitmq.rb b/lib/mcollective/connector/rabbitmq.rb deleted file mode 100644 index 2fe3cef4..00000000 --- a/lib/mcollective/connector/rabbitmq.rb +++ /dev/null @@ -1,517 +0,0 @@ -require 'stomp' - -module MCollective - module Connector - class Rabbitmq= params[:max_hbrlck_fails] - # we're about to force a disconnect - Log.error("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect]) - else - Log.warn("Heartbeat failed to acquire readlock for '%s': %s" % [stomp_url(params), ticker_data.inspect]) - end - else - if params[:max_hbread_fails] == 0 - # failure is disabled - Log.debug("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect]) - elsif ticker_data['read_fail_count'] >= params[:max_hbread_fails] - # we're about to force a reconnect - Log.error("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect]) - else - Log.warn("Heartbeat read failed from '%s': %s" % [stomp_url(params), ticker_data.inspect]) - end - end - rescue Exception => e - end - - # Stomp 1.1+ - heart beat send (transmit) failed. - def on_hbwrite_fail(params, ticker_data) - Log.error("Heartbeat write failed from '%s': %s" % [stomp_url(params), ticker_data.inspect]) - rescue Exception => e - end - - # Log heart beat fires - def on_hbfire(params, srind, curt) - case srind - when "receive_fire" - Log.debug("Received heartbeat from %s: %s, %s" % [stomp_url(params), srind, curt]) - when "send_fire" - Log.debug("Publishing heartbeat to %s: %s, %s" % [stomp_url(params), srind, curt]) - end - rescue Exception => e - end - - def stomp_url(params) - "%s://%s@%s:%d" % [ params[:cur_ssl] ? "stomp+ssl" : "stomp", params[:cur_login], params[:cur_host], params[:cur_port]] - end - end - - def initialize - @config = Config.instance - @subscriptions = [] - @base64 = false - @use_exponential_back_off = get_bool_option("rabbitmq.use_exponential_back_off", "true") - @initial_reconnect_delay = Float(get_option("rabbitmq.initial_reconnect_delay", 0.01)) - @back_off_multiplier = Integer(get_option("rabbitmq.back_off_multiplier", 2)) - @max_reconnect_delay = Float(get_option("rabbitmq.max_reconnect_delay", 30.0)) - @reconnect_delay = @initial_reconnect_delay - - Log.info("RabbitMQ connector initialized. Using stomp-gem #{stomp_version}") - end - - # Connects to the RabbitMQ middleware - def connect(connector = ::Stomp::Connection) - if @connection - Log.debug("Already connection, not re-initializing connection") - return - end - - begin - @base64 = get_bool_option("rabbitmq.base64", "false") - - pools = Integer(get_option("rabbitmq.pool.size")) - hosts = [] - middleware_user = '' - middleware_password = '' - prompt_for_username = get_bool_option("rabbitmq.prompt_user", "false") - prompt_for_password = get_bool_option("rabbitmq.prompt_password", "false") - if prompt_for_username - Log.debug("No previous user exists and rabbitmq.prompt-user is set to true") - print "Please enter user to connect to middleware: " - middleware_user = STDIN.gets.chomp - end - - if prompt_for_password - Log.debug("No previous password exists and rabbitmq.prompt-password is set to true") - middleware_password = MCollective::Util.get_hidden_input("Please enter password: ") - print "\n" - end - - 1.upto(pools) do |poolnum| - host = {} - - host[:host] = get_option("rabbitmq.pool.#{poolnum}.host") - host[:port] = get_option("rabbitmq.pool.#{poolnum}.port", 61613).to_i - host[:ssl] = get_bool_option("rabbitmq.pool.#{poolnum}.ssl", "false") - - # read user from config file - host[:login] = get_env_or_option("STOMP_USER", "rabbitmq.pool.#{poolnum}.user", middleware_user) - if prompt_for_username and host[:login] != middleware_user - Log.info("Using #{host[:login]} from config file to connect to #{host[:host]}. "+ - "plugin.rabbitmq.prompt_user should be set to false to remove the prompt.") - end - - # read password from config file - host[:passcode] = get_env_or_option("STOMP_PASSWORD", "rabbitmq.pool.#{poolnum}.password", middleware_password) - if prompt_for_password and host[:passcode] != middleware_password - Log.info("Using password from config file to connect to #{host[:host]}. "+ - "plugin.rabbitmq.prompt_password should be set to false to remove the prompt.") - end - - # if ssl is enabled set :ssl to the hash of parameters - if host[:ssl] - host[:ssl] = ssl_parameters(poolnum, get_bool_option("rabbitmq.pool.#{poolnum}.ssl.fallback", "false")) - end - - Log.debug("Adding #{host[:host]}:#{host[:port]} to the connection pool") - hosts << host - end - - raise "No hosts found for the RabbitMQ connection pool" if hosts.size == 0 - - connection = {:hosts => hosts} - - # Various STOMP gem options, defaults here matches defaults for 1.1.6 the meaning of - # these can be guessed, the documentation isn't clear - connection[:use_exponential_back_off] = @use_exponential_back_off - connection[:initial_reconnect_delay] = @initial_reconnect_delay - connection[:back_off_multiplier] = @back_off_multiplier - connection[:max_reconnect_delay] = @max_reconnect_delay - connection[:max_reconnect_attempts] = Integer(get_option("rabbitmq.max_reconnect_attempts", 0)) - connection[:randomize] = get_bool_option("rabbitmq.randomize", "false") - connection[:backup] = get_bool_option("rabbitmq.backup", "false") - - connection[:timeout] = Integer(get_option("rabbitmq.timeout", -1)) - connection[:connect_timeout] = Integer(get_option("rabbitmq.connect_timeout", 30)) - connection[:reliable] = true - connection[:max_hbrlck_fails] = Integer(get_option("rabbitmq.max_hbrlck_fails", 0)) - connection[:max_hbread_fails] = Integer(get_option("rabbitmq.max_hbread_fails", 2)) - - connection[:connect_headers] = connection_headers - - connection[:logger] = EventLogger.new - - @connection = connector.new(connection) - - rescue ClientTimeoutError => e - raise e - rescue Exception => e - raise("Could not connect to RabbitMQ Server: #{e}") - end - end - - def connection_headers - headers = {:"accept-version" => "1.0"} - - heartbeat_interval = Integer(get_option("rabbitmq.heartbeat_interval", 0)) - stomp_1_0_fallback = get_bool_option("rabbitmq.stomp_1_0_fallback", true) - - headers[:host] = get_option("rabbitmq.vhost", "/") - - if heartbeat_interval > 0 - unless stomp_version_supports_heartbeat? - raise("Setting STOMP 1.1 properties like heartbeat intervals require at least version 1.2.10 of the STOMP gem") - end - - if heartbeat_interval < 30 - Log.warn("Connection heartbeat is set to %d, forcing to minimum value of 30s") - heartbeat_interval = 30 - end - - heartbeat_interval = heartbeat_interval * 1000 - headers[:"heart-beat"] = "%d,%d" % [heartbeat_interval + 500, heartbeat_interval - 500] - - if stomp_1_0_fallback - headers[:"accept-version"] = "1.1,1.0" - else - headers[:"accept-version"] = "1.1" - end - else - if stomp_version_supports_heartbeat? - Log.info("Connecting without STOMP 1.1 heartbeats, consider setting plugin.rabbitmq.heartbeat_interval") - end - end - - headers - end - - def stomp_version - ::Stomp::Version::STRING - end - - def stomp_version_supports_heartbeat? - return Util.versioncmp(stomp_version, "1.2.10") >= 0 - end - - # Sets the SSL paramaters for a specific connection - def ssl_parameters(poolnum, fallback) - params = { - :cert_file => get_cert_file(poolnum), - :key_file => get_key_file(poolnum), - :ts_files => get_option("rabbitmq.pool.#{poolnum}.ssl.ca", false), - :ciphers => get_option("rabbitmq.pool.#{poolnum}.ssl.ciphers", false), - } - - raise "cert, key and ca has to be supplied for verified SSL mode" unless params[:cert_file] && params[:key_file] && params[:ts_files] - - raise "Cannot find certificate file #{params[:cert_file]}" unless File.exist?(params[:cert_file]) - raise "Cannot find key file #{params[:key_file]}" unless File.exist?(params[:key_file]) - - params[:ts_files].split(",").each do |ca| - raise "Cannot find CA file #{ca}" unless File.exist?(ca) - end - - begin - ::Stomp::SSLParams.new(params) - rescue NameError - raise "Stomp gem >= 1.2.2 is needed" - end - - rescue Exception => e - if fallback - Log.warn("Failed to set full SSL verified mode, falling back to unverified: #{e.class}: #{e}") - return true - else - Log.error("Failed to set full SSL verified mode: #{e.class}: #{e}") - raise(e) - end - end - - # Returns the name of the private key file used by RabbitMQ - # Will first check if an environment variable MCOLLECTIVE_RABBITMQ_POOLX_SSL_KEY exists, - # where X is the RabbitMQ pool number. - # If the environment variable doesn't exist, it will try and load the value from the config. - def get_key_file(poolnum) - ENV["MCOLLECTIVE_RABBITMQ_POOL%s_SSL_KEY" % poolnum] || get_option("rabbitmq.pool.#{poolnum}.ssl.key", false) - end - - # Returns the name of the certificate file used by RabbitMQ - # Will first check if an environment variable MCOLLECTIVE_RABBITMQ_POOLX_SSL_CERT exists, - # where X is the RabbitMQ pool number. - # If the environment variable doesn't exist, it will try and load the value from the config. - def get_cert_file(poolnum) - ENV["MCOLLECTIVE_RABBITMQ_POOL%s_SSL_CERT" % poolnum] || get_option("rabbitmq.pool.#{poolnum}.ssl.cert", false) - end - - # Calculate the exponential backoff needed - def exponential_back_off - if !@use_exponential_back_off - return nil - end - - backoff = @reconnect_delay - - # calculate next delay - @reconnect_delay = @reconnect_delay * @back_off_multiplier - - # cap at max reconnect delay - if @reconnect_delay > @max_reconnect_delay - @reconnect_delay = @max_reconnect_delay - end - - return backoff - end - - # Receives a message from the RabbitMQ connection - def receive - Log.debug("Waiting for a message from RabbitMQ") - - # When the Stomp library > 1.2.0 is mid reconnecting due to its reliable connection - # handling it sets the connection to closed. If we happen to be receiving at just - # that time we will get an exception warning about the closed connection so handling - # that here with a sleep and a retry. - begin - msg = @connection.receive - rescue ::Stomp::Error::NoCurrentConnection - sleep 1 - retry - end - - # In older stomp gems an attempt to receive after failed authentication can return nil - if msg.nil? - raise MessageNotReceived.new(exponential_back_off), "No message received from RabbitMQ." - end - - raise "Received a processing error from RabbitMQ: '%s'" % msg.body.chomp if msg.body =~ /Processing error/ - - # We expect all messages we get to be of STOMP frame type MESSAGE, raise on unexpected types - if msg.command != 'MESSAGE' - Log.debug("Unexpected '#{msg.command}' frame. Headers: #{msg.headers.inspect} Body: #{msg.body.inspect}") - raise UnexpectedMessageType.new(exponential_back_off), - "Received frame of type '#{msg.command}' expected 'MESSAGE'" - end - - Message.new(msg.body, msg, :base64 => @base64, :headers => msg.headers) - end - - # Sends a message to the RabbitMQ connection - def publish(msg) - msg.base64_encode! if @base64 - - if msg.type == :direct_request - msg.discovered_hosts.each do |node| - target = target_for(msg, node) - - Log.debug("Sending a direct message to RabbitMQ target '#{target[:name]}' with headers '#{target[:headers].inspect}'") - - @connection.publish(target[:name], msg.payload, target[:headers]) - end - else - target = target_for(msg) - - Log.debug("Sending a broadcast message to RabbitMQ target '#{target[:name]}' with headers '#{target[:headers].inspect}'") - - @connection.publish(target[:name], msg.payload, target[:headers]) - end - end - - def target_for(msg, node=nil) - if msg.type == :reply - target = {:name => msg.request.headers["reply-to"], :headers => {}, :id => ""} - - elsif [:request, :direct_request].include?(msg.type) - target = make_target(msg.agent, msg.type, msg.collective, msg.reply_to, node) - - else - raise "Don't now how to create a target for message type #{msg.type}" - - end - - # marks messages as valid for ttl + 10 seconds, we do this here - # rather than in make_target as this should only be set on publish - target[:headers]["expiration"] = ((msg.ttl + 10) * 1000).to_s - - target[:headers]["mc_sender"] = Config.instance.identity - - return target - end - - def make_target(agent, type, collective, reply_to=nil, node=nil) - raise("Unknown target type #{type}") unless [:directed, :broadcast, :reply, :request, :direct_request].include?(type) - raise("Unknown collective '#{collective}' known collectives are '#{@config.collectives.join ', '}'") unless @config.collectives.include?(collective) - - agents_multiplex = get_bool_option("rabbitmq.agents_multiplex", "false") - target = {:name => "", :headers => {}, :id => nil} - - if reply_to - reply_path = reply_to - elsif get_bool_option("rabbitmq.use_reply_exchange", false) - reply_path = "/exchange/mcollective_reply/%s_%s_%s" % [ @config.identity, $$, Client.request_sequence ] - else - reply_path = "/temp-queue/mcollective_reply_%s" % agent - end - case type - when :reply # receiving replies on a temp queue - target[:name] = reply_path - target[:id] = "mcollective_%s_replies" % agent - - when :broadcast, :request # publishing a request to all nodes with an agent - if agents_multiplex - target[:name] = "/exchange/%s_broadcast" % collective - target[:id] = "%s_broadcast" % collective - else - target[:name] = "/exchange/%s_broadcast/%s" % [collective, agent] - target[:id] = "%s_broadcast_%s" % [collective, agent] - end - if reply_to - target[:headers]["reply-to"] = reply_to - else - target[:headers]["reply-to"] = reply_path - end - - when :direct_request # a request to a specific node - raise "Directed requests need to have a node identity" unless node - - target[:name] = "/exchange/%s_directed/%s" % [ collective, node] - target[:headers]["reply-to"] = reply_path - - when :directed # subscribing to directed messages - target[:name] = "/exchange/%s_directed/%s" % [ collective, @config.identity ] - target[:id] = "%s_%s_directed_to_identity" % [ collective, @config.identity ] - end - - target - end - - # Subscribe to a topic or queue - def subscribe(agent, type, collective) - if type == :reply - # On rabbitmq if you send a message with a reply-to: header set to - # '/temp-queue/*' it automatically creates a private queue, munges - # the reply-to: header to point to this private queue, and - # subscribes you to it. As such you should never attempt to - # SUBSCRIBE or UNSUBSCRIBE to '/temp-queue/*' directly as that'll - # cause great pain and suffering. - # https://www.rabbitmq.com/stomp.html#d.tqd - - # The exception to this is in 'use_reply_exchange' mode, when the - # reply-to will be set to a queue in an explicit exchange. - if !get_bool_option("rabbitmq.use_reply_exchange", false) - # We aren't in 'use_reply_exchange' mode, don't subscribe. - return - end - end - - source = make_target(agent, type, collective) - - unless @subscriptions.include?(source[:id]) - Log.debug("Subscribing to #{source[:name]} with headers #{source[:headers].inspect.chomp}") - @connection.subscribe(source[:name], source[:headers], source[:id]) - @subscriptions << source[:id] - end - rescue ::Stomp::Error::DuplicateSubscription - Log.error("Received subscription request for #{source.inspect.chomp} but already had a matching subscription, ignoring") - end - - # Subscribe to a topic or queue - def unsubscribe(agent, type, collective) - if type == :reply - # For a more detailed discussion of this logic, please see #subscribe - if !get_bool_option("rabbitmq.use_reply_exchange", false) - # We shouldn't try to unsubscribe from a '/temp-queue/*' queue. - return - end - end - - source = make_target(agent, type, collective) - - Log.debug("Unsubscribing from #{source[:name]}") - @connection.unsubscribe(source[:name], source[:headers], source[:id]) - @subscriptions.delete(source[:id]) - end - - # Disconnects from the RabbitMQ connection - def disconnect - Log.debug("Disconnecting from RabbitMQ") - @connection.disconnect - @connection = nil - end - - # looks in the environment first then in the config file - # for a specific option, accepts an optional default. - # - # raises an exception when it cant find a value anywhere - def get_env_or_option(env, opt, default=nil) - return ENV[env] if ENV.include?(env) - return @config.pluginconf[opt] if @config.pluginconf.include?(opt) - return default if default - - raise("No #{env} environment or plugin.#{opt} configuration option given") - end - - # looks for a config option, accepts an optional default - # - # raises an exception when it cant find a value anywhere - def get_option(opt, default=nil) - return @config.pluginconf[opt] if @config.pluginconf.include?(opt) - return default unless default.nil? - - raise("No plugin.#{opt} configuration option given") - end - - # looks up a boolean value in the config - def get_bool_option(val, default) - Util.str_to_bool(@config.pluginconf.fetch(val, default)) - end - end - end -end - -# vi:tabstop=4:expandtab:ai diff --git a/lib/mcollective/registration.rb b/lib/mcollective/registration.rb deleted file mode 100644 index 7a0c23b5..00000000 --- a/lib/mcollective/registration.rb +++ /dev/null @@ -1,16 +0,0 @@ -module MCollective - # Registration is implimented using a module structure and installations can - # configure which module they want to use. - # - # We provide a simple one that just sends back the list of current known agents - # in MCollective::Registration::Agentlist, you can create your own: - # - # Create a module in plugins/mcollective/registration/.rb - # - # You can inherit from MCollective::Registration::Base in which case you just need - # to supply a _body_ method, whatever this method returns will be send to the - # middleware connection for an agent called _registration_ - module Registration - require "mcollective/registration/base" - end -end diff --git a/lib/mcollective/registration/agentlist.rb b/lib/mcollective/registration/agentlist.rb deleted file mode 100644 index 614464a8..00000000 --- a/lib/mcollective/registration/agentlist.rb +++ /dev/null @@ -1,10 +0,0 @@ -module MCollective - module Registration - # A registration plugin that simply sends in the list of agents we have - class Agentlist e - Log.error("Failed to initialize MCollective runner.") - Log.error(e) - Log.error(e.backtrace.join("\n\t")) - raise e - end - end - - # Deprecated - def run - Log.warn("The #run method has been deprecated. Use #main_loop instead.") - main_loop - end - - # The main runner loop - def main_loop - # Enter the main context - @receiver_thread = start_receiver_thread - loop do - begin - case @state - when :stopping - Log.debug("Stopping MCollective server") - - # If soft_shutdown has been enabled we wait for all running agents to - # finish, one way or the other. - if @config.soft_shutdown - soft_shutdown - end - - stop_threads - @state = :stopped - return - - # When pausing we stop the receiver thread but keep everything else alive - # This means that running agents also run to completion. - when :pausing - Log.debug("Pausing MCollective server") - stop_threads - @state = :paused - - when :unpausing - Log.debug("Unpausing MCollective server") - start_receiver_thread - end - - # prune dead threads from the agent_threads array - @agent_threads.reject! { |t| !t.alive? } - sleep 0.1 - rescue SignalException => e - Log.info("Exiting after signal: #{e}") - stop - rescue => e - Log.error("A failure occurred in the MCollective runner.") - Log.error(e) - Log.error(e.backtrace.join("\n\t")) - stop - end - end - end - - def stop - @state = :stopping - end - - def pause - if @state == :running - @state = :pausing - else - Log.error("Cannot pause MCollective while not in a running state") - end - end - - def resume - if @state == :paused - @state = :unpausing - else - Log.error("Cannot unpause MCollective when it is not paused") - end - end - - private - - def start_receiver_thread - @state = :running - @parent = Thread.current - Thread.new do - begin - receiver_thread - rescue Exception => e - # When we are pausing the receiver thread will be killed. - # If the thread raises an exception at any other time - # reraise it in the main thread. - if @state != :pausing - @parent.raise(e) - end - end - end - end - - def stop_threads - @receiver_thread.kill if @receiver_thread.alive? - # Kill the registration thread if it was made and alive - if @registration_thread && @registration_thread.alive? - @registration_thread.kill - end - end - - def receiver_thread - # Create internal connection in Connector - @connection.connect - - # Subscribe to the direct addressing queue if direct_addressing is enabled - if @config.direct_addressing - Util.subscribe_to_direct_addressing_queue - end - - # Create the agents and let them create their subscriptions - @agents ||= Agents.new - - # Load data sources - Data.load_data_sources - - # Start the registration plugin if interval isn't 0 - begin - if @config.registerinterval != 0 - @registration_thread = PluginManager["registration_plugin"].run(@connection) - end - rescue Exception => e - Log.error("Failed to start registration plugin: #{e}") - end - - # Start the receiver loop - loop do - begin - request = receive - - @agent_threads << agentmsg(request) - rescue MsgTTLExpired => e - Log.warn(e) - - rescue NotTargettedAtUs => e - Log.info(e) - - rescue MessageNotReceived, UnexpectedMessageType => e - Log.warn(e) - if e.backoff && @state != :stopping - Log.info("sleeping for suggested #{e.backoff} seconds") - sleep e.backoff - end - - rescue Exception => e - Log.warn("Failed to handle message: #{e} - #{e.class}\n") - Log.warn(e.backtrace.join("\n\t")) - end - - return if @exit_receiver_thread - end - end - - # Deals with messages directed to agents - def agentmsg(request) - Log.info("Handling message #{request.description}: #{request.payload[:body]}") - - @agents.dispatch(request, @connection) do |reply_message| - reply(reply_message, request) if reply_message - end - end - - # Receive a message from the connection handler - def receive - request = @connection.receive - request.type = :request - - @stats.received - - request.decode! - request.validate - - request - end - - # Sends a reply to a specific target topic - def reply(msg, request) - msg = Message.new(msg, nil, :request => request) - msg.encode! - msg.publish - - @stats.sent - end - - # Waits for all agent threads to complete - # If soft_shutdown_timeout has been defined it will wait for the - # configured grace period before killing all the threads - def soft_shutdown - timeout = @config.soft_shutdown_timeout - - if timeout && timeout <= 0 - Log.warn("soft_shutdown_timeout has been set to '#{timeout}'. soft_shutdown_timeout must be > 0") - Log.warn("Shutting down normally.") - return - end - - if Util.windows? - windows_soft_shutdown(timeout) - return - end - - posix_soft_shutdown(timeout) - end - - # Implements soft shutdown on the Windows platform - # Logs and returns without doing anything if a timeout - # hasn't been specified since waiting for long running threads - # to exit on Windows can put the MCollective service in a broken state - def windows_soft_shutdown(timeout) - if !timeout - Log.warn("soft_shutdown specified but not soft_shutdown_timeout specified.") - Log.warn("To enable soft_shutdown on windows a soft_shutdown_timeout must be specified.") - Log.warn("Shutting down normally.") - return - end - - shutdown_with_timeout(timeout) - end - - # Implements soft shutdown on posix systems - def posix_soft_shutdown(timeout) - if timeout - shutdown_with_timeout(timeout) - return - end - - stop_agent_threads - end - - def shutdown_with_timeout(timeout) - Log.debug("Shutting down agents with a timeout of '#{timeout}' seconds") - begin - Timeout.timeout(timeout) do - stop_agent_threads - end - rescue Timeout::Error - Log.warn("soft_shutdown_timeout reached. Terminating all running agent threads.") - end - end - - def stop_agent_threads - Log.debug("Waiting for all running agents to finish or timeout.") - @agent_threads.each do |t| - if t.alive? - t.join - end - end - Log.debug("All running agents have completed. Stopping.") - end - end -end diff --git a/lib/mcollective/security/aes_security.rb b/lib/mcollective/security/aes_security.rb deleted file mode 100644 index 35aa2330..00000000 --- a/lib/mcollective/security/aes_security.rb +++ /dev/null @@ -1,398 +0,0 @@ -module MCollective - module Security - # Impliments a security system that encrypts payloads using AES and secures - # the AES encrypted data using RSA public/private key encryption. - # - # The design goals of this plugin are: - # - # - Each actor - clients and servers - can have their own set of public and - # private keys - # - All actors are uniquely and cryptographically identified - # - Requests are encrypted using the clients private key and anyone that has - # the public key can see the request. Thus an atacker may see the requests - # given access to network or machine due to the broadcast nature of mcollective - # - The message time and TTL of messages are cryptographically secured making the - # ensuring messages can not be replayed with fake TTLs or times - # - Replies are encrypted using the calling clients public key. Thus no-one but - # the caller can view the contents of replies. - # - Servers can all have their own RSA keys, or share one, or reuse keys created - # by other PKI using software like Puppet - # - Requests from servers - like registration data - can be secured even to external - # eaves droppers depending on the level of configuration you are prepared to do - # - Given a network where you can ensure third parties are not able to access the - # middleware public key distribution can happen automatically - # - # Configuration Options: - # ====================== - # - # Common Options: - # - # # Enable this plugin - # securityprovider = aes_security - # - # # Use YAML as serializer - # plugin.aes.serializer = yaml - # - # # Send our public key with every request so servers can learn it - # plugin.aes.send_pubkey = 1 - # - # Clients: - # - # # The clients public and private keys - # plugin.aes.client_private = /home/user/.mcollective.d/user-private.pem - # plugin.aes.client_public = /home/user/.mcollective.d/user.pem - # - # Servers: - # - # # Where to cache client keys or find manually distributed ones - # plugin.aes.client_cert_dir = /etc/mcollective/ssl/clients - # - # # Cache public keys promiscuously from the network (this requires either a ca_cert to be set - # or insecure_learning to be enabled) - # plugin.aes.learn_pubkeys = 1 - # - # # Do not check if client certificate can be verified by a CA - # plugin.aes.insecure_learning = 1 - # - # # CA cert used to verify public keys when in learning mode - # plugin.aes.ca_cert = /etc/mcollective/ssl/ca.cert - # - # # Log but accept messages that may have been tampered with - # plugin.aes.enforce_ttl = 0 - # - # # The servers public and private keys - # plugin.aes.server_private = /etc/mcollective/ssl/server-private.pem - # plugin.aes.server_public = /etc/mcollective/ssl/server-public.pem - # - class Aes_security body[:sslkey], :data => body[:body]} - - if @initiated_by == :client - body[:body] = deserialize(decrypt(cryptdata, nil)) - else - certname = certname_from_callerid(body[:callerid]) - certfile = "#{client_cert_dir}/#{certname}.pem" - # if aes.ca_cert is set every certificate is validated before we try and use it - if @config.pluginconf.fetch("aes.ca_cert", nil) && !validate_certificate(File.read(certfile), certname) - raise "Unable to validate certificate '#{certname}' against CA" - end - body[:body] = deserialize(decrypt(cryptdata, body[:callerid])) - - # If we got a hash it's possible that this is a message with secure - # TTL and message time, attempt to decode that and transform into a - # traditional message. - # - # If it's not a hash it might be a old style message like old discovery - # ones that would just be a string so we allow that unaudited but only - # if enforce_ttl is disabled. This is primarly to allow a mixed old and - # new plugin infrastructure to work - if body[:body].is_a?(Hash) - update_secure_property(body, :aes_ttl, :ttl, "TTL") - update_secure_property(body, :aes_msgtime, :msgtime, "Message Time") - - body[:body] = body[:body][:aes_msg] if body[:body].include?(:aes_msg) - else - unless @config.pluginconf["aes.enforce_ttl"] == "0" - raise "Message %s is in an unknown or older security protocol, ignoring" % [request_description(body)] - end - end - end - - return body - rescue MsgDoesNotMatchRequestID - raise - - rescue OpenSSL::PKey::RSAError - raise MsgDoesNotMatchRequestID, "Could not decrypt message using our key, possibly directed at another client" - - rescue Exception => e - Log.warn("Could not decrypt message from client: #{e.class}: #{e}") - raise SecurityValidationFailed, "Could not decrypt message" - end - - # To avoid tampering we turn the origin body into a hash and copy some of the protocol keys - # like :ttl and :msg_time into the hash before encrypting it. - # - # This function compares and updates the unencrypted ones based on the encrypted ones. By - # default it enforces matching and presense by raising exceptions, if aes.enforce_ttl is set - # to 0 it will only log warnings about violations - def update_secure_property(msg, secure_property, property, description) - req = request_description(msg) - - unless @config.pluginconf["aes.enforce_ttl"] == "0" - raise "Request #{req} does not have a secure #{description}" unless msg[:body].include?(secure_property) - raise "Request #{req} #{description} does not match encrypted #{description} - possible tampering" unless msg[:body][secure_property] == msg[property] - else - if msg[:body].include?(secure_property) - Log.warn("Request #{req} #{description} does not match encrypted #{description} - possible tampering") unless msg[:body][secure_property] == msg[property] - else - Log.warn("Request #{req} does not have a secure #{description}") unless msg[:body].include?(secure_property) - end - end - - msg[property] = msg[:body][secure_property] if msg[:body].include?(secure_property) - msg[:body].delete(secure_property) - end - - # Encodes a reply - def encodereply(sender, msg, requestid, requestcallerid) - crypted = encrypt(serialize(msg), requestcallerid) - - req = create_reply(requestid, sender, crypted[:data]) - req[:sslkey] = crypted[:key] - - serialize(req) - end - - # Encodes a request msg - def encoderequest(sender, msg, requestid, filter, target_agent, target_collective, ttl=60) - req = create_request(requestid, filter, nil, @initiated_by, target_agent, target_collective, ttl) - - # embed the ttl and msgtime in the crypted data later we will use these in - # the decoding of a message to set the message ones from secure sources. this - # is to ensure messages are not tampered with to facility replay attacks etc - aes_msg = {:aes_msg => msg, - :aes_ttl => ttl, - :aes_msgtime => req[:msgtime]} - - crypted = encrypt(serialize(aes_msg), callerid) - - req[:body] = crypted[:data] - req[:sslkey] = crypted[:key] - - if @config.pluginconf.include?("aes.send_pubkey") && @config.pluginconf["aes.send_pubkey"] == "1" - if @initiated_by == :client - req[:sslpubkey] = File.read(client_public_key) - else - req[:sslpubkey] = File.read(server_public_key) - end - end - - serialize(req) - end - - # Serializes a message using the configured encoder - def serialize(msg) - serializer = @config.pluginconf["aes.serializer"] || "marshal" - - Log.debug("Serializing using #{serializer}") - - case serializer - when "yaml" - return YAML.dump(msg) - else - return Marshal.dump(msg) - end - end - - # De-Serializes a message using the configured encoder - def deserialize(msg) - serializer = @config.pluginconf["aes.serializer"] || "marshal" - - Log.debug("De-Serializing using #{serializer}") - - case serializer - when "yaml" - if YAML.respond_to? :safe_load - return YAML.safe_load(msg, [Symbol]) - else - raise "YAML.safe_load not supported by Ruby #{RUBY_VERSION}. Please update to Ruby 2.1+." - end - else - return Marshal.load(msg) - end - end - - # sets the caller id to the md5 of the public key - def callerid - if @initiated_by == :client - key = client_public_key - else - key = server_public_key - end - - # First try and create a X509 certificate object. If that is possible, - # we lift the callerid from the cert - begin - ssl_cert = OpenSSL::X509::Certificate.new(File.read(key)) - id = "cert=#{certname_from_certificate(ssl_cert)}" - rescue - # If the public key is not a certificate, use the file name as callerid - id = "cert=#{File.basename(key).gsub(/\.pem$/, '')}" - end - - return id - end - - def encrypt(string, certid) - if @initiated_by == :client - @ssl ||= SSL.new(client_public_key, client_private_key) - - Log.debug("Encrypting message using private key") - return @ssl.encrypt_with_private(string) - else - # when the server is initating requests like for registration - # then the certid will be our callerid - if certid == callerid - Log.debug("Encrypting message using private key #{server_private_key}") - - ssl = SSL.new(server_public_key, server_private_key) - return ssl.encrypt_with_private(string) - else - Log.debug("Encrypting message using public key for #{certid}") - - ssl = SSL.new(public_key_path_for_client(certid)) - return ssl.encrypt_with_public(string) - end - end - end - - def decrypt(string, certid) - if @initiated_by == :client - @ssl ||= SSL.new(client_public_key, client_private_key) - - Log.debug("Decrypting message using private key") - return @ssl.decrypt_with_private(string) - else - Log.debug("Decrypting message using public key for #{certid}") - ssl = SSL.new(public_key_path_for_client(certid)) - return ssl.decrypt_with_public(string) - end - end - - def validate_certificate(client_cert, certid) - cert_file = @config.pluginconf.fetch("aes.ca_cert", nil) - - begin - ssl_cert = OpenSSL::X509::Certificate.new(client_cert) - rescue OpenSSL::X509::CertificateError - Log.warn("Received public key that is not a X509 certficate") - return false - end - - ssl_certname = certname_from_certificate(ssl_cert) - - if certid != ssl_certname - Log.warn("certname '#{certid}' doesn't match certificate '#{ssl_certname}'") - return false - end - - Log.debug("Loading CA Cert for verification") - ca_cert = OpenSSL::X509::Store.new - ca_cert.add_file cert_file - - if ca_cert.verify(ssl_cert) - Log.debug("Verified certificate '#{ssl_certname}' against CA") - else - # TODO add cert id - Log.warn("Unable to validate certificate '#{ssl_certname}'' against CA") - return false - end - return true - end - - # On servers this will look in the aes.client_cert_dir for public - # keys matching the clientid, clientid is expected to be in the format - # set by callerid - def public_key_path_for_client(clientid) - raise "Unknown callerid format in '#{clientid}'" unless clientid.match(/^cert=(.+)$/) - - clientid = $1 - - client_cert_dir + "/#{clientid}.pem" - end - - # Figures out the client private key either from MCOLLECTIVE_AES_PRIVATE or the - # plugin.aes.client_private config option - def client_private_key - return ENV["MCOLLECTIVE_AES_PRIVATE"] if ENV.include?("MCOLLECTIVE_AES_PRIVATE") - - raise("No plugin.aes.client_private configuration option specified") unless @config.pluginconf.include?("aes.client_private") - - return @config.pluginconf["aes.client_private"] - end - - # Figures out the client public key either from MCOLLECTIVE_AES_PUBLIC or the - # plugin.aes.client_public config option - def client_public_key - return ENV["MCOLLECTIVE_AES_PUBLIC"] if ENV.include?("MCOLLECTIVE_AES_PUBLIC") - - raise("No plugin.aes.client_public configuration option specified") unless @config.pluginconf.include?("aes.client_public") - - return @config.pluginconf["aes.client_public"] - end - - # Figures out the server public key from the plugin.aes.server_public config option - def server_public_key - raise("No aes.server_public configuration option specified") unless @config.pluginconf.include?("aes.server_public") - return @config.pluginconf["aes.server_public"] - end - - # Figures out the server private key from the plugin.aes.server_private config option - def server_private_key - raise("No plugin.aes.server_private configuration option specified") unless @config.pluginconf.include?("aes.server_private") - @config.pluginconf["aes.server_private"] - end - - # Figures out where to get client public certs from the plugin.aes.client_cert_dir config option - def client_cert_dir - raise("No plugin.aes.client_cert_dir configuration option specified") unless @config.pluginconf.include?("aes.client_cert_dir") - @config.pluginconf["aes.client_cert_dir"] - end - - def request_description(msg) - "%s from %s@%s" % [msg[:requestid], msg[:callerid], msg[:senderid]] - end - - # Takes our cert=foo callerids and return the foo bit else nil - def certname_from_callerid(id) - if id =~ /^cert=([\w\.\-]+)/ - return $1 - else - raise("Received a callerid in an unexpected format: '#{id}', ignoring") - end - end - - def certname_from_certificate(cert) - id = cert.subject - if id.to_s =~ /^\/CN=([\w\.\-]+)/ - return $1 - else - raise("Received a callerid in an unexpected format in an SSL certificate: '#{id}', ignoring") - end - end - end - end -end diff --git a/lib/mcollective/security/psk.rb b/lib/mcollective/security/psk.rb deleted file mode 100644 index 56f59648..00000000 --- a/lib/mcollective/security/psk.rb +++ /dev/null @@ -1,117 +0,0 @@ -module MCollective - module Security - # Impliments message authentication using digests and shared keys - # - # You should configure a psk in the configuration file and all requests - # will be validated for authenticity with this. - # - # Serialization uses Marshal, this is the default security module that is - # supported out of the box. - # - # Validation is as default and is provided by MCollective::Security::Base - # - # You can configure the caller id being created, this can adjust how you - # create authorization plugins. For example you can use a unix group instead - # of uid to do authorization. - class Psk < Base - require 'etc' - - # Decodes a message by unserializing all the bits etc, it also validates - # it as valid using the psk etc - def decodemsg(msg) - body = Marshal.load(msg.payload) - - should_process_msg?(msg, body[:requestid]) - - if validrequest?(body) - body[:body] = Marshal.load(body[:body]) - return body - else - nil - end - end - - # Encodes a reply - def encodereply(sender, msg, requestid, requestcallerid=nil) - serialized = Marshal.dump(msg) - digest = makehash(serialized) - - req = create_reply(requestid, sender, serialized) - req[:hash] = digest - - Marshal.dump(req) - end - - # Encodes a request msg - def encoderequest(sender, msg, requestid, filter, target_agent, target_collective, ttl=60) - serialized = Marshal.dump(msg) - digest = makehash(serialized) - - req = create_request(requestid, filter, serialized, @initiated_by, target_agent, target_collective, ttl) - req[:hash] = digest - - Marshal.dump(req) - end - - # Checks the md5 hash in the request body against our psk, the request sent for validation - # should not have been deserialized already - def validrequest?(req) - digest = makehash(req[:body]) - - if digest == req[:hash] - @stats.validated - - return true - else - @stats.unvalidated - - raise(SecurityValidationFailed, "Received an invalid signature in message") - end - end - - def callerid - if @config.pluginconf.include?("psk.callertype") - callertype = @config.pluginconf["psk.callertype"].to_sym if @config.pluginconf.include?("psk.callertype") - else - callertype = :uid - end - - case callertype - when :gid - id = "gid=#{Process.gid}" - - when :group - raise "Cannot use the 'group' callertype for the PSK security plugin on the Windows platform" if Util.windows? - - id = "group=#{Etc.getgrgid(Process.gid).name}" - - when :user - id = "user=#{Etc.getlogin}" - - when :identity - id = "identity=#{@config.identity}" - - else - id ="uid=#{Process.uid}" - end - - Log.debug("Setting callerid to #{id} based on callertype=#{callertype}") - - id - end - - private - # Retrieves the value of plugin.psk and builds a hash with it and the passed body - def makehash(body) - if ENV.include?("MCOLLECTIVE_PSK") - psk = ENV["MCOLLECTIVE_PSK"] - else - raise("No plugin.psk configuration option specified") unless @config.pluginconf.include?("psk") - psk = @config.pluginconf["psk"] - end - - Digest::MD5.hexdigest(body.to_s + psk) - end - end - end -end diff --git a/lib/mcollective/security/ssl.rb b/lib/mcollective/security/ssl.rb deleted file mode 100644 index 9b8270eb..00000000 --- a/lib/mcollective/security/ssl.rb +++ /dev/null @@ -1,332 +0,0 @@ -require 'base64' -require 'openssl' - -module MCollective - module Security - # Impliments a public/private key based message validation system using SSL - # public and private keys. - # - # The design goal of the plugin is two fold: - # - # - give different security credentials to clients and servers to avoid - # a compromised server from sending new client requests. - # - create a token that uniquely identify the client - based on the filename - # of the public key - # - # To setup you need to create a SSL key pair that is shared by all nodes. - # - # openssl genrsa -out mcserver-private.pem 1024 - # openssl rsa -in mcserver-private.pem -out mcserver-public.pem -outform PEM -pubout - # - # Distribute the private and public file to /etc/mcollective/ssl on all the nodes. - # Distribute the public file to /etc/mcollective/ssl everywhere the client code runs. - # - # Now you should create a key pair for every one of your clients, here we create one - # for user john - you could also if you are less concerned with client id create one - # pair and share it with all clients: - # - # openssl genrsa -out john-private.pem 1024 - # openssl rsa -in john-private.pem -out john-public.pem -outform PEM -pubout - # - # Each user has a unique userid, this is based on the name of the public key. - # In this example case the userid would be 'john-public'. - # - # Store these somewhere like: - # - # /home/john/.mc/john-private.pem - # /home/john/.mc/john-public.pem - # - # Every users public key needs to be distributed to all the nodes, save the john one - # in a file called: - # - # /etc/mcollective/ssl/clients/john-public.pem - # - # If you wish to use registration or auditing that sends connections over MC to a - # central host you will need also put the server-public.pem in the clients directory. - # - # You should be aware if you do add the node public key to the clients dir you will in - # effect be weakening your overall security. You should consider doing this only if - # you also set up an Authorization method that limits the requests the nodes can make. - # - # client.cfg: - # - # securityprovider = ssl - # plugin.ssl_server_public = /etc/mcollective/ssl/server-public.pem - # plugin.ssl_client_private = /home/john/.mc/john-private.pem - # plugin.ssl_client_public = /home/john/.mc/john-public.pem - # - # If you have many clients per machine and dont want to configure the main config file - # with the public/private keys you can set the following environment variables: - # - # export MCOLLECTIVE_SSL_PRIVATE=/home/john/.mc/john-private.pem - # export MCOLLECTIVE_SSL_PUBLIC=/home/john/.mc/john-public.pem - # - # server.cfg: - # - # securityprovider = ssl - # plugin.ssl_server_private = /etc/mcollective/ssl/server-private.pem - # plugin.ssl_server_public = /etc/mcollective/ssl/server-public.pem - # plugin.ssl_client_cert_dir = /etc/mcollective/etc/ssl/clients/ - # - # # Log but accept messages that may have been tampered with - # plugin.ssl.enforce_ttl = 0 - # - # Serialization can be configured to use either Marshal or YAML, data types - # in and out of mcollective will be preserved from client to server and reverse - # - # You can configure YAML serialization: - # - # plugins.ssl_serializer = yaml - # - # else the default is Marshal. Use YAML if you wish to write a client using - # a language other than Ruby that doesn't support Marshal. - # - # Validation is as default and is provided by MCollective::Security::Base - # - # Initial code was contributed by Vladimir Vuksan and modified by R.I.Pienaar - class Ssl < Base - # Decodes a message by unserializing all the bits etc, it also validates - # it as valid using the psk etc - def decodemsg(msg) - body = deserialize(msg.payload) - - should_process_msg?(msg, body[:requestid]) - - if validrequest?(body) - body[:body] = deserialize(body[:body]) - - unless @initiated_by == :client - if body[:body].is_a?(Hash) - update_secure_property(body, :ssl_ttl, :ttl, "TTL") - update_secure_property(body, :ssl_msgtime, :msgtime, "Message Time") - - body[:body] = body[:body][:ssl_msg] if body[:body].include?(:ssl_msg) - else - unless @config.pluginconf["ssl.enforce_ttl"] == nil - raise "Message %s is in an unknown or older security protocol, ignoring" % [request_description(body)] - end - end - end - - return body - else - nil - end - end - - # To avoid tampering we turn the origin body into a hash and copy some of the protocol keys - # like :ttl and :msg_time into the hash before hashing it. - # - # This function compares and updates the unhashed ones based on the hashed ones. By - # default it enforces matching and presense by raising exceptions, if ssl.enforce_ttl is set - # to 0 it will only log warnings about violations - def update_secure_property(msg, secure_property, property, description) - req = request_description(msg) - - unless @config.pluginconf["ssl.enforce_ttl"] == "0" - raise "Request #{req} does not have a secure #{description}" unless msg[:body].include?(secure_property) - raise "Request #{req} #{description} does not match encrypted #{description} - possible tampering" unless msg[:body][secure_property] == msg[property] - else - if msg[:body].include?(secure_property) - Log.warn("Request #{req} #{description} does not match encrypted #{description} - possible tampering") unless msg[:body][secure_property] == msg[property] - else - Log.warn("Request #{req} does not have a secure #{description}") unless msg[:body].include?(secure_property) - end - end - - msg[property] = msg[:body][secure_property] if msg[:body].include?(secure_property) - msg[:body].delete(secure_property) - end - - # Encodes a reply - def encodereply(sender, msg, requestid, requestcallerid=nil) - serialized = serialize(msg) - digest = makehash(serialized) - - - req = create_reply(requestid, sender, serialized) - req[:hash] = digest - - serialize(req) - end - - # Encodes a request msg - def encoderequest(sender, msg, requestid, filter, target_agent, target_collective, ttl=60) - req = create_request(requestid, filter, "", @initiated_by, target_agent, target_collective, ttl) - - ssl_msg = {:ssl_msg => msg, - :ssl_ttl => ttl, - :ssl_msgtime => req[:msgtime]} - - serialized = serialize(ssl_msg) - digest = makehash(serialized) - - req[:hash] = digest - req[:body] = serialized - - serialize(req) - end - - # Checks the SSL signature in the request body - def validrequest?(req) - message = req[:body] - signature = req[:hash] - - Log.debug("Validating request from #{req[:callerid]}") - - if verify(public_key_file(req[:callerid]), signature, message.to_s) - @stats.validated - return true - else - @stats.unvalidated - raise(SecurityValidationFailed, "Received an invalid signature in message") - end - end - - - # sets the caller id to the md5 of the public key - def callerid - if @initiated_by == :client - id = "cert=#{File.basename(client_public_key).gsub(/\.pem$/, '')}" - raise "Invalid callerid generated from client public key" unless valid_callerid?(id) - else - # servers need to set callerid as well, not usually needed but - # would be if you're doing registration or auditing or generating - # requests for some or other reason - id = "cert=#{File.basename(server_public_key).gsub(/\.pem$/, '')}" - raise "Invalid callerid generated from server public key" unless valid_callerid?(id) - end - - return id - end - - private - # Serializes a message using the configured encoder - def serialize(msg) - serializer = @config.pluginconf["ssl_serializer"] || "marshal" - - Log.debug("Serializing using #{serializer}") - - case serializer - when "yaml" - return YAML.dump(msg) - else - return Marshal.dump(msg) - end - end - - # De-Serializes a message using the configured encoder - def deserialize(msg) - serializer = @config.pluginconf["ssl_serializer"] || "marshal" - - Log.debug("De-Serializing using #{serializer}") - - case serializer - when "yaml" - if YAML.respond_to? :safe_load - return YAML.safe_load(msg, [Symbol]) - else - raise "YAML.safe_load not supported by Ruby #{RUBY_VERSION}. Please update to Ruby 2.1+." - end - else - return Marshal.load(msg) - end - end - - # Figures out where to get our private key - def private_key_file - if ENV.include?("MCOLLECTIVE_SSL_PRIVATE") - return ENV["MCOLLECTIVE_SSL_PRIVATE"] - else - if @initiated_by == :node - return server_private_key - else - return client_private_key - end - end - end - - # Figures out the public key to use - # - # If the node is asking do it based on caller id - # If the client is asking just get the node public key - def public_key_file(callerid = nil) - if @initiated_by == :client - return server_public_key - else - if callerid =~ /cert=([\w\.\-]+)/ - cid = $1 - - if File.exist?("#{client_cert_dir}/#{cid}.pem") - return "#{client_cert_dir}/#{cid}.pem" - else - raise("Could not find a public key for #{cid} in #{client_cert_dir}/#{cid}.pem") - end - else - raise("Caller id is not in the expected format") - end - end - end - - # Figures out the client private key either from MCOLLECTIVE_SSL_PRIVATE or the - # plugin.ssl_client_private config option - def client_private_key - return ENV["MCOLLECTIVE_SSL_PRIVATE"] if ENV.include?("MCOLLECTIVE_SSL_PRIVATE") - - raise("No plugin.ssl_client_private configuration option specified") unless @config.pluginconf.include?("ssl_client_private") - - return @config.pluginconf["ssl_client_private"] - end - - # Figures out the client public key either from MCOLLECTIVE_SSL_PUBLIC or the - # plugin.ssl_client_public config option - def client_public_key - return ENV["MCOLLECTIVE_SSL_PUBLIC"] if ENV.include?("MCOLLECTIVE_SSL_PUBLIC") - - raise("No plugin.ssl_client_public configuration option specified") unless @config.pluginconf.include?("ssl_client_public") - - return @config.pluginconf["ssl_client_public"] - end - - # Figures out the server private key from the plugin.ssl_server_private config option - def server_private_key - raise("No plugin.ssl_server_private configuration option specified") unless @config.pluginconf.include?("ssl_server_private") - @config.pluginconf["ssl_server_private"] - end - - # Figures out the server public key from the plugin.ssl_server_public config option - def server_public_key - raise("No ssl_server_public configuration option specified") unless @config.pluginconf.include?("ssl_server_public") - return @config.pluginconf["ssl_server_public"] - end - - # Figures out where to get client public certs from the plugin.ssl_client_cert_dir config option - def client_cert_dir - raise("No plugin.ssl_client_cert_dir configuration option specified") unless @config.pluginconf.include?("ssl_client_cert_dir") - @config.pluginconf["ssl_client_cert_dir"] - end - - # Retrieves the value of plugin.psk and builds a hash with it and the passed body - def makehash(body) - Log.debug("Creating message hash using #{private_key_file}") - - sign(private_key_file, body.to_s) - end - - # Code adapted from http://github.com/adamcooke/basicssl - # signs a message - def sign(key, string) - SSL.new(nil, key).sign(string, true) - end - - # verifies a signature - def verify(key, signature, string) - SSL.new(key).verify_signature(signature, string, true) - end - - def request_description(msg) - "%s from %s@%s" % [msg[:requestid], msg[:callerid], msg[:senderid]] - end - end - end -end diff --git a/lib/mcollective/unix_daemon.rb b/lib/mcollective/unix_daemon.rb deleted file mode 100644 index 2fbd53e8..00000000 --- a/lib/mcollective/unix_daemon.rb +++ /dev/null @@ -1,70 +0,0 @@ -module MCollective - class UnixDaemon - # Daemonize the current process - def self.daemonize - fork do - Process.setsid - exit if fork - Dir.chdir('/tmp') - STDIN.reopen('/dev/null') - STDOUT.reopen('/dev/null', 'a') - STDERR.reopen('/dev/null', 'a') - - yield - end - end - - def self.daemonize_runner(pid=nil) - raise "The Unix Daemonizer can not be used on the Windows Platform" if Util.windows? - - UnixDaemon.daemonize do - if pid - # Clean up stale pidfile if needed - if File.exist?(pid) - lock_pid = File.read(pid) - begin - lock_pid = Integer(lock_pid) - rescue ArgumentError, TypeError - lock_pid = nil - end - - # If there's no pid in the pidfile, remove it - if lock_pid.nil? - File.unlink(pid) - else - begin - # This will raise an error if the process doesn't - # exist, and do nothing otherwise - Process.kill(0, lock_pid) - # If we reach this point then the process is running. - # We should raise an error rather than continuing on - # trying to create the PID - raise "Process is already running with PID #{lock_pid}" - rescue Errno::ESRCH - # Errno::ESRCH = no such process - # PID in pidfile doesn't exist, remove pidfile - File.unlink(pid) - end - end - - end - - # Use exclusive create on the PID to avoid race condition - # when two mcollectived processes start at the same time - opt = File::CREAT | File::EXCL | File::WRONLY - File.open(pid, opt) {|f| f.print(Process.pid) } - end - - begin - runner = Runner.new(nil) - runner.main_loop - rescue => e - Log.warn(e.backtrace) - Log.warn(e) - ensure - File.unlink(pid) if pid && File.exist?(pid) - end - end - end - end -end diff --git a/lib/mcollective/vendor.rb b/lib/mcollective/vendor.rb deleted file mode 100644 index 04514670..00000000 --- a/lib/mcollective/vendor.rb +++ /dev/null @@ -1,41 +0,0 @@ -module MCollective - # Simple module to manage vendored code. - # - # To vendor a library simply download its whole git repo or untar - # into vendor/libraryname and create a load_libraryname.rb file - # to add its libdir into the $:. - # - # Once you have that file, add a require line in vendor/require_vendored.rb - # which will run after all the load_* files. - # - # The intention is to not change vendored libraries and to eventually - # make adding them in optional so that distros can simply adjust their - # packaging to exclude this directory and the various load_xxx.rb scripts - # if they wish to install these gems as native packages. - class Vendor - class << self - def vendor_dir - File.join([File.dirname(File.expand_path(__FILE__)), "vendor"]) - end - - def load_entry(entry) - Log.debug("Loading vendored #{$1}") - load "#{vendor_dir}/#{entry}" - end - - def require_libs - require 'mcollective/vendor/require_vendored' - end - - def load_vendored - Dir.entries(vendor_dir).each do |entry| - if entry.match(/load_(\w+?)\.rb$/) - load_entry entry - end - end - - require_libs - end - end - end -end diff --git a/lib/mcollective/vendor/load_systemu.rb b/lib/mcollective/vendor/load_systemu.rb deleted file mode 100644 index 39626866..00000000 --- a/lib/mcollective/vendor/load_systemu.rb +++ /dev/null @@ -1 +0,0 @@ -$: << File.join([File.dirname(__FILE__), "systemu/lib"]) diff --git a/lib/mcollective/vendor/require_vendored.rb b/lib/mcollective/vendor/require_vendored.rb deleted file mode 100644 index 036ff3ed..00000000 --- a/lib/mcollective/vendor/require_vendored.rb +++ /dev/null @@ -1 +0,0 @@ -require 'systemu' diff --git a/lib/mcollective/vendor/systemu/BSDL b/lib/mcollective/vendor/systemu/BSDL deleted file mode 100755 index 654ce3d7..00000000 --- a/lib/mcollective/vendor/systemu/BSDL +++ /dev/null @@ -1,23 +0,0 @@ -Copyright (c) 2010, Ara T. Howard -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: - -* Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. - -* Redistributions in binary form must reproduce the above copyright notice, - this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" -AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE -FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR -SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, -OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/lib/mcollective/vendor/systemu/LICENSE b/lib/mcollective/vendor/systemu/LICENSE deleted file mode 100755 index fbe7e193..00000000 --- a/lib/mcollective/vendor/systemu/LICENSE +++ /dev/null @@ -1,57 +0,0 @@ -systemu is copyrighted free software by Ara T. Howard . -You can redistribute it and/or modify it under either the terms of the -2-clause BSDL (see the file BSDL), or the conditions below: - - 1. You may make and give away verbatim copies of the source form of the - software without restriction, provided that you duplicate all of the - original copyright notices and associated disclaimers. - - 2. You may modify your copy of the software in any way, provided that - you do at least ONE of the following: - - a) place your modifications in the Public Domain or otherwise - make them Freely Available, such as by posting said - modifications to Usenet or an equivalent medium, or by allowing - the author to include your modifications in the software. - - b) use the modified software only within your corporation or - organization. - - c) give non-standard binaries non-standard names, with - instructions on where to get the original software distribution. - - d) make other distribution arrangements with the author. - - 3. You may distribute the software in object code or binary form, - provided that you do at least ONE of the following: - - a) distribute the binaries and library files of the software, - together with instructions (in the manual page or equivalent) - on where to get the original distribution. - - b) accompany the distribution with the machine-readable source of - the software. - - c) give non-standard binaries non-standard names, with - instructions on where to get the original software distribution. - - d) make other distribution arrangements with the author. - - 4. You may modify and include the part of the software into any other - software (possibly commercial). But some files in the distribution - are not written by the author, so that they are not under these terms. - - For the list of those files and their copying conditions, see the - file LEGAL. - - 5. The scripts and library files supplied as input to or produced as - output from the software do not automatically fall under the - copyright of the software, but belong to whomever generated them, - and may be sold commercially, and may be aggregated with this - software. - - 6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR - IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED - WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR - PURPOSE. - diff --git a/lib/mcollective/vendor/systemu/README b/lib/mcollective/vendor/systemu/README deleted file mode 100755 index 978f24f0..00000000 --- a/lib/mcollective/vendor/systemu/README +++ /dev/null @@ -1,170 +0,0 @@ -NAME - - systemu - -SYNOPSIS - - universal capture of stdout and stderr and handling of child process pid for - windows, *nix, etc. - -URIS - - http://github.com/ahoward/systemu - http://rubyforge.org/projects/codeforpeople/ - -INSTALL - - gem install systemu - -HISTORY - 2.0.0 - - versioning issue. new gem release. - - 1.3.1 - - updates for ruby 1.9.1 - - 1.3.0 - - move to github - - 1.2.0 - - - fixed handling of background thread management - needed - Thread.current.abort_on_exception = true - - - fixed reporting of child pid, it was reported as the parent's pid before - -SAMPLES - - - <========< samples/a.rb >========> - - ~ > cat samples/a.rb - - # - # systemu can be used on any platform to return status, stdout, and stderr of - # any command. unlike other methods like open3/popen4 there is zero danger of - # full pipes or threading issues hanging your process or subprocess. - # - require 'systemu' - - date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " ) - - status, stdout, stderr = systemu date - p [ status, stdout, stderr ] - - ~ > ruby samples/a.rb - - [#, "2011-12-11 22:07:30 -0700\n", "2011-12-11 22:07:30 -0700\n"] - - - <========< samples/b.rb >========> - - ~ > cat samples/b.rb - - # - # quite a few keys can be passed to the command to alter it's behaviour. if - # either stdout or stderr is supplied those objects should respond_to? '<<' - # and only status will be returned - # - require 'systemu' - - date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " ) - - stdout, stderr = '', '' - status = systemu date, 'stdout' => stdout, 'stderr' => stderr - p [ status, stdout, stderr ] - - ~ > ruby samples/b.rb - - [#, "2011-12-11 22:07:30 -0700\n", "2011-12-11 22:07:30 -0700\n"] - - - <========< samples/c.rb >========> - - ~ > cat samples/c.rb - - # - # of course stdin can be supplied too. synonyms for 'stdin' include '0' and - # 0. the other stdio streams have similar shortcuts - # - require 'systemu' - - cat = %q( ruby -e" ARGF.each{|line| puts line} " ) - - status = systemu cat, 0=>'the stdin for cat', 1=>stdout='' - puts stdout - - ~ > ruby samples/c.rb - - the stdin for cat - - - <========< samples/d.rb >========> - - ~ > cat samples/d.rb - - # - # the cwd can be supplied - # - require 'systemu' - require 'tmpdir' - - pwd = %q( ruby -e" STDERR.puts Dir.pwd " ) - - status = systemu pwd, 2=>(stderr=''), :cwd=>Dir.tmpdir - puts stderr - - - ~ > ruby samples/d.rb - - /private/var/folders/sp/nwtflj890qnb6z4b53dqxvlw0000gp/T - - - <========< samples/e.rb >========> - - ~ > cat samples/e.rb - - # - # any environment vars specified are merged into the child's environment - # - require 'systemu' - - env = %q( ruby -r yaml -e" puts ENV[ 'answer' ] " ) - - status = systemu env, 1=>stdout='', 'env'=>{ 'answer' => 0b101010 } - puts stdout - - ~ > ruby samples/e.rb - - 42 - - - <========< samples/f.rb >========> - - ~ > cat samples/f.rb - - # - # if a block is specified then it is passed the child pid and run in a - # background thread. note that this thread will __not__ be blocked during the - # execution of the command so it may do useful work such as killing the child - # if execution time passes a certain threshold - # - require 'systemu' - - looper = %q( ruby -e" loop{ STDERR.puts Time.now.to_i; sleep 1 } " ) - - status, stdout, stderr = - systemu looper do |cid| - sleep 3 - Process.kill 9, cid - end - - p status - p stderr - - ~ > ruby samples/f.rb - - # - "1323666451\n1323666452\n1323666453\n" - - diff --git a/lib/mcollective/vendor/systemu/README.erb b/lib/mcollective/vendor/systemu/README.erb deleted file mode 100755 index ab04eff1..00000000 --- a/lib/mcollective/vendor/systemu/README.erb +++ /dev/null @@ -1,37 +0,0 @@ -NAME - - systemu - -SYNOPSIS - - univeral capture of stdout and stderr and handling of child process pid for windows, *nix, etc. - -URIS - - http://github.com/ahoward/systemu - http://rubyforge.org/projects/codeforpeople/ - -INSTALL - - gem install systemu - -HISTORY - 2.0.0 - - versioning issue. new gem release. - - 1.3.1 - - updates for ruby 1.9.1 - - 1.3.0 - - move to github - - 1.2.0 - - - fixed handling of background thread management - needed - Thread.current.abort_on_exception = true - - - fixed reporting of child pid, it was reported as the parent's pid before - -SAMPLES - -<%= samples %> diff --git a/lib/mcollective/vendor/systemu/Rakefile b/lib/mcollective/vendor/systemu/Rakefile deleted file mode 100755 index bce655c5..00000000 --- a/lib/mcollective/vendor/systemu/Rakefile +++ /dev/null @@ -1,394 +0,0 @@ -This.rubyforge_project = 'codeforpeople' -This.author = "Ara T. Howard" -This.email = "ara.t.howard@gmail.com" -This.homepage = "https://github.com/ahoward/#{ This.lib }" - -task :license do - open('LICENSE', 'w'){|fd| fd.puts "Ruby"} -end - -task :default do - puts((Rake::Task.tasks.map{|task| task.name.gsub(/::/,':')} - ['default']).sort) -end - -task :test do - run_tests! -end - -namespace :test do - task(:unit){ run_tests!(:unit) } - task(:functional){ run_tests!(:functional) } - task(:integration){ run_tests!(:integration) } -end - -def run_tests!(which = nil) - which ||= '**' - test_dir = File.join(This.dir, "test") - test_glob ||= File.join(test_dir, "#{ which }/**_test.rb") - test_rbs = Dir.glob(test_glob).sort - - div = ('=' * 119) - line = ('-' * 119) - - test_rbs.each_with_index do |test_rb, index| - testno = index + 1 - command = "#{ This.ruby } -w -I ./lib -I ./test/lib #{ test_rb }" - - puts - say(div, :color => :cyan, :bold => true) - say("@#{ testno } => ", :bold => true, :method => :print) - say(command, :color => :cyan, :bold => true) - say(line, :color => :cyan, :bold => true) - - system(command) - - say(line, :color => :cyan, :bold => true) - - status = $?.exitstatus - - if status.zero? - say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print) - say("SUCCESS", :color => :green, :bold => true) - else - say("@#{ testno } <= ", :bold => true, :color => :white, :method => :print) - say("FAILURE", :color => :red, :bold => true) - end - say(line, :color => :cyan, :bold => true) - - exit(status) unless status.zero? - end -end - - -task :gemspec do - ignore_extensions = ['git', 'svn', 'tmp', /sw./, 'bak', 'gem'] - ignore_directories = ['pkg'] - ignore_files = ['test/log'] - - shiteless = - lambda do |list| - list.delete_if do |entry| - next unless test(?e, entry) - extension = File.basename(entry).split(%r/[.]/).last - ignore_extensions.any?{|ext| ext === extension} - end - list.delete_if do |entry| - next unless test(?d, entry) - dirname = File.expand_path(entry) - ignore_directories.any?{|dir| File.expand_path(dir) == dirname} - end - list.delete_if do |entry| - next unless test(?f, entry) - filename = File.expand_path(entry) - ignore_files.any?{|file| File.expand_path(file) == filename} - end - end - - lib = This.lib - object = This.object - version = This.version - files = shiteless[Dir::glob("**/**")] - executables = shiteless[Dir::glob("bin/*")].map{|exe| File.basename(exe)} - #has_rdoc = true #File.exist?('doc') - test_files = "test/#{ lib }.rb" if File.file?("test/#{ lib }.rb") - summary = object.respond_to?(:summary) ? object.summary : "summary: #{ lib } kicks the ass" - description = object.respond_to?(:description) ? object.description : "description: #{ lib } kicks the ass" - license = object.respond_to?(:license) ? object.license : "Ruby" - - if This.extensions.nil? - This.extensions = [] - extensions = This.extensions - %w( Makefile configure extconf.rb ).each do |ext| - extensions << ext if File.exists?(ext) - end - end - extensions = [extensions].flatten.compact - - if This.dependencies.nil? - dependencies = [] - else - case This.dependencies - when Hash - dependencies = This.dependencies.values - when Array - dependencies = This.dependencies - end - end - - template = - if test(?e, 'gemspec.erb') - Template{ IO.read('gemspec.erb') } - else - Template { - <<-__ - ## <%= lib %>.gemspec - # - - Gem::Specification::new do |spec| - spec.name = <%= lib.inspect %> - spec.version = <%= version.inspect %> - spec.platform = Gem::Platform::RUBY - spec.summary = <%= lib.inspect %> - spec.description = <%= description.inspect %> - spec.license = <%= license.inspect %> - - spec.files =\n<%= files.sort.pretty_inspect %> - spec.executables = <%= executables.inspect %> - - spec.require_path = "lib" - - spec.test_files = <%= test_files.inspect %> - - <% dependencies.each do |lib_version| %> - spec.add_dependency(*<%= Array(lib_version).flatten.inspect %>) - <% end %> - - spec.extensions.push(*<%= extensions.inspect %>) - - spec.rubyforge_project = <%= This.rubyforge_project.inspect %> - spec.author = <%= This.author.inspect %> - spec.email = <%= This.email.inspect %> - spec.homepage = <%= This.homepage.inspect %> - end - __ - } - end - - Fu.mkdir_p(This.pkgdir) - gemspec = "#{ lib }.gemspec" - open(gemspec, "w"){|fd| fd.puts(template)} - This.gemspec = gemspec -end - -task :gem => [:clean, :gemspec] do - Fu.mkdir_p(This.pkgdir) - before = Dir['*.gem'] - cmd = "gem build #{ This.gemspec }" - `#{ cmd }` - after = Dir['*.gem'] - gem = ((after - before).first || after.first) or abort('no gem!') - Fu.mv(gem, This.pkgdir) - This.gem = File.join(This.pkgdir, File.basename(gem)) -end - -task :readme do - samples = '' - prompt = '~ > ' - lib = This.lib - version = This.version - - Dir['sample*/*'].sort.each do |sample| - samples << "\n" << " <========< #{ sample } >========>" << "\n\n" - - cmd = "cat #{ sample }" - samples << Util.indent(prompt + cmd, 2) << "\n\n" - samples << Util.indent(`#{ cmd }`, 4) << "\n" - - cmd = "ruby #{ sample }" - samples << Util.indent(prompt + cmd, 2) << "\n\n" - - cmd = "ruby -e'STDOUT.sync=true; exec %(ruby -I ./lib #{ sample })'" - samples << Util.indent(`#{ cmd } 2>&1`, 4) << "\n" - end - - template = - if test(?e, 'README.erb') - Template{ IO.read('README.erb') } - else - Template { - <<-__ - NAME - #{ lib } - - DESCRIPTION - - INSTALL - gem install #{ lib } - - SAMPLES - #{ samples } - __ - } - end - - open("README", "w"){|fd| fd.puts template} -end - - -task :clean do - Dir[File.join(This.pkgdir, '**/**')].each{|entry| Fu.rm_rf(entry)} -end - - -task :release => [:clean, :gemspec, :gem] do - gems = Dir[File.join(This.pkgdir, '*.gem')].flatten - raise "which one? : #{ gems.inspect }" if gems.size > 1 - raise "no gems?" if gems.size < 1 - - cmd = "gem push #{ This.gem }" - puts cmd - puts - system(cmd) - abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero? - - cmd = "rubyforge login && rubyforge add_release #{ This.rubyforge_project } #{ This.lib } #{ This.version } #{ This.gem }" - puts cmd - puts - system(cmd) - abort("cmd(#{ cmd }) failed with (#{ $?.inspect })") unless $?.exitstatus.zero? -end - - - - - -BEGIN { -# support for this rakefile -# - $VERBOSE = nil - - require 'ostruct' - require 'erb' - require 'fileutils' - require 'rbconfig' - require 'pp' - -# fu shortcut -# - Fu = FileUtils - -# cache a bunch of stuff about this rakefile/environment -# - This = OpenStruct.new - - This.file = File.expand_path(__FILE__) - This.dir = File.dirname(This.file) - This.pkgdir = File.join(This.dir, 'pkg') - -# grok lib -# - lib = ENV['LIB'] - unless lib - lib = File.basename(Dir.pwd).sub(/[-].*$/, '') - end - This.lib = lib - -# grok version -# - version = ENV['VERSION'] - unless version - require "./lib/#{ This.lib }" - This.name = lib.capitalize - This.object = eval(This.name) - version = This.object.send(:version) - end - This.version = version - -# see if dependencies are export by the module -# - if This.object.respond_to?(:dependencies) - This.dependencies = This.object.dependencies - end - -# we need to know the name of the lib an it's version -# - abort('no lib') unless This.lib - abort('no version') unless This.version - -# discover full path to this ruby executable -# - c = Config::CONFIG - bindir = c["bindir"] || c['BINDIR'] - ruby_install_name = c['ruby_install_name'] || c['RUBY_INSTALL_NAME'] || 'ruby' - ruby_ext = c['EXEEXT'] || '' - ruby = File.join(bindir, (ruby_install_name + ruby_ext)) - This.ruby = ruby - -# some utils -# - module Util - def indent(s, n = 2) - s = unindent(s) - ws = ' ' * n - s.gsub(%r/^/, ws) - end - - def unindent(s) - indent = nil - s.each_line do |line| - next if line =~ %r/^\s*$/ - indent = line[%r/^\s*/] and break - end - indent ? s.gsub(%r/^#{ indent }/, "") : s - end - extend self - end - -# template support -# - class Template - def initialize(&block) - @block = block - @template = block.call.to_s - end - def expand(b=nil) - ERB.new(Util.unindent(@template)).result((b||@block).binding) - end - alias_method 'to_s', 'expand' - end - def Template(*args, &block) Template.new(*args, &block) end - -# colored console output support -# - This.ansi = { - :clear => "\e[0m", - :reset => "\e[0m", - :erase_line => "\e[K", - :erase_char => "\e[P", - :bold => "\e[1m", - :dark => "\e[2m", - :underline => "\e[4m", - :underscore => "\e[4m", - :blink => "\e[5m", - :reverse => "\e[7m", - :concealed => "\e[8m", - :black => "\e[30m", - :red => "\e[31m", - :green => "\e[32m", - :yellow => "\e[33m", - :blue => "\e[34m", - :magenta => "\e[35m", - :cyan => "\e[36m", - :white => "\e[37m", - :on_black => "\e[40m", - :on_red => "\e[41m", - :on_green => "\e[42m", - :on_yellow => "\e[43m", - :on_blue => "\e[44m", - :on_magenta => "\e[45m", - :on_cyan => "\e[46m", - :on_white => "\e[47m" - } - def say(phrase, *args) - options = args.last.is_a?(Hash) ? args.pop : {} - options[:color] = args.shift.to_s.to_sym unless args.empty? - keys = options.keys - keys.each{|key| options[key.to_s.to_sym] = options.delete(key)} - - color = options[:color] - bold = options.has_key?(:bold) - - parts = [phrase] - parts.unshift(This.ansi[color]) if color - parts.unshift(This.ansi[:bold]) if bold - parts.push(This.ansi[:clear]) if parts.size > 1 - - method = options[:method] || :puts - - Kernel.send(method, parts.join) - end - -# always run out of the project dir -# - Dir.chdir(This.dir) -} diff --git a/lib/mcollective/vendor/systemu/lib/systemu.rb b/lib/mcollective/vendor/systemu/lib/systemu.rb deleted file mode 100755 index 5f7745a1..00000000 --- a/lib/mcollective/vendor/systemu/lib/systemu.rb +++ /dev/null @@ -1,379 +0,0 @@ -# encoding: utf-8 - -require 'tmpdir' -require 'socket' -require 'fileutils' -require 'rbconfig' -require 'thread' - -class Object - def systemu(*a, &b) SystemUniversal.new(*a, &b).systemu end -end - -class SystemUniversal -# -# constants -# - SystemUniversal::VERSION = '2.6.5' unless SystemUniversal.send(:const_defined?, :VERSION) - def SystemUniversal.version() SystemUniversal::VERSION end - def version() SystemUniversal::VERSION end - def SystemUniversal.description - "universal capture of stdout and stderr and handling of child process pid for windows, *nix, etc." - end -# -# class methods -# - - @host = Socket.gethostname - @ppid = Process.ppid - @pid = Process.pid - @turd = ENV['SYSTEMU_TURD'] - @ruby = nil - - def self.ruby - return @ruby if @ruby - - c = begin; ::RbConfig::CONFIG; rescue NameError; ::Config::CONFIG; end - ruby = File.join(c['bindir'], c['ruby_install_name']) << c['EXEEXT'] - @ruby = if system(ruby, '-e', '42') - ruby - else - system('ruby', '-e', '42') ? 'ruby' : warn('no ruby in PATH/CONFIG') - end - end - - class << SystemUniversal - %w( host ppid pid turd ).each{|a| attr_accessor a} - - def quote(*words) - words.map{|word| word.inspect}.join(' ') - end - end - -# -# instance methods -# - - def initialize argv, opts = {}, &block - getopt = getopts opts - - @argv = argv - @block = block - - @stdin = getopt[ ['stdin', 'in', '0', 0] ] - @stdout = getopt[ ['stdout', 'out', '1', 1] ] - @stderr = getopt[ ['stderr', 'err', '2', 2] ] - @env = getopt[ 'env' ] - @cwd = getopt[ 'cwd' ] - - @host = getopt[ 'host', self.class.host ] - @ppid = getopt[ 'ppid', self.class.ppid ] - @pid = getopt[ 'pid', self.class.pid ] - @ruby = getopt[ 'ruby', self.class.ruby ] - end - - def systemu - tmpdir do |tmp| - c = child_setup tmp - status = nil - - begin - thread = nil - - quietly{ - IO.popen "#{ quote(@ruby) } #{ quote(c['program']) }", 'rb+' do |pipe| - line = pipe.gets - case line - when %r/^pid: \d+$/ - cid = Integer line[%r/\d+/] - else - begin - buf = pipe.read - buf = "#{ line }#{ buf }" - e = Marshal.load buf - raise unless Exception === e - raise e - rescue - raise "systemu: Error - process interrupted!\n#{ buf }\n" - end - end - thread = new_thread cid, @block if @block - pipe.read rescue nil - end - } - status = $? - ensure - if thread - begin - class << status - attr 'thread' - end - status.instance_eval{ @thread = thread } - rescue - 42 - end - end - end - - if @stdout or @stderr - open(c['stdout'], 'rb'){|f| relay f => @stdout} if @stdout - open(c['stderr'], 'rb'){|f| relay f => @stderr} if @stderr - status - else - [status, open(c['stdout'], 'rb'){|f| f.read}, open(c['stderr'], 'rb'){|f| f.read}] - end - end - end - - def quote *args, &block - SystemUniversal.quote(*args, &block) - end - - def new_thread child_pid, block - q = Queue.new - Thread.new(child_pid) do |cid| - current = Thread.current - current.abort_on_exception = true - q.push current - block.call cid - end - q.pop - end - - def child_setup tmp - stdin = File.expand_path(File.join(tmp, 'stdin')) - stdout = File.expand_path(File.join(tmp, 'stdout')) - stderr = File.expand_path(File.join(tmp, 'stderr')) - program = File.expand_path(File.join(tmp, 'program')) - config = File.expand_path(File.join(tmp, 'config')) - - if @stdin - open(stdin, 'wb'){|f| relay @stdin => f} - else - FileUtils.touch stdin - end - FileUtils.touch stdout - FileUtils.touch stderr - - c = {} - c['argv'] = @argv - c['env'] = @env - c['cwd'] = @cwd - c['stdin'] = stdin - c['stdout'] = stdout - c['stderr'] = stderr - c['program'] = program - open(config, 'wb'){|f| Marshal.dump(c, f)} - - open(program, 'wb'){|f| f.write child_program(config)} - - c - end - - def quietly - v = $VERBOSE - $VERBOSE = nil - yield - ensure - $VERBOSE = v - end - - def child_program config - <<-program - # encoding: utf-8 - - PIPE = STDOUT.dup - begin - config = Marshal.load(IO.read('#{ config }',:mode=>"rb")) - - argv = config['argv'] - env = config['env'] - cwd = config['cwd'] - stdin = config['stdin'] - stdout = config['stdout'] - stderr = config['stderr'] - - Dir.chdir cwd if cwd - env.each{|k,v| ENV[k.to_s] = v.to_s} if env - - STDIN.reopen stdin - STDOUT.reopen stdout - STDERR.reopen stderr - - PIPE.puts "pid: \#{ Process.pid }" - PIPE.flush ### the process is ready yo! - PIPE.close - - exec *argv - rescue Exception => e - PIPE.write Marshal.dump(e) rescue nil - exit 42 - end - program - end - - def relay srcdst - src, dst, _ = srcdst.to_a.first - if src.respond_to? 'read' - while((buffer = src.read(8192))); dst << buffer; end - else - if src.respond_to?(:each_line) - src.each_line{|buf| dst << buf} - else - src.each{|buf| dst << buf} - end - end - end - - def slug_for(*args) - options = args.last.is_a?(Hash) ? args.pop : {} - join = (options[:join] || options['join'] || '_').to_s - string = args.flatten.compact.join(join) - words = string.to_s.scan(%r|[/\w]+|) - words.map!{|word| word.gsub %r|[^/0-9a-zA-Z_-]|, ''} - words.delete_if{|word| word.nil? or word.strip.empty?} - words.join(join).downcase.gsub('/', (join * 2)) - end - - def tmpdir d = Dir.tmpdir, max = 42, &b - i = -1 and loop{ - i += 1 - - tmp = File.join(d, slug_for("systemu_#{ @host }_#{ @ppid }_#{ @pid }_#{ rand }_#{ i += 1 }")) - - begin - Dir.mkdir tmp - rescue Errno::EEXIST - raise if i >= max - next - end - - break( - if b - begin - b.call tmp - ensure - FileUtils.rm_rf tmp unless SystemU.turd - end - else - tmp - end - ) - } - end - - def getopts opts = {} - lambda do |*args| - keys, default, _ = args - catch(:opt) do - [keys].flatten.each do |key| - [key, key.to_s, key.to_s.intern].each do |k| - throw :opt, opts[k] if opts.has_key?(k) - end - end - default - end - end - end -end - -# some monkeypatching for JRuby -if defined? JRUBY_VERSION - require 'jruby' - java_import org.jruby.RubyProcess - - class SystemUniversal - def systemu - split_argv = JRuby::PathHelper.smart_split_command @argv - process = java.lang.Runtime.runtime.exec split_argv.to_java(:string) - - stdout, stderr = [process.input_stream, process.error_stream].map do |stream| - StreamReader.new(stream) - end - - field = process.get_class.get_declared_field("pid") - field.set_accessible(true) - pid = field.get(process) - thread = new_thread pid, @block if @block - exit_code = process.wait_for - [ - RubyProcess::RubyStatus.new_process_status(JRuby.runtime, exit_code, pid), - stdout.join, - stderr.join - ] - end - - class StreamReader - def initialize(stream) - @data = "" - @thread = Thread.new do - reader = java.io.BufferedReader.new java.io.InputStreamReader.new(stream) - - while line = reader.read_line - @data << line << "\n" - end - end - end - - def join - @thread.join - @data - end - end - end -end - - - -SystemU = SystemUniversal unless defined? SystemU -Systemu = SystemUniversal unless defined? Systemu - - - - - - - - - - - - - -if $0 == __FILE__ -# -# date -# - date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " ) - - status, stdout, stderr = systemu date - p [status, stdout, stderr] - - status = systemu date, 1=>(stdout = '') - p [status, stdout] - - status = systemu date, 2=>(stderr = '') - p [status, stderr] -# -# sleep -# - sleep = %q( ruby -e" p(sleep(1)) " ) - status, stdout, stderr = systemu sleep - p [status, stdout, stderr] - - sleep = %q( ruby -e" p(sleep(42)) " ) - status, stdout, stderr = systemu(sleep){|cid| Process.kill 9, cid} - p [status, stdout, stderr] -# -# env -# - env = %q( ruby -e" p ENV['A'] " ) - status, stdout, stderr = systemu env, :env => {'A' => 42} - p [status, stdout, stderr] -# -# cwd -# - env = %q( ruby -e" p Dir.pwd " ) - status, stdout, stderr = systemu env, :cwd => Dir.tmpdir - p [status, stdout, stderr] -end diff --git a/lib/mcollective/vendor/systemu/samples/a.rb b/lib/mcollective/vendor/systemu/samples/a.rb deleted file mode 100755 index 37af06ab..00000000 --- a/lib/mcollective/vendor/systemu/samples/a.rb +++ /dev/null @@ -1,11 +0,0 @@ -# -# systemu can be used on any platform to return status, stdout, and stderr of -# any command. unlike other methods like open3/popen4 there is zero danger of -# full pipes or threading issues hanging your process or subprocess. -# - require 'systemu' - - date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " ) - - status, stdout, stderr = systemu date - p [ status, stdout, stderr ] diff --git a/lib/mcollective/vendor/systemu/samples/b.rb b/lib/mcollective/vendor/systemu/samples/b.rb deleted file mode 100755 index 951dce11..00000000 --- a/lib/mcollective/vendor/systemu/samples/b.rb +++ /dev/null @@ -1,12 +0,0 @@ -# -# quite a few keys can be passed to the command to alter it's behaviour. if -# either stdout or stderr is supplied those objects should respond_to? '<<' -# and only status will be returned -# - require 'systemu' - - date = %q( ruby -e" t = Time.now; STDOUT.puts t; STDERR.puts t " ) - - stdout, stderr = '', '' - status = systemu date, 'stdout' => stdout, 'stderr' => stderr - p [ status, stdout, stderr ] diff --git a/lib/mcollective/vendor/systemu/samples/c.rb b/lib/mcollective/vendor/systemu/samples/c.rb deleted file mode 100755 index c3ffc544..00000000 --- a/lib/mcollective/vendor/systemu/samples/c.rb +++ /dev/null @@ -1,10 +0,0 @@ -# -# of course stdin can be supplied too. synonyms for 'stdin' include '0' and -# 0. the other stdio streams have similar shortcuts -# - require 'systemu' - - cat = %q( ruby -e" ARGF.each{|line| puts line} " ) - - status = systemu cat, 0=>'the stdin for cat', 1=>stdout='' - puts stdout diff --git a/lib/mcollective/vendor/systemu/samples/d.rb b/lib/mcollective/vendor/systemu/samples/d.rb deleted file mode 100755 index 84d4ae9e..00000000 --- a/lib/mcollective/vendor/systemu/samples/d.rb +++ /dev/null @@ -1,11 +0,0 @@ -# -# the cwd can be supplied -# - require 'systemu' - require 'tmpdir' - - pwd = %q( ruby -e" STDERR.puts Dir.pwd " ) - - status = systemu pwd, 2=>(stderr=''), :cwd=>Dir.tmpdir - puts stderr - diff --git a/lib/mcollective/vendor/systemu/samples/e.rb b/lib/mcollective/vendor/systemu/samples/e.rb deleted file mode 100755 index 2c26e623..00000000 --- a/lib/mcollective/vendor/systemu/samples/e.rb +++ /dev/null @@ -1,9 +0,0 @@ -# -# any environment vars specified are merged into the child's environment -# - require 'systemu' - - env = %q( ruby -r yaml -e" puts ENV[ 'answer' ] " ) - - status = systemu env, 1=>stdout='', 'env'=>{ 'answer' => 0b101010 } - puts stdout diff --git a/lib/mcollective/vendor/systemu/samples/f.rb b/lib/mcollective/vendor/systemu/samples/f.rb deleted file mode 100755 index 158301de..00000000 --- a/lib/mcollective/vendor/systemu/samples/f.rb +++ /dev/null @@ -1,18 +0,0 @@ -# -# if a block is specified then it is passed the child pid and run in a -# background thread. note that this thread will __not__ be blocked during the -# execution of the command so it may do useful work such as killing the child -# if execution time passes a certain threshold -# - require 'systemu' - - looper = %q( ruby -e" loop{ STDERR.puts Time.now.to_i; sleep 1 } " ) - - status, stdout, stderr = - systemu looper do |cid| - sleep 3 - Process.kill 9, cid - end - - p status - p stderr diff --git a/lib/mcollective/vendor/systemu/systemu.gemspec b/lib/mcollective/vendor/systemu/systemu.gemspec deleted file mode 100755 index bfe834c8..00000000 --- a/lib/mcollective/vendor/systemu/systemu.gemspec +++ /dev/null @@ -1,45 +0,0 @@ -## systemu.gemspec -# - -Gem::Specification::new do |spec| - spec.name = "systemu" - spec.version = "2.6.4" - spec.platform = Gem::Platform::RUBY - spec.summary = "systemu" - spec.description = "universal capture of stdout and stderr and handling of child process pid for windows, *nix, etc." - spec.license = "Ruby" - - spec.files = -["LICENSE", - "README", - "README.erb", - "Rakefile", - "lib", - "lib/systemu.rb", - "samples", - "samples/a.rb", - "samples/b.rb", - "samples/c.rb", - "samples/d.rb", - "samples/e.rb", - "samples/f.rb", - "systemu.gemspec", - "test", - "test/systemu_test.rb", - "test/testing.rb"] - - spec.executables = [] - - spec.require_path = "lib" - - spec.test_files = nil - - - - spec.extensions.push(*[]) - - spec.rubyforge_project = "codeforpeople" - spec.author = "Ara T. Howard" - spec.email = "ara.t.howard@gmail.com" - spec.homepage = "https://github.com/ahoward/systemu" -end diff --git a/lib/mcollective/vendor/systemu/test/systemu_test.rb b/lib/mcollective/vendor/systemu/test/systemu_test.rb deleted file mode 100755 index 6d4bf2ba..00000000 --- a/lib/mcollective/vendor/systemu/test/systemu_test.rb +++ /dev/null @@ -1,78 +0,0 @@ - -Testing SystemU do - -## -# - testing 'that simple usage works' do - status, stdout, stderr = assert{ systemu :bin/:ls } - assert{ status == 0 } - assert{ stdout['lib'] } - assert{ stderr.strip.empty? } - end - - testing 'program with stdin' do - stdin = '42' - status, stdout, stderr = assert{ systemu :bin/:cat, :stdin => stdin } - assert{ status == 0 } - assert{ stdout == stdin } - end - - testing 'silly hostnames' do - host = SystemU.instance_variable_get('@host') - silly_hostname = "silly's hostname with spaces" - begin - SystemU.instance_variable_set('@host', silly_hostname) - assert{ SystemU.instance_variable_get('@host') == silly_hostname } - stdin = '42' - status, stdout, stderr = assert{ systemu :bin/:cat, :stdin => stdin } - assert{ status == 0 } - ensure - assert{ SystemU.instance_variable_set('@host', host) } - end - end - -end - - - - - -BEGIN { - -# silly hax to build commands we can shell out to on any platform. since -# tests might run on windoze we assume only that 'ruby' is available and build -# other command-line programs from it. -# - module Kernel - private - def bin(which, options = {}, &block) - case which.to_s - when 'ls' - %| ruby -e'puts Dir.glob("*").sort' | - - when 'cat' - %| ruby -e'STDOUT.write(ARGF.read)' | - - when 'find' - %| ruby -e'puts Dir.glob("**/**").sort' | - end - end - end - -# just let's us write: :bin/:ls -# - class Symbol - def / other, options = {}, &block - eval "#{ self }(:#{ other }, options, &block)" - end - end - - testdir = File.dirname(File.expand_path(__FILE__)) - rootdir = File.dirname(testdir) - libdir = File.join(rootdir, 'lib') - require File.join(libdir, 'systemu') - require File.join(testdir, 'testing') - - - Dir.chdir(rootdir) -} diff --git a/lib/mcollective/vendor/systemu/test/testing.rb b/lib/mcollective/vendor/systemu/test/testing.rb deleted file mode 100755 index d7870fc2..00000000 --- a/lib/mcollective/vendor/systemu/test/testing.rb +++ /dev/null @@ -1,195 +0,0 @@ -require 'test/unit' - -testdir = File.expand_path(File.dirname(__FILE__)) -rootdir = File.dirname(testdir) -libdir = File.join(rootdir, 'lib') - -STDOUT.sync = true - -$:.unshift(testdir) unless $:.include?(testdir) -$:.unshift(libdir) unless $:.include?(libdir) -$:.unshift(rootdir) unless $:.include?(rootdir) - -class Testing - class Slug < ::String - def Slug.for(*args) - string = args.flatten.compact.join('-') - words = string.to_s.scan(%r/\w+/) - words.map!{|word| word.gsub %r/[^0-9a-zA-Z_-]/, ''} - words.delete_if{|word| word.nil? or word.strip.empty?} - new(words.join('-').downcase) - end - end - - class Context - attr_accessor :name - - def initialize(name, *args) - @name = name - end - - def to_s - Slug.for(name) - end - end -end - -def Testing(*args, &block) - Class.new(::Test::Unit::TestCase) do - - ## class methods - # - class << self - def contexts - @contexts ||= [] - end - - def context(*args, &block) - return contexts.last if(args.empty? and block.nil?) - - context = Testing::Context.new(*args) - contexts.push(context) - - begin - block.call(context) - ensure - contexts.pop - end - end - - def slug_for(*args) - string = [context, args].flatten.compact.join('-') - words = string.to_s.scan(%r/\w+/) - words.map!{|word| word.gsub %r/[^0-9a-zA-Z_-]/, ''} - words.delete_if{|word| word.nil? or word.strip.empty?} - words.join('-').downcase.sub(/_$/, '') - end - - def name() const_get(:Name) end - - def testno() - '%05d' % (@testno ||= 0) - ensure - @testno += 1 - end - - def testing(*args, &block) - method = ["test", testno, slug_for(*args)].delete_if{|part| part.empty?}.join('_') - define_method(method, &block) - end - - def test(*args, &block) - testing(*args, &block) - end - - def setup(&block) - define_method(:setup, &block) if block - end - - def teardown(&block) - define_method(:teardown, &block) if block - end - - def prepare(&block) - @prepare ||= [] - @prepare.push(block) if block - @prepare - end - - def cleanup(&block) - @cleanup ||= [] - @cleanup.push(block) if block - @cleanup - end - end - - ## configure the subclass! - # - const_set(:Testno, '0') - slug = slug_for(*args).gsub(%r/-/,'_') - name = ['TESTING', '%03d' % const_get(:Testno), slug].delete_if{|part| part.empty?}.join('_') - name = name.upcase! - const_set(:Name, name) - const_set(:Missing, Object.new.freeze) - - ## instance methods - # - alias_method('__assert__', 'assert') - - def assert(*args, &block) - if args.size == 1 and args.first.is_a?(Hash) - options = args.first - expected = getopt(:expected, options){ missing } - actual = getopt(:actual, options){ missing } - if expected == missing and actual == missing - actual, expected, *ignored = options.to_a.flatten - end - expected = expected.call() if expected.respond_to?(:call) - actual = actual.call() if actual.respond_to?(:call) - assert_equal(expected, actual) - end - - if block - label = "assert(#{ args.join(' ') })" - result = nil - assert_nothing_raised{ result = block.call } - __assert__(result, label) - result - else - result = args.shift - label = "assert(#{ args.join(' ') })" - __assert__(result, label) - result - end - end - - def missing - self.class.const_get(:Missing) - end - - def getopt(opt, hash, options = nil, &block) - [opt.to_s, opt.to_s.to_sym].each do |key| - return hash[key] if hash.has_key?(key) - end - default = - if block - block.call - else - options.is_a?(Hash) ? options[:default] : nil - end - return default - end - - def subclass_of exception - class << exception - def ==(other) super or self > other end - end - exception - end - - ## - # - module_eval(&block) - - self.setup() - self.prepare.each{|b| b.call()} - - at_exit{ - self.teardown() - self.cleanup.each{|b| b.call()} - } - - self - end -end - - -if $0 == __FILE__ - - Testing 'Testing' do - testing('foo'){ assert true } - test{ assert true } - p instance_methods.grep(/test/) - end - -end diff --git a/lib/mcollective/windows_daemon.rb b/lib/mcollective/windows_daemon.rb deleted file mode 100644 index ea0bf037..00000000 --- a/lib/mcollective/windows_daemon.rb +++ /dev/null @@ -1,45 +0,0 @@ -require 'win32/daemon' - -module MCollective - class WindowsDaemon < Win32::Daemon - - def self.daemonize_runner(pid=nil) - raise "Writing pid files are not supported on the Windows Platform" if pid - raise "The Windows Daemonizer should only be used on the Windows Platform" unless Util.windows? - - WindowsDaemon.mainloop - end - - def service_main - Log.debug("Starting Windows Service Daemon") - - @runner = Runner.new(nil) - @runner.main_loop - - # On shut down there may be threads outside of the runner's context that are - # in a sleeping state, causing the stop action to wait for them to cleanly exit. - # We get around this by iterating the list of threads and killing everything that - # isn't the main thread, letting us shut down cleanly. - Thread.list.each do |t| - if t != Thread.current - t.kill - end - end - end - - def service_stop - Log.info("Windows service stopping") - @runner.stop - end - - def service_pause - Log.info("Pausing MCollective Windows server") - @runner.pause - end - - def service_resume - Log.info("Resuming MCollective Windows server") - @runner.resume - end - end -end diff --git a/mcollective.init b/mcollective.init deleted file mode 100755 index c608f006..00000000 --- a/mcollective.init +++ /dev/null @@ -1,122 +0,0 @@ -#!/bin/sh -# -# mcollective Application Server for STOMP based agents -# -# chkconfig: 345 24 76 -# -# description: mcollective lets you build powerful Stomp compatible middleware clients in ruby without having to worry too -# much about all the setup and management of a Stomp connection, it also provides stats, logging and so forth -# as a bonus. -# -### BEGIN INIT INFO -# Provides: mcollective -# Required-Start: $remote_fs -# Required-Stop: $remote_fs -# Default-Start: 2 3 4 5 -# Default-Stop: 0 1 6 -# Short-Description: Start daemon at boot time -# Description: Enable service provided by daemon. -### END INIT INFO - -mcollectived="/usr/sbin/mcollectived" - -# Lockfile -if [ -d /var/lock/subsys ]; then - # RedHat/CentOS/etc who use subsys - lock="/var/lock/subsys/mcollective" -else - # The rest of them - lock="/var/lock/mcollective" -fi - - -# PID directory -pidfile="/var/run/mcollectived.pid" - -# Source function library. -. /lib/lsb/init-functions - -# Check that binary exists -if ! [ -f $mcollectived ] -then - echo "mcollectived binary not found" - exit 0 -fi - -# See how we were called. -case "$1" in - start) - echo -n "Starting mcollective: " - - if [ -f ${lock} ]; then - # we were not shut down correctly - if [ -s ${pidfile} ]; then - kill `cat ${pidfile}` >/dev/null 2>&1 - fi - rm -f ${pidfile} - - rm -f ${lock} - sleep 2 - fi - - rm -f ${pidfile} - - ${mcollectived} --pid=${pidfile} --config="/etc/mcollective/server.cfg" --daemonize - if [ $? = 0 ]; then - log_success_msg - touch $lock - exit 0 - else - log_failure_msg - exit 1 - fi - ;; - stop) - echo -n "Shutting down mcollective: " - - if [ -s ${pidfile} ]; then - kill `cat ${pidfile}` >/dev/null 2>&1 - fi - rm -f ${pidfile} - - log_success_msg - rm -f $lock - ;; - restart) - $0 stop - sleep 2 - $0 start - ;; - condrestart) - if [ -f $lock ]; then - $0 stop - # avoid race - sleep 2 - $0 start - fi - ;; - status) - if [ -f ${lock} ]; then - if [ -s ${pidfile} ]; then - if [ -e /proc/`cat ${pidfile}` ]; then - echo "mcollectived (`cat ${pidfile}`) is running" - exit 0 - else - echo "mcollectived (`cat ${pidfile}`) is NOT running" - exit 1 - fi - fi - else - echo "mcollectived: service not started" - exit 1 - fi - ;; - force-reload) - echo "not implemented" - ;; - *) - echo "Usage: mcollectived {start|stop|restart|condrestart|status}" - exit 1 - ;; -esac -exit 0 diff --git a/spec/unit/mcollective/agent/rpcutil_spec.rb b/spec/unit/mcollective/agent/rpcutil_spec.rb deleted file mode 100644 index 2641b9b4..00000000 --- a/spec/unit/mcollective/agent/rpcutil_spec.rb +++ /dev/null @@ -1,176 +0,0 @@ -#!/usr/bin/env rspec - -@agent_file = File.join('mcollective', 'agent', 'rpcutil') - -require 'spec_helper' -require @agent_file - -module MCollective - module Agent - describe Rpcutil do - module Facts - def self.[](fact) - {'fact1' => 'value1', 'fact2' => 'value2', 'fact3' => 'value3'}[fact] - end - end - - before :each do - @agent = MCollective::Test::LocalAgentTest.new('rpcutil', :agent_file => @agent_file).plugin - end - - describe '#inventory' do - it 'should return the node inventory' do - facts = mock - facts.stubs(:get_facts).returns({:key => 'value'}) - Agents.stubs(:agentlist).returns(['test']) - PluginManager.stubs(:[]).with('facts_plugin').returns(facts) - MCollective.stubs(:version).returns('2.4.0') - PluginManager.stubs(:grep).with(/_data$/).returns(['test_data']) - Config.any_instance.stubs(:classesfile).returns('classes.txt') - File.stubs(:exist?).with('classes.txt').returns(true) - File.stubs(:readlines).with('classes.txt').returns(['class1', 'class2']) - result = @agent.call(:inventory) - result.should be_successful - result.should have_data_items({:agents=>["test"], - :facts=>{:key=>"value"}, - :version=>"2.4.0", - :classes=>["class1", "class2"], - :main_collective=>"mcollective", - :collectives=>["production", "staging"], - :data_plugins=>["test_data"]}) - end - end - - describe '#get_fact' do - it 'should return the value of the queried fact' do - result = @agent.call(:get_fact, :fact => 'fact1') - result.should be_successful - result.should have_data_items({:fact => 'fact1', :value => 'value1'}) - end - - it 'should not break if the fact is not present' do - result = @agent.call(:get_fact, :fact => 'fact4') - result.should be_successful - result.should have_data_items({:fact => 'fact4', :value => nil }) - end - end - - describe '#get_facts' do - it 'should return the value of the supplied facts' do - result = @agent.call(:get_facts, :facts => 'fact1, fact3') - result.should be_successful - result.should have_data_items(:values => {'fact1' => 'value1', 'fact3' => 'value3'}) - end - - it 'should not break if the facts are not present' do - result = @agent.call(:get_facts, :facts => 'fact4, fact5') - result.should be_successful - result.should have_data_items(:values => {'fact4' => nil, 'fact5' => nil}) - end - end - - describe '#daemon_stats' do - it "it should return the daemon's statistics" do - stats = {:threads => 2, - :agents => ['agent1', 'agent2'], - :pid => 42, - :times => 12345, - :configfile => '/etc/mcollective/server.cfg', - :version => '2.4.0', - :stats => {}} - config = mock - Config.stubs(:instance).returns(config) - MCollective.stubs(:version).returns('2.4.0') - config.stubs(:configfile).returns('/etc/mcollective/server.cfg') - PluginManager.stubs(:[]).returns(stats) - result = @agent.call(:daemon_stats) - result.should be_successful - stats.delete(:stats) - result.should have_data_items(stats) - end - end - - describe '#agent_inventory' do - it 'should return the agent inventory' do - meta = {:license => 'ASL 2', - :description => 'Agent for testing', - :url => 'http://www.theurl.net', - :version => '1', - :author => 'rspec'} - agent = mock - agent.stubs(:meta).returns(meta) - agent.stubs(:timeout).returns(2) - PluginManager.stubs(:[]).with('test_agent').returns(agent) - Agents.stubs(:agentlist).returns(['test']) - - result = @agent.call(:agent_inventory) - result.should be_successful - result.should have_data_items(:agents => [meta.merge({:timeout => 2, :name => 'test', :agent => 'test'})]) - end - end - - describe '#get_config_item' do - it 'should return the value of the requested config item' do - Config.any_instance.stubs(:respond_to?).with(:main_collective).returns(true) - result = @agent.call(:get_config_item, :item => :main_collective) - result.should be_successful - result.should have_data_items(:item => :main_collective, :value => "mcollective") - end - - it 'should fail if the config item has not been defined' do - result = @agent.call(:get_config_item, :item => :failure) - result.should be_aborted_error - end - end - - describe '#ping' do - it 'should return the current time on the host' do - Time.expects(:now).returns("123456") - result = @agent.call(:ping) - result.should be_successful - result.should have_data_items(:pong => 123456) - end - end - - describe '#collective_info' do - it 'should return the main collective and list of defined collectives' do - result = @agent.call(:collective_info) - result.should be_successful - result.should have_data_items({:main_collective => 'mcollective', :collectives => ['production', 'staging']}) - end - end - - describe '#get_data' do - let(:query_data) do - {:key1 => 'value1', :key2 => 'value2'} - end - - let(:data) do - mock - end - - let(:ddl) do - mock - end - - it 'should return the data results if a query has been specified' do - data.stubs(:lookup).with('query').returns(query_data) - Data.stubs(:ddl).with('test_data').returns(ddl) - Data.stubs(:ddl_transform_input).with(ddl, 'query').returns('query') - Data.stubs(:[]).with('test_data').returns(data) - result = @agent.call(:get_data, :source => 'test_data', :query => 'query') - result.should be_successful - result.should have_data_items(:key1 => 'value1', :key2 => 'value2') - end - - it 'should return the data results if no query has been specified' do - data.stubs(:lookup).with(nil).returns(query_data) - Data.stubs(:[]).with('test_data').returns(data) - result = @agent.call(:get_data, :source => 'test_data') - result.should be_successful - result.should have_data_items(:key1 => 'value1', :key2 => 'value2') - end - end - end - end -end diff --git a/spec/unit/mcollective/audit/logfile_spec.rb b/spec/unit/mcollective/audit/logfile_spec.rb deleted file mode 100644 index dbc184e2..00000000 --- a/spec/unit/mcollective/audit/logfile_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' -require 'mcollective/audit/logfile' - -module MCollective - module RPC - describe Logfile do - let(:file) do - mock - end - - let(:request) do - req = mock - req.stubs(:uniqid).returns("1234") - req.stubs(:time).returns(1389179593) - req.stubs(:caller).returns("test_user") - req.stubs(:sender).returns("test_host") - req.stubs(:agent).returns("rspec_agent") - req.stubs(:action).returns("testme") - req.stubs(:data).returns({}) - req - end - - before :each do - Time.stubs(:now).returns(Time.at(1389180255)) - file.expects(:puts).with("[2014-01-08 11:24:15 UTC] reqid=1234: reqtime=1389179593 caller=test_user@test_host " + - "agent=rspec_agent action=testme data={}") - end - - it 'should log to a user defined logfile' do - Config.any_instance.stubs(:pluginconf).returns("rpcaudit.logfile" => "/nonexisting") - File.expects(:open).with("/nonexisting", "a").yields(file) - Logfile.new.audit_request(request, nil) - end - - it 'should log to a default logfile' do - Config.any_instance.stubs(:pluginconf).returns({}) - File.expects(:open).with("/var/log/puppetlabs/mcollective/mcollective-audit.log", "a").yields(file) - Logfile.new.audit_request(request, nil) - end - end - end -end diff --git a/spec/unit/mcollective/connector/activemq_spec.rb b/spec/unit/mcollective/connector/activemq_spec.rb deleted file mode 100644 index dad8b460..00000000 --- a/spec/unit/mcollective/connector/activemq_spec.rb +++ /dev/null @@ -1,833 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' - -MCollective::PluginManager.clear - -require 'mcollective/connector/activemq' - -# create the stomp error class here as it does not always exist -# all versions of the stomp gem and we do not want to tie tests -# to the stomp gem -module Stomp - module Error - class DuplicateSubscription < RuntimeError; end - end -end - -module MCollective - module Connector - describe Activemq do - - let(:config) do - conf = mock - conf.stubs(:configured).returns(true) - conf.stubs(:identity).returns("rspec") - conf.stubs(:collectives).returns(["mcollective"]) - conf - end - - let(:logger) do - log = mock - log.stubs(:log) - log.stubs(:start) - Log.configure(log) - log - end - - let(:msg) do - m = mock - m.stubs(:base64_encode!) - m.stubs(:payload).returns("msg") - m.stubs(:agent).returns("agent") - m.stubs(:type).returns(:reply) - m.stubs(:collective).returns("mcollective") - m - end - - let(:subscription) do - sub = mock - sub.stubs(:<<).returns(true) - sub.stubs(:include?).returns(false) - sub.stubs(:delete).returns(false) - sub - end - - let(:connection) do - con = mock - con.stubs(:subscribe).returns(true) - con.stubs(:unsubscribe).returns(true) - con - end - - let(:connector) do - Activemq.any_instance.stubs(:get_bool_option).with("activemq.use_exponential_back_off", "true").returns(true) - Activemq.any_instance.stubs(:get_option).with("activemq.initial_reconnect_delay", 0.01).returns(0.01) - Activemq.any_instance.stubs(:get_option).with("activemq.back_off_multiplier", 2).returns(2) - Activemq.any_instance.stubs(:get_option).with("activemq.max_reconnect_delay", 30.0).returns(30.0) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.agents_multiplex", "false").returns(true) - c = Activemq.new - c.instance_variable_set("@subscriptions", subscription) - c.instance_variable_set("@connection", connection) - c - end - - before do - unless ::Stomp::Error.constants.map{|c| c.to_s}.include?("NoCurrentConnection") - class ::Stomp::Error::NoCurrentConnection < RuntimeError ; end - end - - logger - Config.stubs(:instance).returns(config) - end - - describe "#initialize" do - before :each do - Activemq.any_instance.stubs(:get_bool_option).with("activemq.use_exponential_back_off", "true").returns(true) - Activemq.any_instance.stubs(:get_option).with("activemq.initial_reconnect_delay", 0.01).returns(0.01) - Activemq.any_instance.stubs(:get_option).with("activemq.back_off_multiplier", 2).returns(2) - Activemq.any_instance.stubs(:get_option).with("activemq.max_reconnect_delay", 30.0).returns(30.0) - end - - it "should set the @config variable" do - connector_obj = Activemq.new - connector_obj.instance_variable_get("@config").should == config - end - - it "should set @subscriptions to an empty list" do - connector_obj = Activemq.new - connector_obj.instance_variable_get("@subscriptions").should == [] - end - end - - describe "#connect" do - before :each do - Activemq.any_instance.stubs(:get_option).with("activemq.heartbeat_interval", 0).returns(30) - Activemq.any_instance.stubs(:get_bool_option).with('activemq.stomp_1_0_fallback', true).returns(true) - Activemq.any_instance.stubs(:get_bool_option).with('activemq.base64', 'false').returns(false) - Activemq.any_instance.stubs(:get_option).with('activemq.vhost', 'mcollective').returns('rspec') - Activemq.any_instance.stubs(:get_option).with("activemq.max_reconnect_attempts", 0).returns(5) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.randomize", "false").returns(true) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.backup", "false").returns(true) - Activemq.any_instance.stubs(:get_option).with("activemq.timeout", -1).returns(1) - Activemq.any_instance.stubs(:get_option).with("activemq.connect_timeout", 30).returns(5) - Activemq.any_instance.stubs(:get_option).with("activemq.max_hbrlck_fails", 0).returns(0) - Activemq.any_instance.stubs(:get_option).with("activemq.max_hbread_fails", 2).returns(2) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.base64", 'false').returns(false) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.prompt_user", 'false').returns(false) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.prompt_password", 'false').returns(false) - Activemq.any_instance.stubs(:get_option).with("activemq.priority", 0).returns(0) - Activemq.any_instance.stubs(:get_option).with("activemq.pool.size").returns(2) - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.host").returns("host1") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.port", 61613).returns(6163) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.pool.1.ssl", "false").returns(false) - Activemq.any_instance.stubs(:get_option).with("activemq.pool.2.host").returns("host2") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.2.port", 61613).returns(6164) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.pool.2.ssl", "false").returns(true) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.pool.2.ssl.fallback", "false").returns(true) - Activemq.any_instance.stubs(:get_env_or_option).with("STOMP_USER", "activemq.pool.1.user", '').returns("user1") - Activemq.any_instance.stubs(:get_env_or_option).with("STOMP_USER", "activemq.pool.2.user", '').returns("user2") - Activemq.any_instance.stubs(:get_env_or_option).with("STOMP_PASSWORD", "activemq.pool.1.password", '').returns("password1") - Activemq.any_instance.stubs(:get_env_or_option).with("STOMP_PASSWORD", "activemq.pool.2.password", '').returns("password2") - Activemq.any_instance.instance_variable_set("@subscriptions", subscription) - Activemq.any_instance.instance_variable_set("@connection", connection) - end - - it "should not try to reconnect if already connected" do - Log.expects(:debug).with("Already connection, not re-initializing connection").once - connector.connect - end - - it "should support new style config" do - ENV.delete("STOMP_USER") - ENV.delete("STOMP_PASSWORD") - - Activemq::EventLogger.expects(:new).returns("logger") - - connector_obj = mock - connector_obj.expects(:new).with(:backup => true, - :back_off_multiplier => 2, - :max_reconnect_delay => 30.0, - :timeout => 1, - :connect_timeout => 5, - :use_exponential_back_off => true, - :max_reconnect_attempts => 5, - :initial_reconnect_delay => 0.01, - :max_hbrlck_fails => 0, - :max_hbread_fails => 2, - :randomize => true, - :reliable => true, - :logger => "logger", - :connect_headers => {}, - :hosts => [{:passcode => 'password1', - :host => 'host1', - :port => 6163, - :ssl => false, - :login => 'user1'}, - {:passcode => 'password2', - :host => 'host2', - :port => 6164, - :ssl => true, - :login => 'user2'} - ]) - - connector.expects(:ssl_parameters).with(2, true).returns(true) - connector.expects(:connection_headers).returns({}) - - connector.instance_variable_set("@connection", nil) - connector.connect(connector_obj) - end - end - - describe "#stomp_version_supports_heartbeat?" do - it "should not be supported with stomp 1.2.9" do - connector.stubs(:stomp_version).returns("1.2.9") - connector.stomp_version_supports_heartbeat? == false - end - - it "should be supported with stomp 1.2.10" do - connector.stubs(:stomp_version).returns("1.2.10") - connector.stomp_version_supports_heartbeat? == true - end - end - - describe "#connection_headers" do - before do - connector.stubs(:stomp_version).returns("1.2.10") - Activemq.any_instance.stubs(:get_option).with("activemq.heartbeat_interval", 0).returns(1) - Activemq.any_instance.stubs(:get_bool_option).with('activemq.stomp_1_0_fallback', true).returns(true) - Activemq.any_instance.stubs(:get_option).with('activemq.vhost', 'mcollective').returns('rspec') - end - - it "should default to stomp 1.0 only" do - Activemq.any_instance.stubs(:get_option).with("activemq.heartbeat_interval", 0).returns(0) - connector.connection_headers[:"accept-version"] == "1.0" - end - - it "should support setting the vhost" do - connector.connection_headers[:host].should == "rspec" - end - - it "should log an informational message about not using Stomp 1.1" do - Activemq.any_instance.stubs(:get_option).with("activemq.heartbeat_interval", 0).returns(0) - Log.expects(:info).with(regexp_matches(/without STOMP 1.1 heartbeats/)) - connector.connection_headers - end - - it "should not log an informational message about not using Stomp 1.1 if the gem won't support it" do - Activemq.any_instance.stubs(:get_option).with("activemq.heartbeat_interval", 0).returns(0) - connector.stubs(:stomp_version).returns("1.0.0") - Log.expects(:info).with(regexp_matches(/without STOMP 1.1 heartbeats/)).never - connector.connection_headers - end - - it "should not support stomp 1.1 with older versions of the stomp gem" do - Activemq.any_instance.stubs(:get_option).with("activemq.heartbeat_interval", 0).returns(1) - connector.expects(:stomp_version).returns("1.0.0").once - expect { connector.connection_headers }.to raise_error("Setting STOMP 1.1 properties like heartbeat intervals require at least version 1.2.10 of the STOMP gem") - end - - it "should force the heartbeat to min 30 seconds" do - Activemq.any_instance.stubs(:get_option).with("activemq.heartbeat_interval", 0).returns(1) - connector.connection_headers[:"heart-beat"].should == "30500,29500" - end - - it "should default to 1.0 and 1.1 support" do - Activemq.any_instance.stubs(:get_option).with("activemq.heartbeat_interval", 0).returns(1) - connector.connection_headers[:"accept-version"].should == "1.1,1.0" - end - - it "should support stomp 1.1 only operation" do - Activemq.any_instance.stubs(:get_option).with("activemq.heartbeat_interval", 0).returns(1) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.stomp_1_0_fallback", true).returns(false) - connector.connection_headers[:"accept-version"].should == "1.1" - end - end - - describe "#ssl_paramaters" do - - before :each do - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.host").returns("host1") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.port").returns("6164") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.user").returns("user1") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.password").returns("password1") - Activemq.any_instance.stubs(:get_bool_option).with("activemq.pool.1.ssl", false).returns(true) - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.ciphers", false).returns(false) - end - - it "should ensure all settings are provided" do - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.cert", false).returns("rspec") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.key", false).returns(nil) - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.ca", false).returns(nil) - - expect { connector.ssl_parameters(1, false) }.to raise_error("cert, key and ca has to be supplied for verified SSL mode") - end - - it "should verify the ssl files exist" do - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.cert", false).returns("rspec") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.key", false).returns('rspec.key') - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.ca", false).returns('rspec1.ca,rspec2.ca') - - connector.expects(:get_key_file).returns("rspec.key").at_least_once - connector.expects(:get_cert_file).returns("rspec.cert").at_least_once - - File.expects(:exist?).with("rspec.cert").twice.returns(true) - File.expects(:exist?).with("rspec.key").twice.returns(true) - File.expects(:exist?).with("rspec1.ca").twice.returns(true) - File.expects(:exist?).with("rspec2.ca").twice.returns(false) - - expect { connector.ssl_parameters(1, false) }.to raise_error("Cannot find CA file rspec2.ca") - - connector.ssl_parameters(1, true).should == true - end - - it "should support fallback mode when there are errors" do - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.cert", false).returns("rspec") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.key", false).returns('rspec.key') - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.ca", false).returns('rspec1.ca,rspec2.ca') - - connector.ssl_parameters(1, true).should == true - end - - it "should fail if fallback isnt enabled" do - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.cert", false).returns("rspec") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.key", false).returns('rspec.key') - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.ca", false).returns('rspec1.ca,rspec2.ca') - - expect { connector.ssl_parameters(1, false) }.to raise_error - end - - context 'ciphers' do - before :each do - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.cert", false).returns("rspec") - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.key", false).returns('rspec.key') - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.ca", false).returns('rspec1.ca,rspec2.ca') - File.stubs(:exist?).returns(true) - end - - it 'should not set ciphers by default' do - parameters = connector.ssl_parameters(1, false) - parameters.ciphers.should == false - end - - it 'should support setting of ciphers' do - Activemq.any_instance.stubs(:get_option).with("activemq.pool.1.ssl.ciphers", false).returns('TLSv127:!NUTS') - parameters = connector.ssl_parameters(1, false) - parameters.ciphers.should == 'TLSv127:!NUTS' - end - end - - end - - describe "#get_key_file" do - it "should return the filename from the environment variable" do - ENV["MCOLLECTIVE_ACTIVEMQ_POOL2_SSL_KEY"] = "/path/to/rspec/env" - connector.get_key_file(2).should == "/path/to/rspec/env" - end - - it "should return the filename defined in the config file if the environment varialbe doesn't exist" do - ENV.delete("MCOLLECTIVE_ACTIVEMQ_POOL2_SSL_KEY") - connector.expects(:get_option).with("activemq.pool.2.ssl.key", false).returns("/path/to/rspec/conf") - connector.get_key_file(2).should == "/path/to/rspec/conf" - end - - end - - describe "#get_cert_file" do - it "should return the filename from the environment variable" do - ENV["MCOLLECTIVE_ACTIVEMQ_POOL2_SSL_CERT"] = "/path/to/rspec/env" - connector.get_cert_file(2).should == "/path/to/rspec/env" - end - - it "should return the filename defined in the config file if the environment varialbe doesn't exist" do - ENV.delete("MCOLLECTIVE_ACTIVEMQ_POOL2_SSL_CERT") - connector.expects(:get_option).with("activemq.pool.2.ssl.cert", false).returns("/path/to/rspec/conf") - connector.get_cert_file(2).should == "/path/to/rspec/conf" - end - end - - describe '#exponential_back_off' do - it "should not do anything when use_exponential_back_off is off" do - connector.instance_variable_set(:@use_exponential_back_off, false) - connector.exponential_back_off.should == nil - end - - it 'should return values of the expected sequence on subsequent calls' do - connector.instance_variable_set(:@use_exponential_back_off, true) - connector.instance_variable_set(:@initial_reconnect_delay, 5.0) - connector.instance_variable_set(:@back_off_multiplier, 2) - connector.instance_variable_set(:@max_reconnect_delay, 30.0) - connector.instance_variable_set(:@reconnect_delay, 5.0) - - connector.exponential_back_off.should == 5 - connector.exponential_back_off.should == 10 - connector.exponential_back_off.should == 20 - connector.exponential_back_off.should == 30 - connector.exponential_back_off.should == 30 - end - end - - describe "#receive" do - it "should receive from the middleware" do - payload = mock - payload.stubs(:command).returns("MESSAGE") - payload.stubs(:body).returns("msg") - payload.stubs(:headers).returns("headers") - - connection.expects(:receive).returns(payload) - - Message.expects(:new).with("msg", payload, :base64 => true, :headers => "headers").returns("message") - connector.instance_variable_set("@base64", true) - - received = connector.receive - received.should == "message" - end - - it "should sleep and retry if recieving while disconnected" do - payload = mock - payload.stubs(:command).returns("MESSAGE") - payload.stubs(:body).returns("msg") - payload.stubs(:headers).returns("headers") - - Message.stubs(:new).returns("rspec") - connection.expects(:receive).raises(::Stomp::Error::NoCurrentConnection).returns(payload).twice - connector.expects(:sleep).with(1) - - connector.receive.should == "rspec" - end - - it "should raise an error on failure to receive a frame" do - connection.expects(:receive).returns(nil) - - expect { connector.receive }.to raise_error(MessageNotReceived, /No message received from ActiveMQ./) - end - - it "should log and raise UnexpectedMessageType on non-MESSAGE frames" do - payload = mock - payload.stubs(:command).returns("ERROR") - payload.stubs(:body).returns("Out of cheese exception") - payload.stubs(:headers).returns("headers") - - connection.expects(:receive).returns(payload) - - Message.stubs(:new) - - Log.stubs(:debug) - Log.expects(:debug).with('Unexpected \'ERROR\' frame. Headers: "headers" Body: "Out of cheese exception"') - - expect { connector.receive }.to raise_error(UnexpectedMessageType, /Received frame of type 'ERROR' expected 'MESSAGE'/) - end - end - - describe "#publish" do - before do - connection.stubs(:publish).with("test", "msg", {}).returns(true) - end - - it "should base64 encode a message if configured to do so" do - connector.instance_variable_set("@base64", true) - connector.expects(:headers_for).returns({}) - connector.expects(:target_for).returns({:name => "test", :headers => {}}) - connection.expects(:publish).with("test", "msg", {}) - msg.expects(:base64_encode!) - - connector.publish(msg) - end - - it "should not base64 encode if not configured to do so" do - connector.instance_variable_set("@base64", false) - connector.expects(:headers_for).returns({}) - connector.expects(:target_for).returns({:name => "test", :headers => {}}) - connection.expects(:publish).with("test", "msg", {}) - msg.expects(:base64_encode!).never - - connector.publish(msg) - end - - it "should publish the correct message to the correct target with msgheaders" do - connection.expects(:publish).with("test", "msg", {"test" => "test"}).once - connector.expects(:headers_for).returns({"test" => "test"}) - connector.expects(:target_for).returns({:name => "test", :headers => {}}) - - connector.publish(msg) - end - - it "should publish direct messages based on discovered_hosts" do - msg = mock - msg.stubs(:base64_encode!) - msg.stubs(:payload).returns("msg") - msg.stubs(:agent).returns("agent") - msg.stubs(:collective).returns("mcollective") - msg.stubs(:type).returns(:direct_request) - msg.expects(:discovered_hosts).returns(["one", "two"]) - - connector.expects(:headers_for).with(msg, "one") - connector.expects(:headers_for).with(msg, "two") - connection.expects(:publish).with('/queue/mcollective.nodes', 'msg', nil).twice - - connector.publish(msg) - end - end - - describe "#subscribe" do - it "should handle duplicate subscription errors" do - connection.expects(:subscribe).raises(::Stomp::Error::DuplicateSubscription) - Log.expects(:error).with(regexp_matches(/already had a matching subscription, ignoring/)) - connector.subscribe("test", :broadcast, "mcollective") - end - - it "should use the make_target correctly" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}}) - connector.subscribe("test", :broadcast, "mcollective") - end - - it "should check for existing subscriptions" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - subscription.expects(:include?).with("rspec").returns(false) - connection.expects(:subscribe).never - - connector.subscribe("test", :broadcast, "mcollective") - end - - it "should subscribe to the middleware" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - connection.expects(:subscribe).with("test", {}, "rspec") - connector.subscribe("test", :broadcast, "mcollective") - end - - it "should add to the list of subscriptions" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - subscription.expects(:<<).with("rspec") - connector.subscribe("test", :broadcast, "mcollective") - end - end - - describe "#unsubscribe" do - it "should use make_target correctly" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}}) - connector.unsubscribe("test", :broadcast, "mcollective") - end - - it "should unsubscribe from the target" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - connection.expects(:unsubscribe).with("test", {}, "rspec").once - - connector.unsubscribe("test", :broadcast, "mcollective") - end - - it "should delete the source from subscriptions" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - subscription.expects(:delete).with("rspec").once - - connector.unsubscribe("test", :broadcast, "mcollective") - end - end - - describe "#target_for" do - it "should create reply targets based on reply-to headers in requests" do - message = mock - message.expects(:type).returns(:reply) - - request = mock - request.expects(:headers).returns({"reply-to" => "foo"}) - - message.expects(:request).returns(request) - - connector.target_for(message).should == {:name => "foo", :headers => {}} - end - - it "should create new request targets" do - message = mock - message.expects(:type).returns(:request).times(3) - message.expects(:agent).returns("rspecagent") - message.expects(:collective).returns("mcollective") - - connector.expects(:make_target).with("rspecagent", :request, "mcollective") - connector.target_for(message) - end - - it "should support direct requests" do - message = mock - message.expects(:type).returns(:direct_request).times(3) - message.expects(:agent).returns("rspecagent") - message.expects(:collective).returns("mcollective") - - connector.expects(:make_target).with("rspecagent", :direct_request, "mcollective") - connector.target_for(message) - end - - it "should fail for unknown message types" do - message = mock - message.stubs(:type).returns(:fail) - - expect { - connector.target_for(message) - }.to raise_error("Don't now how to create a target for message type fail") - end - end - - describe "#disconnect" do - it "should disconnect from the stomp connection" do - connection.expects(:disconnect) - connector.disconnect - connector.connection.should == nil - end - end - - describe "#headers_for" do - it "should not set priority header if priority is 0" do - message = mock - message.expects(:type).returns(:foo) - message.stubs(:ttl).returns(30) - - Time.expects(:now).twice.returns(Time.at(1368557431)) - - connector.instance_variable_set("@msgpriority", 0) - connector.headers_for(message).should_not includes("priority") - end - - it "should return a priority if priority is non 0" do - message = mock - message.expects(:type).returns(:foo) - message.stubs(:ttl).returns(30) - - Time.expects(:now).twice.returns(Time.at(1368557431)) - - connector.instance_variable_set("@msgpriority", 1) - connector.headers_for(message)["priority"].should == 1 - end - - it "should set mc_identity for direct requests" do - message = mock - message.expects(:type).returns(:direct_request).twice - message.expects(:agent).returns("rspecagent") - message.expects(:collective).returns("mcollective") - message.expects(:reply_to).returns(nil) - message.stubs(:ttl).returns(30) - - Time.expects(:now).twice.returns(Time.at(1368557431)) - - connector.instance_variable_set("@msgpriority", 0) - connector.expects(:make_target).with("rspecagent", :reply, "mcollective").returns({:name => "test"}) - - headers = connector.headers_for(message, "some.node") - headers["mc_identity"].should == "some.node" - headers["reply-to"].should == "test" - end - - it "should set a reply-to header for :request type messages" do - message = mock - message.expects(:type).returns(:request).twice - message.expects(:agent).returns("rspecagent") - message.expects(:collective).returns("mcollective") - message.expects(:reply_to).returns(nil) - message.stubs(:ttl).returns(30) - - Time.expects(:now).twice.returns(Time.at(1368557431)) - - connector.instance_variable_set("@msgpriority", 0) - connector.expects(:make_target).with("rspecagent", :reply, "mcollective").returns({:name => "test"}) - connector.headers_for(message)["reply-to"].should == "test" - end - - it "should set reply-to correctly if the message defines it" do - message = mock - message.expects(:type).returns(:request).twice - message.expects(:agent).returns("rspecagent") - message.expects(:collective).returns("mcollective") - message.expects(:reply_to).returns("rspec").twice - message.stubs(:ttl).returns(30) - - Time.expects(:now).twice.returns(Time.at(1368557431)) - - connector.headers_for(message)["reply-to"].should == "rspec" - end - - it "should set the timestamp and ttl based on the message object" do - message = mock - message.expects(:type).returns(:foo) - message.stubs(:ttl).returns(100) - - Time.expects(:now).twice.returns(Time.at(1368557431)) - - headers = connector.headers_for(message) - headers["timestamp"].should == "1368557431000" - headers["expires"].should == "1368557541000" - end - - it "should set mc_sender based on the identity" do - message = mock - message.expects(:type).returns(:foo) - message.stubs(:ttl).returns(100) - - headers = connector.headers_for(message) - headers['mc_sender'].should == 'rspec' - end - end - - describe "#make_target" do - it "should create correct targets if agents_multiplex is false" do - # realize the connector let so that we can unstub it - connector - Activemq.any_instance.unstub(:get_bool_option) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.agents_multiplex", "false").returns(false) - Client.stubs(:request_sequence).returns(42) - - connector.make_target("test", :reply, "mcollective").should == { - :name => "/queue/mcollective.reply.rspec_#{$$}.42", - :headers => {}, - :id => "/queue/mcollective.reply.rspec_#{$$}.42", - } - - connector.make_target("test", :broadcast, "mcollective").should == { - :name => "/topic/mcollective.test.agent", - :headers => {}, - :id => "/topic/mcollective.test.agent", - } - - connector.make_target("test", :request, "mcollective").should == { - :name => "/topic/mcollective.test.agent", - :headers => {}, - :id => "/topic/mcollective.test.agent", - } - - connector.make_target("test", :direct_request, "mcollective").should == { - :name => "/queue/mcollective.nodes", - :headers => {}, - :id => "/queue/mcollective.nodes", - } - - connector.make_target("test", :directed, "mcollective").should == { - :name => "/queue/mcollective.nodes", - :headers => { - "selector" => "mc_identity = 'rspec'", - }, - :id => "mcollective_directed_to_identity", - } - end - - it "should create correct targets if agents_multiplex is true" do - # realize the connector let so that we can unstub it - connector - Activemq.any_instance.unstub(:get_bool_option) - Activemq.any_instance.stubs(:get_bool_option).with("activemq.agents_multiplex", "false").returns(true) - Client.stubs(:request_sequence).returns(42) - - connector.make_target("test", :reply, "mcollective").should == { - :name => "/queue/mcollective.reply.rspec_#{$$}.42", - :headers => {}, - :id => "/queue/mcollective.reply.rspec_#{$$}.42", - } - - connector.make_target("test", :broadcast, "mcollective").should == { - :name => "/topic/mcollective.agents", - :headers => {}, - :id => "/topic/mcollective.agents", - } - - connector.make_target("test", :request, "mcollective").should == { - :name => "/topic/mcollective.agents", - :headers => {}, - :id => "/topic/mcollective.agents", - } - - connector.make_target("test", :direct_request, "mcollective").should == { - :name => "/queue/mcollective.nodes", - :headers => {}, - :id => "/queue/mcollective.nodes", - } - - connector.make_target("test", :directed, "mcollective").should == { - :name => "/queue/mcollective.nodes", - :headers => { - "selector" => "mc_identity = 'rspec'", - }, - :id => "mcollective_directed_to_identity", - } - end - - it "should raise an error for unknown collectives" do - expect { - connector.make_target("test", :broadcast, "foo") - }.to raise_error("Unknown collective 'foo' known collectives are 'mcollective'") - end - - it "should raise an error for unknown types" do - expect { - connector.make_target("test", :test, "mcollective") - }.to raise_error("Unknown target type test") - end - end - - - describe "#get_env_or_option" do - it "should return the environment variable if set" do - ENV["test"] = "rspec_env_test" - - connector.get_env_or_option("test", nil, nil).should == "rspec_env_test" - - ENV.delete("test") - end - - it "should return the config option if set" do - config.expects(:pluginconf).returns({"test" => "rspec_test"}).twice - connector.get_env_or_option("test", "test", "test").should == "rspec_test" - end - - it "should return default if nothing else matched" do - config.expects(:pluginconf).returns({}).once - connector.get_env_or_option("test", "test", "test").should == "test" - end - - it "should raise an error if no default is supplied" do - config.expects(:pluginconf).returns({}).once - - expect { - connector.get_env_or_option("test", "test") - }.to raise_error("No test environment or plugin.test configuration option given") - end - end - - describe "#get_option" do - before :each do - # realize the connector let so that we can unstub it - connector - Activemq.any_instance.unstub(:get_option) - end - - it "should return the config option if set" do - config.expects(:pluginconf).returns({"test" => "rspec_test"}).twice - connector.get_option("test").should == "rspec_test" - end - - it "should return default option was not found" do - config.expects(:pluginconf).returns({}).once - connector.get_option("test", "test").should == "test" - end - - it "should raise an error if no default is supplied" do - config.expects(:pluginconf).returns({}).once - - expect { - connector.get_option("test") - }.to raise_error("No plugin.test configuration option given") - end - end - - describe "#get_bool_option" do - before :each do - # realize the connector let so that we can unstub it - connector - Activemq.any_instance.unstub(:get_bool_option) - end - - it "should use Util::str_to_bool to translate a boolean value found in the config" do - config.expects(:pluginconf).returns({"rspec" => "true"}) - Util.expects(:str_to_bool).with("true").returns(true) - - connector.get_bool_option("rspec", "true").should be_true - end - end - end - end -end diff --git a/spec/unit/mcollective/connector/rabbitmq_spec.rb b/spec/unit/mcollective/connector/rabbitmq_spec.rb deleted file mode 100644 index bc4b3401..00000000 --- a/spec/unit/mcollective/connector/rabbitmq_spec.rb +++ /dev/null @@ -1,867 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' - -MCollective::PluginManager.clear - -require 'mcollective/connector/rabbitmq' - -# create the stomp error class here as it does not always exist -# all versions of the stomp gem and we do not want to tie tests -# to the stomp gem -module Stomp - module Error - class DuplicateSubscription < RuntimeError; end - end -end - -module MCollective - module Connector - describe Rabbitmq do - - let(:config) do - conf = mock - conf.stubs(:configured).returns(true) - conf.stubs(:identity).returns("rspec") - conf.stubs(:collectives).returns(["mcollective"]) - conf - end - - let(:logger) do - log = mock - log.stubs(:log) - log.stubs(:start) - Log.configure(log) - log - end - - let(:msg) do - m = mock - m.stubs(:base64_encode!) - m.stubs(:payload).returns("msg") - m.stubs(:agent).returns("agent") - m.stubs(:type).returns(:reply) - m.stubs(:collective).returns("mcollective") - m - end - - let(:subscription) do - sub = mock - sub.stubs(:<<).returns(true) - sub.stubs(:include?).returns(false) - sub.stubs(:delete).returns(false) - sub - end - - let(:connection) do - con = mock - con.stubs(:subscribe).returns(true) - con.stubs(:unsubscribe).returns(true) - con - end - - let(:connector) do - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.use_exponential_back_off", "true").returns(true) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.initial_reconnect_delay", 0.01).returns(0.01) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.back_off_multiplier", 2).returns(2) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.max_reconnect_delay", 30.0).returns(30.0) - c = Rabbitmq.new - c.instance_variable_set("@subscriptions", subscription) - c.instance_variable_set("@connection", connection) - c - end - - before do - unless ::Stomp::Error.constants.map{|c| c.to_s}.include?("NoCurrentConnection") - class ::Stomp::Error::NoCurrentConnection < RuntimeError ; end - end - - logger - Config.stubs(:instance).returns(config) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.use_reply_exchange", false).returns(false) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.agents_multiplex", "false").returns(false) - end - - describe "#initialize" do - before :each do - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.use_exponential_back_off", "true").returns(true) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.initial_reconnect_delay", 0.01).returns(0.01) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.back_off_multiplier", 2).returns(2) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.max_reconnect_delay", 30.0).returns(30.0) - end - - it "should set the @config variable" do - c = Rabbitmq.new - c.instance_variable_get("@config").should == config - end - - it "should set @subscriptions to an empty list" do - c = Rabbitmq.new - c.instance_variable_get("@subscriptions").should == [] - end - end - - describe "#connect" do - before :each do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.heartbeat_interval", 0).returns(30) - Rabbitmq.any_instance.stubs(:get_bool_option).with('rabbitmq.stomp_1_0_fallback', true).returns(true) - Rabbitmq.any_instance.stubs(:get_bool_option).with('rabbitmq.base64', 'false').returns(false) - Rabbitmq.any_instance.stubs(:get_option).with('rabbitmq.vhost', '/').returns('rspec') - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.max_reconnect_attempts", 0).returns(5) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.randomize", "false").returns(true) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.backup", "false").returns(true) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.timeout", -1).returns(1) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.connect_timeout", 30).returns(5) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.max_hbrlck_fails", 0).returns(0) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.max_hbread_fails", 2).returns(2) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.base64", 'false').returns(false) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.prompt_user", 'false').returns(false) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.prompt_password", 'false').returns(false) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.size").returns(2) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.host").returns("host1") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.port", 61613).returns(6163) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.pool.1.ssl", "false").returns(false) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.2.host").returns("host2") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.2.port", 61613).returns(6164) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.pool.2.ssl", "false").returns(true) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.pool.2.ssl.fallback", "false").returns(true) - Rabbitmq.any_instance.stubs(:get_env_or_option).with("STOMP_USER", "rabbitmq.pool.1.user", '').returns("user1") - Rabbitmq.any_instance.stubs(:get_env_or_option).with("STOMP_USER", "rabbitmq.pool.2.user", '').returns("user2") - Rabbitmq.any_instance.stubs(:get_env_or_option).with("STOMP_PASSWORD", "rabbitmq.pool.1.password", '').returns("password1") - Rabbitmq.any_instance.stubs(:get_env_or_option).with("STOMP_PASSWORD", "rabbitmq.pool.2.password", '').returns("password2") - Rabbitmq.any_instance.instance_variable_set("@subscriptions", subscription) - Rabbitmq.any_instance.instance_variable_set("@connection", connection) - end - - it "should not try to reconnect if already connected" do - Log.expects(:debug).with("Already connection, not re-initializing connection").once - connector.connect - end - - it "should support new style config" do - ENV.delete("STOMP_USER") - ENV.delete("STOMP_PASSWORD") - - Rabbitmq::EventLogger.expects(:new).returns("logger") - - connector_obj = mock - connector_obj.expects(:new).with(:backup => true, - :back_off_multiplier => 2, - :max_reconnect_delay => 30.0, - :timeout => 1, - :connect_timeout => 5, - :use_exponential_back_off => true, - :max_reconnect_attempts => 5, - :initial_reconnect_delay => 0.01, - :max_hbrlck_fails => 0, - :max_hbread_fails => 2, - :randomize => true, - :reliable => true, - :logger => "logger", - :connect_headers => {}, - :hosts => [{:passcode => 'password1', - :host => 'host1', - :port => 6163, - :ssl => false, - :login => 'user1'}, - {:passcode => 'password2', - :host => 'host2', - :port => 6164, - :ssl => true, - :login => 'user2'} - ]) - - connector.expects(:ssl_parameters).with(2, true).returns(true) - connector.expects(:connection_headers).returns({}) - - connector.instance_variable_set("@connection", nil) - connector.connect(connector_obj) - end - end - - describe "#stomp_version_supports_heartbeat?" do - it "should not be supported with stomp 1.2.9" do - connector.stubs(:stomp_version).returns("1.2.9") - connector.stomp_version_supports_heartbeat? == false - end - - it "should be supported with stomp 1.2.10" do - connector.stubs(:stomp_version).returns("1.2.10") - connector.stomp_version_supports_heartbeat? == true - end - end - - describe "#connection_headers" do - before do - connector.stubs(:stomp_version).returns("1.2.10") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.heartbeat_interval", 0).returns(1) - Rabbitmq.any_instance.stubs(:get_bool_option).with('rabbitmq.stomp_1_0_fallback', true).returns(true) - Rabbitmq.any_instance.stubs(:get_option).with('rabbitmq.vhost', '/').returns('rspec') - end - - it "should default to stomp 1.0" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.heartbeat_interval", 0).returns(0) - connector.connection_headers[:"accept-version"] == "1.0" - end - - it "should support setting the vhost" do - connector.connection_headers[:host].should == "rspec" - end - - it "should log an informational message about not using Stomp 1.1" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.heartbeat_interval", 0).returns(0) - Log.expects(:info).with(regexp_matches(/without STOMP 1.1 heartbeats/)) - connector.connection_headers - end - - it "should not log an informational message about not using Stomp 1.1 if the gem won't support it" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.heartbeat_interval", 0).returns(0) - connector.stubs(:stomp_version).returns("1.0.0") - Log.expects(:info).with(regexp_matches(/without STOMP 1.1 heartbeats/)).never - connector.connection_headers - end - - it "should not support stomp 1.1 with older versions of the stomp gem" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.heartbeat_interval", 0).returns(30) - connector.expects(:stomp_version).returns("1.0.0").once - expect { connector.connection_headers }.to raise_error("Setting STOMP 1.1 properties like heartbeat intervals require at least version 1.2.10 of the STOMP gem") - end - - it "should force the heartbeat to min 30 seconds" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.heartbeat_interval", 0).returns(30) - connector.connection_headers[:"heart-beat"].should == "30500,29500" - end - - it "should default to 1.0 and 1.1 support" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.heartbeat_interval", 0).returns(30) - connector.connection_headers[:"accept-version"].should == "1.1,1.0" - end - - it "should support stomp 1.1 only operation" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.heartbeat_interval", 0).returns(30) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.stomp_1_0_fallback", true).returns(false) - connector.connection_headers[:"accept-version"].should == "1.1" - end - end - - describe "#ssl_paramaters" do - before :each do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.host").returns("host1") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.port").returns("6164") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.user").returns("user1") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.password").returns("password1") - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.pool.1.ssl", false).returns(true) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.ciphers", false).returns(false) - end - - it "should ensure all settings are provided" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.cert", false).returns("rspec") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.key", false).returns(nil) - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.ca", false).returns(nil) - - expect { connector.ssl_parameters(1, false) }.to raise_error("cert, key and ca has to be supplied for verified SSL mode") - end - - it "should verify the ssl files exist" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.cert", false).returns("rspec") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.key", false).returns('rspec.key') - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.ca", false).returns('rspec1.ca,rspec2.ca') - - connector.expects(:get_key_file).returns("rspec.key").at_least_once - connector.expects(:get_cert_file).returns("rspec.cert").at_least_once - - File.expects(:exist?).with("rspec.cert").twice.returns(true) - File.expects(:exist?).with("rspec.key").twice.returns(true) - File.expects(:exist?).with("rspec1.ca").twice.returns(true) - File.expects(:exist?).with("rspec2.ca").twice.returns(false) - - expect { connector.ssl_parameters(1, false) }.to raise_error("Cannot find CA file rspec2.ca") - - connector.ssl_parameters(1, true).should == true - end - - it "should support fallback mode when there are errors" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.cert", false).returns("rspec") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.key", false).returns('rspec.key') - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.ca", false).returns('rspec1.ca,rspec2.ca') - - connector.ssl_parameters(1, true).should == true - end - - it "should fail if fallback isnt enabled" do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.cert", false).returns("rspec") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.key", false).returns('rspec.key') - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.ca", false).returns('rspec1.ca,rspec2.ca') - - expect { connector.ssl_parameters(1, false) }.to raise_error - end - - context 'ciphers' do - before :each do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.cert", false).returns("rspec") - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.key", false).returns('rspec.key') - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.ca", false).returns('rspec1.ca,rspec2.ca') - File.stubs(:exist?).returns(true) - end - - it 'should not set ciphers by default' do - parameters = connector.ssl_parameters(1, false) - parameters.ciphers.should == false - end - - it 'should support setting of ciphers' do - Rabbitmq.any_instance.stubs(:get_option).with("rabbitmq.pool.1.ssl.ciphers", false).returns('TLSv127:!NUTS') - parameters = connector.ssl_parameters(1, false) - parameters.ciphers.should == 'TLSv127:!NUTS' - end - end - end - - describe "#get_key_file" do - it "should return the filename from the environment variable" do - ENV["MCOLLECTIVE_RABBITMQ_POOL2_SSL_KEY"] = "/path/to/rspec/env" - connector.get_key_file(2).should == "/path/to/rspec/env" - end - - it "should return the filename define in the config file if the environment variable doesn't exist" do - ENV.delete("MCOLLECTIVE_RABBITMQ_POOL2_SSL_KEY") - connector.expects(:get_option).with("rabbitmq.pool.2.ssl.key", false).returns("/path/to/rspec/conf") - connector.get_key_file(2).should == "/path/to/rspec/conf" - end - end - - describe "#get_cert_file" do - it "shold return the filename from the environment variable" do - ENV["MCOLLECTIVE_RABBITMQ_POOL2_SSL_CERT"] = "/path/to/rspec/env" - connector.get_cert_file(2).should == "/path/to/rspec/env" - end - - it "should return the filename defined in the config file if the environment variable doesn't exist" do - ENV.delete("MCOLLECTIVE_RABBITMQ_POOL2_SSL_CERT") - connector.expects(:get_option).with("rabbitmq.pool.2.ssl.cert", false).returns("/path/to/rspec/conf") - connector.get_cert_file(2).should == "/path/to/rspec/conf" - end - end - - describe '#exponential_back_off' do - it "should not do anything when use_exponential_back_off is off" do - connector.instance_variable_set(:@use_exponential_back_off, false) - connector.exponential_back_off.should == nil - end - - it 'should return values of the expected sequence on subsequent calls' do - connector.instance_variable_set(:@use_exponential_back_off, true) - connector.instance_variable_set(:@initial_reconnect_delay, 5.0) - connector.instance_variable_set(:@back_off_multiplier, 2) - connector.instance_variable_set(:@max_reconnect_delay, 30.0) - connector.instance_variable_set(:@reconnect_delay, 5.0) - - connector.exponential_back_off.should == 5 - connector.exponential_back_off.should == 10 - connector.exponential_back_off.should == 20 - connector.exponential_back_off.should == 30 - connector.exponential_back_off.should == 30 - end - end - - describe "#receive" do - it "should receive from the middleware" do - payload = mock - payload.stubs(:command).returns("MESSAGE") - payload.stubs(:body).returns("msg") - payload.stubs(:headers).returns("headers") - - connection.expects(:receive).returns(payload) - - Message.expects(:new).with("msg", payload, :base64 => true, :headers => "headers").returns("message") - connector.instance_variable_set("@base64", true) - - received = connector.receive - received.should == "message" - end - - it "should sleep and retry if recieving while disconnected" do - payload = mock - payload.stubs(:command).returns("MESSAGE") - payload.stubs(:body).returns("msg") - payload.stubs(:headers).returns("headers") - - Message.stubs(:new).returns("rspec") - connection.expects(:receive).raises(::Stomp::Error::NoCurrentConnection).returns(payload).twice - connector.expects(:sleep).with(1) - - connector.receive.should == "rspec" - end - - it "should raise an error on failure to receive a frame" do - connection.expects(:receive).returns(nil) - - expect { connector.receive }.to raise_error(MessageNotReceived, /No message received from RabbitMQ./) - end - - it "should log and raise UnexpectedMessageType on non-MESSAGE frames" do - payload = mock - payload.stubs(:command).returns("ERROR") - payload.stubs(:body).returns("Out of cheese exception") - payload.stubs(:headers).returns("headers") - - connection.expects(:receive).returns(payload) - - Message.stubs(:new) - - Log.expects(:debug).with('Waiting for a message from RabbitMQ') - Log.expects(:debug).with('Unexpected \'ERROR\' frame. Headers: "headers" Body: "Out of cheese exception"') - expect { connector.receive }.to raise_error(UnexpectedMessageType, /Received frame of type 'ERROR' expected 'MESSAGE'/) - end - end - - describe "#publish" do - before :each do - connection.stubs(:publish).with("test", "msg", {}).returns(true) - end - - it "should base64 encode a message if configured to do so" do - connector.instance_variable_set("@base64", true) - connector.expects(:target_for).returns({:name => "test", :headers => {}}) - connection.expects(:publish).with("test", "msg", {}) - msg.expects(:base64_encode!) - - connector.publish(msg) - end - - it "should not base64 encode if not configured to do so" do - connector.instance_variable_set("@base64", false) - connector.expects(:target_for).returns({:name => "test", :headers => {}}) - connection.expects(:publish).with("test", "msg", {}) - msg.expects(:base64_encode!).never - - connector.publish(msg) - end - - it "should publish the correct message to the correct target with msgheaders" do - connection.expects(:publish).with("test", "msg", {}).once - connector.expects(:target_for).returns({:name => "test", :headers => {}}) - - connector.publish(msg) - end - - it "should publish direct messages based on discovered_hosts" do - msg = mock - msg.stubs(:base64_encode!) - msg.stubs(:payload).returns("msg") - msg.stubs(:agent).returns("agent") - msg.stubs(:collective).returns("mcollective") - msg.stubs(:type).returns(:direct_request) - msg.stubs(:reply_to).returns("/topic/mcollective") - msg.stubs(:ttl).returns(60) - msg.expects(:discovered_hosts).returns(["one", "two"]) - - connection.expects(:publish).with('/exchange/mcollective_directed/one', - 'msg', - { 'reply-to' => '/topic/mcollective', - 'expiration' => '70000', - 'mc_sender' => 'rspec', - }) - connection.expects(:publish).with('/exchange/mcollective_directed/two', - 'msg', - { 'reply-to' => '/topic/mcollective', - 'expiration' => '70000', - 'mc_sender' => 'rspec', - }) - - connector.publish(msg) - end - end - - describe "#subscribe" do - it "should handle duplicate subscription errors" do - connection.expects(:subscribe).raises(::Stomp::Error::DuplicateSubscription) - Log.expects(:error).with(regexp_matches(/already had a matching subscription, ignoring/)) - connector.subscribe("test", :broadcast, "mcollective") - end - - it "should use the make_target correctly" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}}) - connector.subscribe("test", :broadcast, "mcollective") - end - - it "should check for existing subscriptions" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - subscription.expects("include?").with("rspec").returns(false) - connection.expects(:subscribe).never - - connector.subscribe("test", :broadcast, "mcollective") - end - - it "should subscribe to the middleware" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - connection.expects(:subscribe).with("test", {}, "rspec") - connector.subscribe("test", :broadcast, "mcollective") - end - - it "should add to the list of subscriptions" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - subscription.expects(:<<).with("rspec") - connector.subscribe("test", :broadcast, "mcollective") - end - - it "should not normally subscribe to :reply messages" do - connection.expects(:subscribe).never - connector.subscribe("test", :reply, "mcollective") - end - - it "should subscribe to :reply messages when use_reply_exchange is set" do - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.use_reply_exchange", false).returns(true) - connector.expects(:make_target).with("test", :reply, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - connection.expects(:subscribe).with("test", {}, "rspec").once - - connector.subscribe("test", :reply, "mcollective") - end - end - - describe "#unsubscribe" do - it "should use make_target correctly" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}}) - connector.unsubscribe("test", :broadcast, "mcollective") - end - - it "should unsubscribe from the target" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - connection.expects(:unsubscribe).with("test", {}, "rspec").once - - connector.unsubscribe("test", :broadcast, "mcollective") - end - - it "should delete the source from subscriptions" do - connector.expects(:make_target).with("test", :broadcast, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - subscription.expects(:delete).with("rspec").once - - connector.unsubscribe("test", :broadcast, "mcollective") - end - - it "should not normally unsubscribe from :reply messages" do - connection.expects(:unsubscribe).never - connector.unsubscribe("test", :reply, "mcollective") - end - - it "should unsubscribe from :reply messages when use_reply_exchange is set" do - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.use_reply_exchange", false).returns(true) - connector.expects(:make_target).with("test", :reply, "mcollective").returns({:name => "test", :headers => {}, :id => "rspec"}) - connection.expects(:unsubscribe).with("test", {}, "rspec").once - - connector.unsubscribe("test", :reply, "mcollective") - end - end - - describe "#target_for" do - it "should create reply targets based on reply-to headers in requests" do - message = mock - message.expects(:type).returns(:reply) - message.expects(:ttl).returns(60) - - request = mock - request.expects(:headers).returns({"reply-to" => "foo"}) - - message.expects(:request).returns(request) - - connector.target_for(message).should == { - :name => "foo", - :headers => { - "expiration" => "70000", - 'mc_sender' => 'rspec', - }, - :id => "", - } - end - - it "should create new request targets" do - message = mock - message.expects(:type).returns(:request).times(3) - message.expects(:agent).returns("rspecagent") - message.expects(:collective).returns("mcollective") - message.expects(:reply_to).returns("/topic/rspec") - message.expects(:ttl).returns(60) - - connector.expects(:make_target).with("rspecagent", :request, "mcollective", "/topic/rspec", nil).returns({:name => "", :headers => {}, :id => nil}) - connector.target_for(message) - end - - it "should support direct requests" do - message = mock - message.expects(:type).returns(:direct_request).times(3) - message.expects(:agent).returns("rspecagent") - message.expects(:collective).returns("mcollective") - message.expects(:reply_to).returns("/topic/rspec") - message.expects(:ttl).returns(60) - - connector.expects(:make_target).with("rspecagent", :direct_request, "mcollective", "/topic/rspec", nil).returns({:name => "", :headers => {}, :id => nil}) - connector.target_for(message) - end - - it "should fail for unknown message types" do - message = mock - message.stubs(:type).returns(:fail) - - expect { - connector.target_for(message) - }.to raise_error("Don't now how to create a target for message type fail") - end - end - - describe "#disconnect" do - it "should disconnect from the stomp connection" do - connection.expects(:disconnect) - connector.disconnect - connector.connection.should == nil - end - end - - describe "#make_target" do - context 'rabbitmq.use_reply_exchange' do - context 'default (false) default (false)' do - before :each do - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.use_reply_exchange", false).returns(false) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.agents_multiplex", "false").returns(false) - end - - it "should create correct targets" do - connector.make_target("test", :reply, "mcollective").should eq({ - :name => "/temp-queue/mcollective_reply_test", - :headers => {}, - :id => "mcollective_test_replies", - }) - connector.make_target("test", :broadcast, "mcollective").should eq({ - :name => "/exchange/mcollective_broadcast/test", - :headers => { "reply-to" => "/temp-queue/mcollective_reply_test" }, - :id => "mcollective_broadcast_test" - }) - connector.make_target("test", :request, "mcollective").should eq({ - :name => "/exchange/mcollective_broadcast/test", - :headers => { "reply-to" => "/temp-queue/mcollective_reply_test" }, - :id => "mcollective_broadcast_test", - }) - connector.make_target("test", :direct_request, "mcollective", nil, "rspec").should eq({ - :headers => { "reply-to" => "/temp-queue/mcollective_reply_test" }, - :name => "/exchange/mcollective_directed/rspec", - :id => nil - }) - connector.make_target("test", :directed, "mcollective").should eq({ - :name => "/exchange/mcollective_directed/rspec", - :headers => {}, - :id => "mcollective_rspec_directed_to_identity", - }) - connector.make_target("test", :request, "mcollective", "/topic/rspec", "rspec").should eq({ - :headers => { "reply-to" => "/topic/rspec" }, - :name => "/exchange/mcollective_broadcast/test", - :id => "mcollective_broadcast_test", - }) - end - end - - context 'true false' do - before :each do - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.use_reply_exchange", false).returns(true) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.agents_multiplex", "false").returns(false) - end - - it "should create correct targets" do - Client.stubs(:request_sequence).returns(42) - connector.make_target("test", :reply, "mcollective").should eq({ - :name => "/exchange/mcollective_reply/rspec_#{$$}_42", - :headers => {}, - :id => "mcollective_test_replies", - }) - connector.make_target("test", :broadcast, "mcollective").should eq({ - :name => "/exchange/mcollective_broadcast/test", - :headers => { "reply-to" => "/exchange/mcollective_reply/rspec_#{$$}_42" }, - :id => "mcollective_broadcast_test" - }) - connector.make_target("test", :request, "mcollective").should eq({ - :name => "/exchange/mcollective_broadcast/test", - :headers => { "reply-to" => "/exchange/mcollective_reply/rspec_#{$$}_42" }, - :id => "mcollective_broadcast_test", - }) - connector.make_target("test", :direct_request, "mcollective", nil, "rspec").should eq({ - :headers => { "reply-to" => "/exchange/mcollective_reply/rspec_#{$$}_42" }, - :name => "/exchange/mcollective_directed/rspec", - :id => nil - }) - connector.make_target("test", :directed, "mcollective").should eq({ - :name => "/exchange/mcollective_directed/rspec", - :headers => {}, - :id => "mcollective_rspec_directed_to_identity", - }) - connector.make_target("test", :request, "mcollective", "/topic/rspec", "rspec").should eq({ - :headers => { "reply-to" => "/topic/rspec" }, - :name => "/exchange/mcollective_broadcast/test", - :id => "mcollective_broadcast_test", - }) - end - end - - context 'true true' do - before :each do - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.use_reply_exchange", false).returns(true) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.agents_multiplex", "false").returns(true) - end - - it "should create correct targets" do - Client.stubs(:request_sequence).returns(42) - connector.make_target("test", :reply, "mcollective").should eq({ - :name => "/exchange/mcollective_reply/rspec_#{$$}_42", - :headers => {}, - :id => "mcollective_test_replies", - }) - connector.make_target("test", :broadcast, "mcollective").should eq({ - :name => "/exchange/mcollective_broadcast", - :headers => { "reply-to" => "/exchange/mcollective_reply/rspec_#{$$}_42" }, - :id => "mcollective_broadcast" - }) - connector.make_target("test", :request, "mcollective").should eq({ - :name => "/exchange/mcollective_broadcast", - :headers => { "reply-to" => "/exchange/mcollective_reply/rspec_#{$$}_42" }, - :id => "mcollective_broadcast", - }) - connector.make_target("test", :direct_request, "mcollective", nil, "rspec").should eq({ - :headers => { "reply-to" => "/exchange/mcollective_reply/rspec_#{$$}_42" }, - :name => "/exchange/mcollective_directed/rspec", - :id => nil - }) - connector.make_target("test", :directed, "mcollective").should eq({ - :name => "/exchange/mcollective_directed/rspec", - :headers => {}, - :id => "mcollective_rspec_directed_to_identity", - }) - connector.make_target("test", :request, "mcollective", "/topic/rspec", "rspec").should eq({ - :headers => { "reply-to" => "/topic/rspec" }, - :name => "/exchange/mcollective_broadcast", - :id => "mcollective_broadcast", - }) - end - end - - context 'false (default) true' do - before :each do - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.use_reply_exchange", false).returns(false) - Rabbitmq.any_instance.stubs(:get_bool_option).with("rabbitmq.agents_multiplex", "false").returns(true) - end - it "should create correct targets" do - connector.make_target("test", :reply, "mcollective").should eq({ - :name => "/temp-queue/mcollective_reply_test", - :headers => {}, - :id => "mcollective_test_replies", - }) - connector.make_target("test", :broadcast, "mcollective").should eq({ - :name => "/exchange/mcollective_broadcast", - :headers => { "reply-to" => "/temp-queue/mcollective_reply_test" }, - :id => "mcollective_broadcast" - }) - connector.make_target("test", :request, "mcollective").should eq({ - :name => "/exchange/mcollective_broadcast", - :headers => { "reply-to" => "/temp-queue/mcollective_reply_test" }, - :id => "mcollective_broadcast", - }) - connector.make_target("test", :direct_request, "mcollective", nil, "rspec").should eq({ - :headers => { "reply-to" => "/temp-queue/mcollective_reply_test" }, - :name => "/exchange/mcollective_directed/rspec", - :id => nil - }) - connector.make_target("test", :directed, "mcollective").should eq({ - :name => "/exchange/mcollective_directed/rspec", - :headers => {}, - :id => "mcollective_rspec_directed_to_identity", - }) - connector.make_target("test", :request, "mcollective", "/topic/rspec", "rspec").should eq({ - :headers => { "reply-to" => "/topic/rspec" }, - :name => "/exchange/mcollective_broadcast", - :id => "mcollective_broadcast", - }) - end - end - end - - it "should raise an error for unknown collectives" do - expect { - connector.make_target("test", :broadcast, "foo") - }.to raise_error("Unknown collective 'foo' known collectives are 'mcollective'") - end - - it "should raise an error for unknown types" do - expect { - connector.make_target("test", :test, "mcollective") - }.to raise_error("Unknown target type test") - end - end - - - describe "#get_env_or_option" do - it "should return the environment variable if set" do - ENV["test"] = "rspec_env_test" - - connector.get_env_or_option("test", nil, nil).should == "rspec_env_test" - - ENV.delete("test") - end - - it "should return the config option if set" do - config.expects(:pluginconf).returns({"test" => "rspec_test"}).twice - connector.get_env_or_option("test", "test", "test").should == "rspec_test" - end - - it "should return default if nothing else matched" do - config.expects(:pluginconf).returns({}).once - connector.get_env_or_option("test", "test", "test").should == "test" - end - - it "should raise an error if no default is supplied" do - config.expects(:pluginconf).returns({}).once - - expect { - connector.get_env_or_option("test", "test") - }.to raise_error("No test environment or plugin.test configuration option given") - end - end - - describe "#get_option" do - before :each do - # realize the connector let so that we can unstub it - connector - Rabbitmq.any_instance.unstub(:get_option) - end - - it "should return the config option if set" do - config.expects(:pluginconf).returns({"test" => "rspec_test"}).twice - connector.get_option("test").should == "rspec_test" - end - - it "should return default option was not found" do - config.expects(:pluginconf).returns({}).once - connector.get_option("test", "test").should == "test" - end - - it "should raise an error if no default is supplied" do - config.expects(:pluginconf).returns({}).once - - expect { - connector.get_option("test") - }.to raise_error("No plugin.test configuration option given") - end - end - - describe "#get_bool_option" do - before :each do - # realize the connector let so that we can unstub it - connector - Rabbitmq.any_instance.unstub(:get_bool_option) - end - - it "should use Util::str_to_bool to translate a boolean value found in the config" do - config.expects(:pluginconf).returns({"rspec" => "true"}) - Util.expects(:str_to_bool).with("true").returns(true) - - connector.get_bool_option("rspec", "true").should be_true - end - end - end - end -end diff --git a/spec/unit/mcollective/monkey_patches_spec.rb b/spec/unit/mcollective/monkey_patches_spec.rb index 9c8bdf70..20bbc042 100644 --- a/spec/unit/mcollective/monkey_patches_spec.rb +++ b/spec/unit/mcollective/monkey_patches_spec.rb @@ -13,8 +13,10 @@ end it 'explicitly disable SSLv2 ciphers using the ! prefix so they cannot be re-added' do - cipher_str = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers] - cipher_str.split(':').should include('!SSLv2') + if OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers] + cipher_str = OpenSSL::SSL::SSLContext::DEFAULT_PARAMS[:ciphers] + cipher_str.split(':').should include('!SSLv2') + end end it 'has no ciphers with version SSLv2 enabled' do diff --git a/spec/unit/mcollective/registration/base_spec.rb b/spec/unit/mcollective/registration/base_spec.rb deleted file mode 100755 index a633cd9f..00000000 --- a/spec/unit/mcollective/registration/base_spec.rb +++ /dev/null @@ -1,123 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' - -module MCollective - module Registration - describe Base do - - let(:connection) { mock } - - before do - @config = mock - @config.stubs(:identity).returns("rspec") - @config.stubs(:main_collective).returns("main_collective") - Config.stubs(:instance).returns(@config) - - @reg = Base.new - end - - describe "#config" do - it "should provide access the main configuration class" do - @reg.config.should == @config - end - end - - describe "#run" do - it "should not start the publish_thread if the registration interval is 0" do - @reg.stubs(:interval).returns(0) - Thread.expects(:new).never - @reg.run(connection).should == false - end - - it "should start the publish_thread" do - @reg.stubs(:interval).returns(1) - Thread.expects(:new).returns(true) - @reg.run(connection).should be_true - end - end - - describe "#identity" do - it "should return the correct identity" do - @reg.config.identity.should == "rspec" - end - end - - describe "#msg_filter" do - it "should target the registration agent" do - @reg.msg_filter["agent"].should == ["registration"] - end - end - - describe "#target_collective" do - it "should return the configured registration_collective" do - @config.expects(:registration_collective).returns("registration").once - @config.expects(:collectives).returns(["main_collective", "registration"]).once - @reg.target_collective.should == "registration" - end - - it "should use the main collective if registration collective is not valid" do - @config.expects(:registration_collective).returns("registration").once - @config.expects(:collectives).returns(["main_collective"]).once - - Log.expects(:warn).with("Sending registration to main_collective: registration is not a valid collective").once - - @reg.target_collective.should == "main_collective" - end - end - - describe "#publish" do - it "should skip registration for empty messages" do - Log.expects(:debug).with("Skipping registration due to nil body") - @reg.publish(nil) - end - - it "should publish via the message object" do - message = mock - message.expects(:encode!) - message.expects(:publish) - message.expects(:requestid).returns("123") - message.expects(:collective).returns("mcollective") - - Message.expects(:new).returns(message) - - Log.expects(:debug).with("Sending registration 123 to collective mcollective") - - @reg.expects(:target_collective).returns("mcollective") - - @reg.publish("message") - end - end - - describe "#body" do - it "should fail if body hasn't been implemented" do - expect { - @reg.body - }.to raise_error - end - end - - describe "#publish_thread" do - before(:each) do - @reg.expects(:loop).returns("looping") - end - - it "should splay if splay is set" do - @reg.stubs(:interval).returns(1) - @config.stubs(:registration_splay).returns(true) - Log.expects(:debug) - @reg.expects(:sleep) - @reg.send(:publish_thread, connection) - end - - it "should not splay if splay isn't set" do - @reg.stubs(:interval).returns(1) - @config.stubs(:registration_splay).returns(false) - Log.expects(:debug).never - @reg.expects(:sleep).never - @reg.send(:publish_thread, connection) - end - end - end - end -end diff --git a/spec/unit/mcollective/runner_spec.rb b/spec/unit/mcollective/runner_spec.rb deleted file mode 100644 index f0680db8..00000000 --- a/spec/unit/mcollective/runner_spec.rb +++ /dev/null @@ -1,360 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' -require 'mcollective/runner' - -module MCollective - describe Runner do - let(:config) do - c = mock - c.stubs(:loadconfig) - c.stubs(:configured).returns(true) - c.stubs(:mode=) - c.stubs(:direct_addressing).returns(false) - c.stubs(:registerinterval).returns(1) - c.stubs(:soft_shutdown).returns(false) - c - end - - let(:stats) { mock } - - let(:security) do - s = mock - s.stubs(:initiated_by=) - s - end - - let(:connector) do - c = mock - c.stubs(:connect) - c.stubs(:disconnect) - c - end - - let(:agents) { mock } - - let(:receiver_thread) do - rt = mock - rt - end - - before :each do - Config.stubs(:instance).returns(config) - PluginManager.stubs(:[]).with('global_stats').returns(stats) - PluginManager.stubs(:[]).with('security_plugin').returns(security) - PluginManager.stubs(:[]).with('connector_plugin').returns(connector) - Agents.stubs(:new).returns(agents) - end - - describe 'initialize' do - it 'should set up the signal handlers when not on windows' do - Util.stubs(:windows).returns(false) - Signal.expects(:trap).with('USR1') - Signal.expects(:trap).with('USR2') - Signal.expects(:trap).with('WINCH') - Runner.new(nil) - end - - it 'should not set up the signal handlers when on windows' do - Util.stubs(:windows?).returns(true) - Signal.expects(:trap).with('USR1').never - Signal.expects(:trap).with('USR2').never - Signal.expects(:trap).with('WINCH').never - - Util.expects(:setup_windows_sleeper) - Runner.new(nil) - end - - it 'should log a message when it cannot initialize the runner' do - Config.stubs(:instance).raises('failure') - Log.expects(:error).times(3) - expect { - Runner.new(nil) - }.to raise_error 'failure' - end - end - - describe '#main_loop' do - let(:runner) do - Runner.new(nil) - end - - let(:receiver_thread) do - rt = mock - rt.stubs(:alive?).returns(false) - rt - end - - let(:agent_thread) do - at = mock - at.stubs(:alive?).returns(true) - at - end - - before :each do - Log.stubs(:debug) - Log.stubs(:info) - Log.stubs(:warn) - Log.stubs(:error) - runner.stubs(:start_receiver_thread).returns(receiver_thread) - end - - context 'stopping' do - it 'should stop normally' do - receiver_thread.stubs(:alive?).returns(true) - runner.instance_variable_set(:@state, :stopping) - runner.expects(:stop_threads) - runner.main_loop - end - - it 'should do a soft_shutdown' do - config.stubs(:soft_shutdown).returns(true) - Util.stubs(:windows?).returns(false) - runner.instance_variable_set(:@state, :stopping) - runner.instance_variable_set(:@agent_threads, [agent_thread]) - runner.expects(:soft_shutdown) - runner.expects(:stop_threads) - runner.main_loop - end - end - - # Because paused is not a terminal state, we raise after testing pause - # which forces a run. This means checks like #stop_threads will happen twice - context 'pausing' do - before :each do - receiver_thread.stubs(:alive?).returns(false) - end - - it 'should pause' do - runner.instance_variable_set(:@state, :pausing) - runner.expects(:stop_threads).twice - runner.expects(:sleep).with(0.1).raises("error") - runner.main_loop - end - - it 'should resume' do - runner.instance_variable_set(:@state, :unpausing) - runner.expects(:stop_threads) - runner.expects(:start_receiver_thread).returns(receiver_thread) - runner.expects(:sleep).with(0.1).raises("error") - runner.main_loop - end - end - end - - context 'action methods' do - let(:runner) do - Runner.new(nil) - end - - describe '#stop' do - it 'should change the state to stopping' do - runner.instance_variable_set(:@state, :running) - runner.stop - runner.state.should == :stopping - end - end - - describe '#pause' do - it 'should change the state to pausing' do - runner.instance_variable_set(:@state, :running) - runner.pause - runner.state.should == :pausing - end - - it 'should fail if state is not running' do - Log.expects(:error).with('Cannot pause MCollective while not in a running state') - runner.pause - end - end - - describe '#resume' do - it 'should change the state to unpausing' do - runner.instance_variable_set(:@state, :paused) - runner.resume - runner.state.should == :unpausing - end - - it 'should fail if state is not paused' do - runner.instance_variable_set(:@state, :running) - Log.expects(:error).with('Cannot unpause MCollective when it is not paused') - runner.resume - end - end - - describe '#start_receiver_thread' do - let(:current) do - mock('current thread') - end - - before :each do - Thread.stubs(:new).yields - Thread.stubs(:current).returns(current) - end - - it 'should reraise exceptions' do - runner.instance_variable_set(:@state, :running) - runner.stubs(:receiver_thread).raises('test error') - current.expects(:raise).once - runner.send(:start_receiver_thread) - end - end - - describe '#receiver_thread' do - let(:runner) do - Runner.new(nil) - end - - let(:registration_agent) do - ra = mock - ra.stubs(:run) - ra - end - - let(:request) do - r = mock - r.stubs(:agent).returns("rspec") - r - end - - before :each do - PluginManager.stubs(:[]).with("registration_plugin").returns(registration_agent) - Data.stubs(:load_data_sources) - Util.stubs(:subscribe_to_direct_addressing_queue) - end - - it 'should receive a message and spawn an agent thread' do - runner.expects(:receive).returns(request) - runner.expects(:agentmsg).with(request) - runner.instance_variable_set(:@exit_receiver_thread, true) - runner.send(:receiver_thread) - end - - it 'should subscribe to the direct addressing queue if direct_addressing is configured' do - runner.expects(:receive).returns(request) - runner.expects(:agentmsg).with(request) - config.stubs(:direct_addressing).returns(true) - Util.expects(:subscribe_to_direct_addressing_queue) - runner.instance_variable_set(:@exit_receiver_thread, true) - runner.send(:receiver_thread) - end - - it 'should load agents before data plugins' do - load_order = sequence('load_order') - Agents.expects(:new).in_sequence(load_order) - Data.expects(:load_data_sources).in_sequence(load_order) - runner.expects(:receive).returns(request) - runner.expects(:agentmsg).with(request) - runner.instance_variable_set(:@exit_receiver_thread, true) - runner.send(:receiver_thread) - end - - it 'should warn when a received message has expired' do - runner.expects(:receive).raises(MsgTTLExpired) - runner.instance_variable_set(:@exit_receiver_thread, true) - Log.expects(:warn) - runner.send(:receiver_thread) - end - - it 'should log if a message was received by not directed at the server' do - runner.expects(:receive).raises(NotTargettedAtUs) - runner.instance_variable_set(:@exit_receiver_thread, true) - Log.expects(:info) - runner.send(:receiver_thread) - end - - it 'should back off on MessageNotReceived and UnexpectedMessageType' do - runner.expects(:receive).raises(MessageNotReceived.new(1)) - runner.instance_variable_set(:@exit_receiver_thread, true) - Log.expects(:warn) - Log.expects(:info).with("sleeping for suggested 1 seconds") - runner.expects(:sleep).with(1) - runner.send(:receiver_thread) - end - end - end - - context "soft_shutdown" do - let(:runner) do - Runner.new(nil) - end - - before(:each) do - config.stubs(:soft_shutdown).returns(true) - end - - describe "#soft_shutdown" do - it "should not shutdown if the timeout is set and <= 0" do - config.stubs(:soft_shutdown_timeout).returns(0) - Log.expects(:warn).twice - runner.expects(:windows_soft_shutdown).never - runner.expects(:posix_soft_shutdown).never - runner.send(:soft_shutdown) - end - - it "should call the windows soft_shutdown on Windows" do - config.stubs(:soft_shutdown_timeout).returns(1) - Util.stubs(:windows?).returns(true) - runner.expects(:windows_soft_shutdown) - runner.expects(:posix_soft_shutdown).never - runner.send(:soft_shutdown) - end - - it "should call the posix soft_shutdown when not on windows" do - config.stubs(:soft_shutdown_timeout).returns(1) - Util.stubs(:windows?).returns(false) - runner.expects(:windows_soft_shutdown).never - runner.expects(:posix_soft_shutdown) - runner.send(:soft_shutdown) - end - end - - describe "#windows_soft_shutdown" do - it "should not shutdown if no timeout is set" do - runner.expects(:shutdown_with_timeout).never - Log.expects(:warn).times(3) - runner.send(:windows_soft_shutdown, nil) - end - - it "should shutdown in a timeout" do - runner.expects(:shutdown_with_timeout) - runner.send(:windows_soft_shutdown, 1) - end - end - - describe "#posix_soft_shutdown" do - it "should shutdown without a timeout" do - runner.expects(:stop_agent_threads) - runner.send(:posix_soft_shutdown, nil) - end - - it "should shutdown with a timeout" do - runner.expects(:shutdown_with_timeout) - runner.send(:posix_soft_shutdown, 1) - end - end - - describe "#shutdown_with_timeout" do - it "should timeout if it can't stop agent threads in time" do - Timeout.expects(:timeout).with(1) - runner.send(:shutdown_with_timeout, 1) - end - end - - describe "#stop_agent_threads" do - let(:agent_thread) do - at = mock - at.stubs(:alive?).returns(true) - at - end - - it "should stop all agent threads" do - runner.instance_variable_set(:@agent_threads, [agent_thread]) - Log.stubs(:debug) - agent_thread.expects(:join) - runner.send(:stop_agent_threads) - end - end - end - end -end diff --git a/spec/unit/mcollective/security/aes_security_spec.rb b/spec/unit/mcollective/security/aes_security_spec.rb deleted file mode 100644 index b12afd82..00000000 --- a/spec/unit/mcollective/security/aes_security_spec.rb +++ /dev/null @@ -1,211 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' -require 'mcollective/security/aes_security' - -module MCollective - module Security - # Clear the PluginManager so that security plugin tests do not conflict - PluginManager.clear - describe Aes_security do - let(:pluginconf) do - {"aes.client_cert_dir" => "testing"} - end - - let(:config) do - conf = mock - conf.stubs(:identity).returns("test") - conf.stubs(:configured).returns(true) - conf.stubs(:pluginconf).returns(pluginconf) - conf - end - - let(:plugin) do - Aes_security.new - end - - let(:msg) do - m = mock - m.stubs(:payload) - m - end - - before :each do - stats = mock("stats") - MCollective::PluginManager << {:type => "global_stats", :class => stats} - MCollective::Config.stubs("instance").returns(config) - MCollective::Log.stubs(:debug) - MCollective::Log.stubs(:warn) - end - - describe "#deserialize" do - let(:safe_payload) { - {:payload => "words", :ttl => 15} - } - - class Sock - attr_reader :size - def initialize size - @size = size - end - - def ==(another_sock) - self.size == another_sock.size - end - end - - let(:unsafe_payload) { - {:payload => Sock.new(10)} - } - - it "should accept marshal by default" do - expect(plugin.deserialize(Marshal.dump(unsafe_payload))).to eq(unsafe_payload) - expect(plugin.deserialize(Marshal.dump(safe_payload))).to eq(safe_payload) - end - - context "yaml" do - before do - pluginconf['aes.serializer'] = 'yaml' - end - - if YAML.respond_to? :safe_load - it "should round-trip yaml with symbols" do - expect(plugin.deserialize(YAML.dump(safe_payload))).to eq(safe_payload) - end - - it "should reject yaml with other objects" do - expect{ plugin.deserialize(YAML.dump(unsafe_payload)) }.to raise_error(Psych::DisallowedClass) - end - else - it "should raise on older Ruby" do - expect{ plugin.deserialize(YAML.dump(safe_payload)) }.to raise_error("YAML.safe_load not supported by Ruby #{RUBY_VERSION}. Please update to Ruby 2.1+.") - end - end - end - end - - describe "#decodemsg" do - let(:body) do - {:sslpubkey => "ssl_public_key", - :callerid => "cert=testing", - :requestid => 1} - end - - before :each do - pluginconf["aes.learn_pubkeys"] = "1" - plugin.stubs(:should_process_msg?) - plugin.stubs(:deserialize).returns(body) - plugin.stubs(:decrypt) - plugin.stubs(:deserialize).returns(body) - plugin.stubs(:update_secure_property) - end - - it "should not learn the public key if the key has not been passed" do - body.delete(:sslpubkey) - plugin.decodemsg(msg) - File.expects(:exist?).never - File.expects(:open).never - end - - it "should not learn the public key if keyfile is present on disk" do - File.expects(:exist?).with("testing/testing.pem").returns(true) - File.expects(:open).never - plugin.decodemsg(msg) - end - - it "should not learn the key if there is no ca_cert and insecure_learning is false" do - File.expects(:exist?).returns(false) - Log.expects(:warn).with() do |msg| - msg =~ /No CA certificate specified/ - end - expect { - plugin.decodemsg(msg) - }.to raise_error SecurityValidationFailed - end - - it "should not learn the key if the cert cannot be verified against the CA" do - File.expects(:exist?).returns(false) - pluginconf["aes.ca_cert"] = "ca_cert" - plugin.expects(:validate_certificate).with("ssl_public_key", "testing").returns(false) - Log.expects(:warn).with() do |msg| - msg.should match(/Unable to validate certificate/) - end - expect { - plugin.decodemsg(msg) - }.to raise_error SecurityValidationFailed - end - - it "it should learn the public key if insecure_learning is enabled" do - pluginconf["aes.insecure_learning"] = "1" - File.expects(:exist?).returns(false) - Log.expects(:warn).with() do |msg| - msg.should match(/Do NOT use this mode in sensitive environments/) - end - File.expects(:open) - plugin.decodemsg(msg) - end - - it "should learn the public key if the CA can verify the cert" do - File.expects(:exist?).returns(false) - pluginconf["aes.ca_cert"] = "ca_cert" - File.expects(:read).with("testing/testing.pem").returns("ssl_public_key") - plugin.expects(:validate_certificate).with("ssl_public_key", "testing").twice.returns(true) - File.expects(:open) - plugin.decodemsg(msg) - end - end - - describe "#validate_certificate" do - let(:cert) do - mock - end - - let(:ca_cert) do - ca = mock - ca.stubs(:add_file).returns(true) - ca - end - - let(:callerid) do - "rspec_caller" - end - - it "should fail if the cert is not a X509 certificate" do - OpenSSL::X509::Certificate.expects(:new).with("ssl_cert").raises(OpenSSL::X509::CertificateError) - Log.expects(:warn).with() do |msg| - msg.should match(/Received public key that is not a X509 certficate/) - end - plugin.validate_certificate("ssl_cert", callerid).should be_false - end - - it "should fail if the name in the cert doesn't match the callerid" do - OpenSSL::X509::Certificate.expects(:new).with("ssl_cert").returns(cert) - plugin.stubs(:certname_from_certificate).with(cert).returns("not_rspec_caller") - Log.expects(:warn).with() do |msg| - msg.should match(/certname 'rspec_caller' doesn't match certificate 'not_rspec_caller'/) - end - plugin.validate_certificate("ssl_cert", callerid).should be_false - end - - it "should fail if the cert wasn't signed by the CA" do - OpenSSL::X509::Certificate.expects(:new).with("ssl_cert").returns(cert) - plugin.stubs(:certname_from_certificate).with(cert).returns("rspec_caller") - OpenSSL::X509::Store.stubs(:new).returns(ca_cert) - ca_cert.stubs(:verify).with(cert).returns(false) - Log.expects(:warn).with() do |msg| - msg.should match(/Unable to validate certificate/) - end - plugin.validate_certificate("ssl_cert", callerid).should be_false - end - - it "should validate the cert" do - OpenSSL::X509::Certificate.expects(:new).with("ssl_cert").returns(cert) - plugin.stubs(:certname_from_certificate).with(cert).returns("rspec_caller") - OpenSSL::X509::Store.stubs(:new).returns(ca_cert) - ca_cert.stubs(:verify).with(cert).returns(true) - plugin.validate_certificate("ssl_cert", callerid).should be_true - end - end - end - end -end diff --git a/spec/unit/mcollective/security/psk_spec.rb b/spec/unit/mcollective/security/psk_spec.rb deleted file mode 100755 index 15e3bc9d..00000000 --- a/spec/unit/mcollective/security/psk_spec.rb +++ /dev/null @@ -1,161 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' -require 'mcollective/security/psk' - -module MCollective - # Clear the PluginManager so that security plugin tests do not conflict - PluginManager.clear -end - -module MCollective::Security - describe Psk do - before do - @config = mock("config") - @config.stubs(:identity).returns("test") - @config.stubs(:configured).returns(true) - @config.stubs(:pluginconf).returns({"psk" => "12345"}) - - @stats = mock("stats") - - @time = Time.now.to_i - ::Time.stubs(:now).returns(@time) - - MCollective::Log.stubs(:debug).returns(true) - - MCollective::PluginManager << {:type => "global_stats", :class => @stats} - MCollective::Config.stubs("instance").returns(@config) - MCollective::Util.stubs("empty_filter?").returns(false) - - @plugin = Psk.new - end - - describe "#decodemsg" do - it "should correctly decode a message" do - @plugin.stubs("validrequest?").returns(true).once - - msg = mock("message") - msg.stubs(:payload).returns(Marshal.dump({:body => Marshal.dump("foo")})) - msg.stubs(:expected_msgid).returns(nil) - - @plugin.decodemsg(msg).should == {:body=>"foo"} - end - - it "should return nil on failure" do - @plugin.stubs("validrequest?").raises("fail").once - - msg = mock("message") - msg.stubs(:payload).returns(Marshal.dump({:body => Marshal.dump("foo"), :requestid => "123"})) - msg.stubs(:expected_msgid).returns(nil) - - expect { @plugin.decodemsg(msg) }.to raise_error("fail") - end - - it "should not decode messages not addressed to us" do - msg = mock("message") - msg.stubs(:payload).returns(Marshal.dump({:body => Marshal.dump("foo"), :requestid => "456"})) - msg.stubs(:expected_msgid).returns("123") - - expect { - @plugin.decodemsg(msg) - }.to raise_error("Got a message with id 456 but was expecting 123, ignoring message") - - end - - it "should only decode messages addressed to us" do - @plugin.stubs("validrequest?").returns(true).once - - msg = mock("message") - msg.stubs(:payload).returns(Marshal.dump({:body => Marshal.dump("foo"), :requestid => "456"})) - msg.stubs(:expected_msgid).returns("456") - - @plugin.decodemsg(msg).should == {:body=>"foo", :requestid=>"456"} - end - end - - describe "#encodereply" do - it "should correctly Marshal encode the reply" do - @plugin.stubs("create_reply").returns({:test => "test"}) - Marshal.stubs("dump").with("test message").returns("marshal_test_message").once - Marshal.stubs("dump").with({:hash => '2dbeb0d7938a08a34eacd2c1dab25602', :test => 'test'}).returns("marshal_test_reply").once - - @plugin.encodereply("sender", "test message", "requestid", "callerid").should == "marshal_test_reply" - end - end - - describe "#encoderequest" do - it "should correctly Marshal encode the request" do - @plugin.stubs("create_request").returns({:test => "test"}) - Marshal.stubs("dump").with("test message").returns("marshal_test_message").once - Marshal.stubs("dump").with({:hash => '2dbeb0d7938a08a34eacd2c1dab25602', :test => 'test'}).returns("marshal_test_request").once - - @plugin.encoderequest("sender", "test message", "requestid", "filter", "agent", "collective").should == "marshal_test_request" - end - end - - describe "#validrequest?" do - it "should correctly validate requests" do - @stats.stubs(:validated).once - @stats.stubs(:unvalidated).never - @plugin.validrequest?({:body => "foo", :hash => "e83ac78027b77b659a49bccbbcfa4849"}) - end - - it "should raise an exception on failure" do - @stats.stubs(:validated).never - @stats.stubs(:unvalidated).once - expect { @plugin.validrequest?({:body => "foo", :hash => ""}) }.to raise_error("Received an invalid signature in message") - end - end - - describe "#callerid" do - it "should do uid based callerid when unconfigured" do - @plugin.callerid.should == "uid=#{Process.uid}" - end - - it "should support gid based callerids" do - @config.stubs(:pluginconf).returns({"psk.callertype" => "gid"}) - @plugin.callerid.should == "gid=#{Process.gid}" - end - - it "should support group based callerids", :unless => MCollective::Util.windows? do - @config.stubs(:pluginconf).returns({"psk.callertype" => "group"}) - @plugin.callerid.should == "group=#{Etc.getgrgid(Process.gid).name}" - end - - it "should raise an error if the group callerid type is used on windows" do - MCollective::Util.expects("windows?").returns(true) - @config.stubs(:pluginconf).returns({"psk.callertype" => "group"}) - expect { @plugin.callerid }.to raise_error("Cannot use the 'group' callertype for the PSK security plugin on the Windows platform") - end - - it "should support user based callerids" do - @config.stubs(:pluginconf).returns({"psk.callertype" => "user"}) - @plugin.callerid.should == "user=#{Etc.getlogin}" - end - - it "should support identity based callerids" do - @config.stubs(:pluginconf).returns({"psk.callertype" => "identity"}) - @plugin.callerid.should == "identity=test" - end - end - - describe "#makehash" do - it "should return the correct md5 digest" do - @plugin.send(:makehash, "foo").should == "e83ac78027b77b659a49bccbbcfa4849" - end - - it "should fail if no PSK is configured" do - @config.stubs(:pluginconf).returns({}) - expect { @plugin.send(:makehash, "foo") }.to raise_error("No plugin.psk configuration option specified") - end - - it "should support reading the PSK from the environment" do - ENV["MCOLLECTIVE_PSK"] = "54321" - - @plugin.send(:makehash, "foo").should == "d3fb63cc6b1d47cc4b2012df926c2feb" - - ENV.delete("MCOLLECTIVE_PSK") - end - end - end -end diff --git a/spec/unit/mcollective/security/ssl_spec.rb b/spec/unit/mcollective/security/ssl_spec.rb deleted file mode 100644 index 63d4d13d..00000000 --- a/spec/unit/mcollective/security/ssl_spec.rb +++ /dev/null @@ -1,90 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' -require 'mcollective/security/ssl' - -module MCollective - module Security - # Clear the PluginManager so that security plugin tests do not conflict - PluginManager.clear - describe Ssl do - let(:pluginconf) do - {"ssl_server_public" => "server-public.pem", - "ssl_client_private" => "client-private.pem", - "ssl_client_public" => "client_public.pem"} - end - - let(:config) do - conf = mock - conf.stubs(:identity).returns("test") - conf.stubs(:configured).returns(true) - conf.stubs(:pluginconf).returns(pluginconf) - conf - end - - let(:plugin) do - Ssl.new - end - - let(:msg) do - m = mock - m.stubs(:payload) - m - end - - before :each do - stats = mock("stats") - MCollective::PluginManager << {:type => "global_stats", :class => stats} - MCollective::Config.stubs("instance").returns(config) - MCollective::Log.stubs(:debug) - MCollective::Log.stubs(:warn) - end - - describe "#deserialize" do - let(:safe_payload) { - {:payload => "words", :ttl => 15} - } - - class Sock - attr_reader :size - def initialize size - @size = size - end - - def ==(another_sock) - self.size == another_sock.size - end - end - - let(:unsafe_payload) { - {:payload => Sock.new(10)} - } - - it "should accept marshal by default" do - expect(plugin.send(:deserialize, Marshal.dump(unsafe_payload))).to eq(unsafe_payload) - expect(plugin.send(:deserialize, Marshal.dump(safe_payload))).to eq(safe_payload) - end - - context "yaml" do - before do - pluginconf['ssl_serializer'] = 'yaml' - end - - if YAML.respond_to? :safe_load - it "should round-trip yaml with symbols" do - expect(plugin.send(:deserialize, YAML.dump(safe_payload))).to eq(safe_payload) - end - - it "should reject yaml with other objects" do - expect{ plugin.send(:deserialize, YAML.dump(unsafe_payload)) }.to raise_error(Psych::DisallowedClass) - end - else - it "should raise on older Ruby" do - expect{ plugin.send(:deserialize, YAML.dump(safe_payload)) }.to raise_error("YAML.safe_load not supported by Ruby #{RUBY_VERSION}. Please update to Ruby 2.1+.") - end - end - end - end - end - end -end diff --git a/spec/unit/mcollective/unix_daemon_spec.rb b/spec/unit/mcollective/unix_daemon_spec.rb deleted file mode 100755 index d96fadea..00000000 --- a/spec/unit/mcollective/unix_daemon_spec.rb +++ /dev/null @@ -1,85 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' -require 'mcollective/unix_daemon' - -module MCollective - describe UnixDaemon do - describe "#daemonize_runner" do - it "should not run on the windows platform" do - Util.expects("windows?").returns(true) - expect { UnixDaemon.daemonize_runner }.to raise_error("The Unix Daemonizer can not be used on the Windows Platform") - end - - it "should write the pid file if requested", :unless => MCollective::Util.windows? do - f = mock - f.expects(:print).with(Process.pid) - - File.expects(:open).with("/nonexisting", File::CREAT | File::EXCL | File::WRONLY).yields(f) - - r = mock - r.expects(:main_loop) - - Runner.expects(:new).returns(r) - UnixDaemon.expects(:daemonize).yields - - UnixDaemon.daemonize_runner("/nonexisting") - end - - it "should clean a stale pid file", :unless => MCollective::Util.windows? do - f = mock - f.expects(:print).with(Process.pid) - - File.expects(:exist?).with("/nonexisting").twice.returns(true).then.returns(false) - File.expects(:read).with("/nonexisting").returns '1234' - File.expects(:unlink).with("/nonexisting") - Process.expects(:kill).with(0, 1234).raises(Errno::ESRCH) - File.expects(:open).with("/nonexisting", File::CREAT | File::EXCL | File::WRONLY).yields(f).returns true - - r = mock - r.expects(:main_loop) - Runner.expects(:new).returns(r) - UnixDaemon.expects(:daemonize).yields - UnixDaemon.daemonize_runner("/nonexisting") - end - - it "should not write a pid file if the process is running", :unless => MCollective::Util.windows? do - File.expects(:exist?).with("/nonexisting").returns true - File.expects(:read).with("/nonexisting").returns '1234' - Process.expects(:kill).with(0, 1234).returns true - File.expects(:open).never - - UnixDaemon.expects(:daemonize).yields - expect { UnixDaemon.daemonize_runner("/nonexisting") }.to raise_error "Process is already running with PID 1234" - end - - it "should clean an empty pid file", :unless => MCollective::Util.windows? do - f = mock - f.expects(:print).with(Process.pid) - - File.expects(:exist?).with("/nonexisting").twice.returns(true).then.returns(false) - File.expects(:read).with("/nonexisting").returns '' - - File.expects(:unlink).with("/nonexisting") - File.expects(:open).with("/nonexisting", File::CREAT | File::EXCL | File::WRONLY).yields(f).returns true - - r = mock - r.expects(:main_loop) - Runner.expects(:new).returns(r) - UnixDaemon.expects(:daemonize).yields - UnixDaemon.daemonize_runner("/nonexisting") - end - - it "should not write a pid file unless requested", :unless => MCollective::Util.windows? do - r = mock - r.expects(:main_loop) - - UnixDaemon.expects(:daemonize).yields - Runner.expects(:new).returns(r) - File.expects(:open).never - - UnixDaemon.daemonize_runner(nil) - end - end - end -end diff --git a/spec/unit/mcollective/vendor_spec.rb b/spec/unit/mcollective/vendor_spec.rb deleted file mode 100755 index 6e655615..00000000 --- a/spec/unit/mcollective/vendor_spec.rb +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' - -module MCollective - describe Vendor do - describe "#vendor_dir" do - it "should return correct vendor directory" do - specdir = File.dirname(__FILE__) - expected_dir = File.expand_path("#{specdir}/../../../lib/mcollective/vendor") - Vendor.vendor_dir.should == expected_dir - end - end - - describe "#load_entry" do - it "should attempt to load the correct path" do - specdir = File.dirname(__FILE__) - expected_dir = File.expand_path("#{specdir}/../../../lib/mcollective/vendor") - - Class.any_instance.stubs("load").with("#{expected_dir}/foo").once - - Vendor.load_entry("foo") - end - end - - describe "#require_libs" do - it "should require the vendor loader" do - Class.any_instance.stubs("require").with("mcollective/vendor/require_vendored").once - - Vendor.require_libs - end - end - end -end diff --git a/spec/unit/mcollective/windows_daemon_spec.rb b/spec/unit/mcollective/windows_daemon_spec.rb deleted file mode 100755 index 9a29c361..00000000 --- a/spec/unit/mcollective/windows_daemon_spec.rb +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env rspec - -require 'spec_helper' - -module MCollective - if Util.windows? - require 'mcollective/windows_daemon' - - describe WindowsDaemon do - describe "#daemonize_runner" do - it "should only run on the windows platform" do - Util.expects("windows?").returns(false) - expect { WindowsDaemon.daemonize_runner }.to raise_error("The Windows Daemonizer should only be used on the Windows Platform") - end - - it "should not support writing pid files" do - expect { WindowsDaemon.daemonize_runner(true) }.to raise_error("Writing pid files are not supported on the Windows Platform") - end - - it "should start the mainloop" do - Util.stubs(:windows?).returns(true) - WindowsDaemon.expects(:mainloop) - WindowsDaemon.daemonize_runner - end - end - - describe "#service_main" do - it "should start the runner" do - runner = mock - Runner.stubs(:new).returns(runner) - d = WindowsDaemon.new - runner.expects(:main_loop) - d.service_main - end - - it "should kill any other living threads on exit" do - d = WindowsDaemon.new - d.stubs(:running?).returns(false) - other = mock - Thread.stubs(:list).returns([Thread.current, other]) - Thread.current.expects(:kill).never - other.expects(:kill) - d.service_main - end - end - - describe "#service_stop" do - it "should log, disconnect, stop the runner and exit" do - runner = mock - Log.expects(:info) - d = WindowsDaemon.new - runner.expects(:stop) - d.service_stop - end - end - end - end -end diff --git a/website/_includes/main_menu.html b/website/_includes/main_menu.html deleted file mode 100644 index 22283181..00000000 --- a/website/_includes/main_menu.html +++ /dev/null @@ -1,19 +0,0 @@ -
-   -
-
- Home -
-
- Bugs -
-
- GitHub -
-
- Files -
-
- User List -
- diff --git a/website/blueprint/ie.css b/website/blueprint/ie.css deleted file mode 100644 index 3dddda94..00000000 --- a/website/blueprint/ie.css +++ /dev/null @@ -1,35 +0,0 @@ -/* ----------------------------------------------------------------------- - - - Blueprint CSS Framework 0.9 - http://blueprintcss.org - - * Copyright (c) 2007-Present. See LICENSE for more info. - * See README for instructions on how to use Blueprint. - * For credits and origins, see AUTHORS. - * This is a compressed file. See the sources in the 'src' directory. - ------------------------------------------------------------------------ */ - -/* ie.css */ -body {text-align:center;} -.container {text-align:left;} -* html .column, * html .span-1, * html .span-2, * html .span-3, * html .span-4, * html .span-5, * html .span-6, * html .span-7, * html .span-8, * html .span-9, * html .span-10, * html .span-11, * html .span-12, * html .span-13, * html .span-14, * html .span-15, * html .span-16, * html .span-17, * html .span-18, * html .span-19, * html .span-20, * html .span-21, * html .span-22, * html .span-23, * html .span-24 {display:inline;overflow-x:hidden;} -* html legend {margin:0px -8px 16px 0;padding:0;} -sup {vertical-align:text-top;} -sub {vertical-align:text-bottom;} -html>body p code {*white-space:normal;} -hr {margin:-8px auto 11px;} -img {-ms-interpolation-mode:bicubic;} -.clearfix, .container {display:inline-block;} -* html .clearfix, * html .container {height:1%;} -fieldset {padding-top:0;} -textarea {overflow:auto;} -input.text, input.title, textarea {background-color:#fff;border:1px solid #bbb;} -input.text:focus, input.title:focus {border-color:#666;} -input.text, input.title, textarea, select {margin:0.5em 0;} -input.checkbox, input.radio {position:relative;top:.25em;} -form.inline div, form.inline p {vertical-align:middle;} -form.inline label {position:relative;top:-0.25em;} -form.inline input.checkbox, form.inline input.radio, form.inline input.button, form.inline button {margin:0.5em 0;} -button, input.button {position:relative;top:0.25em;} \ No newline at end of file diff --git a/website/blueprint/plugins/buttons/icons/cross.png b/website/blueprint/plugins/buttons/icons/cross.png deleted file mode 100755 index 1514d51a3cf1b67e1c5b9ada36f1fd474e2d214a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 655 zcmV;A0&x9_P)uEoyT++I zn$b9r%cFfhHe2K68PkBu*@^<$y+7xQ$wJ~;c5aBx$R=xq*41Wo zhwQus_VOgm0hughj}MhOvs#{>Vg09Y8WxjWUJY5YW zJ?&8eG!59Cz=|E%Ns@013KLWOLV)CObIIj_5{>{#k%TEAMs_GbdDV`x-iYsGH z#=Z{USAQA>NY(}X7=3{K8#4^nI0$7`a(T+P4hBKZ7hk58-_j0w;$<(*=f7ic$nT z*Wgd55in08>183j3?S=MAoDDTLoLSL$!_UDxXqSf-?qdd@H%8(We~hQu&uVIo$6NV z(zMY7wn6r5i617ZGZ)-J($xXssTcN*&WujcIDRIp6J4_PqOvJ}9!p6+yo8LmAGS3~ xN#Qq?aIt$6X#&>gHs{AQG2a)rMyf zFQK~pm1x3+7!nu%-M`k}``c>^00{o_1pjWJUTfl8mg=3qGEl8H@}^@w`VUx0_$uy4 z2FhRqKX}xI*?Tv1DJd8z#F#0c%*~rM30HE1@2o5m~}ZyoWhqv>ql{V z1ZGE0lgcoK^lx+eqc*rAX1Ky;Xx3U%u#zG!m-;eD1Qsn@kf3|F9qz~|95=&g3(7!X zB}JAT>RU;a%vaNOGnJ%e1=K6eAh43c(QN8RQ6~GP%O}Jju$~Ld*%`mO1p and - - - Change Password - - - - Cancel - diff --git a/website/blueprint/plugins/buttons/screen.css b/website/blueprint/plugins/buttons/screen.css deleted file mode 100644 index bb66b215..00000000 --- a/website/blueprint/plugins/buttons/screen.css +++ /dev/null @@ -1,97 +0,0 @@ -/* -------------------------------------------------------------- - - buttons.css - * Gives you some great CSS-only buttons. - - Created by Kevin Hale [particletree.com] - * particletree.com/features/rediscovering-the-button-element - - See Readme.txt in this folder for instructions. - --------------------------------------------------------------- */ - -a.button, button { - display:block; - float:left; - margin: 0.7em 0.5em 0.7em 0; - padding:5px 10px 5px 7px; /* Links */ - - border:1px solid #dedede; - border-top:1px solid #eee; - border-left:1px solid #eee; - - background-color:#f5f5f5; - font-family:"Lucida Grande", Tahoma, Arial, Verdana, sans-serif; - font-size:100%; - line-height:130%; - text-decoration:none; - font-weight:bold; - color:#565656; - cursor:pointer; -} -button { - width:auto; - overflow:visible; - padding:4px 10px 3px 7px; /* IE6 */ -} -button[type] { - padding:4px 10px 4px 7px; /* Firefox */ - line-height:17px; /* Safari */ -} -*:first-child+html button[type] { - padding:4px 10px 3px 7px; /* IE7 */ -} -button img, a.button img{ - margin:0 3px -3px 0 !important; - padding:0; - border:none; - width:16px; - height:16px; - float:none; -} - - -/* Button colors --------------------------------------------------------------- */ - -/* Standard */ -button:hover, a.button:hover{ - background-color:#dff4ff; - border:1px solid #c2e1ef; - color:#336699; -} -a.button:active{ - background-color:#6299c5; - border:1px solid #6299c5; - color:#fff; -} - -/* Positive */ -body .positive { - color:#529214; -} -a.positive:hover, button.positive:hover { - background-color:#E6EFC2; - border:1px solid #C6D880; - color:#529214; -} -a.positive:active { - background-color:#529214; - border:1px solid #529214; - color:#fff; -} - -/* Negative */ -body .negative { - color:#d12f19; -} -a.negative:hover, button.negative:hover { - background-color:#fbe3e4; - border:1px solid #fbc2c4; - color:#d12f19; -} -a.negative:active { - background-color:#d12f19; - border:1px solid #d12f19; - color:#fff; -} diff --git a/website/blueprint/plugins/fancy-type/readme.txt b/website/blueprint/plugins/fancy-type/readme.txt deleted file mode 100644 index 85f24915..00000000 --- a/website/blueprint/plugins/fancy-type/readme.txt +++ /dev/null @@ -1,14 +0,0 @@ -Fancy Type - -* Gives you classes to use if you'd like some - extra fancy typography. - -Credits and instructions are specified above each class -in the fancy-type.css file in this directory. - - -Usage ----------------------------------------------------------------- - -1) Add this plugin to lib/settings.yml. - See compress.rb for instructions. diff --git a/website/blueprint/plugins/fancy-type/screen.css b/website/blueprint/plugins/fancy-type/screen.css deleted file mode 100644 index 68994d8f..00000000 --- a/website/blueprint/plugins/fancy-type/screen.css +++ /dev/null @@ -1,71 +0,0 @@ -/* -------------------------------------------------------------- - - fancy-type.css - * Lots of pretty advanced classes for manipulating text. - - See the Readme file in this folder for additional instructions. - --------------------------------------------------------------- */ - -/* Indentation instead of line shifts for sibling paragraphs. */ - p + p { text-indent:2em; margin-top:-1.5em; } - form p + p { text-indent: 0; } /* Don't want this in forms. */ - - -/* For great looking type, use this code instead of asdf: - asdf - Best used on prepositions and ampersands. */ - -.alt { - color: #666; - font-family: "Warnock Pro", "Goudy Old Style","Palatino","Book Antiqua", Georgia, serif; - font-style: italic; - font-weight: normal; -} - - -/* For great looking quote marks in titles, replace "asdf" with: - asdf” - (That is, when the title starts with a quote mark). - (You may have to change this value depending on your font size). */ - -.dquo { margin-left: -.5em; } - - -/* Reduced size type with incremental leading - (http://www.markboulton.co.uk/journal/comments/incremental_leading/) - - This could be used for side notes. For smaller type, you don't necessarily want to - follow the 1.5x vertical rhythm -- the line-height is too much. - - Using this class, it reduces your font size and line-height so that for - every four lines of normal sized type, there is five lines of the sidenote. eg: - - New type size in em's: - 10px (wanted side note size) / 12px (existing base size) = 0.8333 (new type size in ems) - - New line-height value: - 12px x 1.5 = 18px (old line-height) - 18px x 4 = 72px - 72px / 5 = 14.4px (new line height) - 14.4px / 10px = 1.44 (new line height in em's) */ - -p.incr, .incr p { - font-size: 10px; - line-height: 1.44em; - margin-bottom: 1.5em; -} - - -/* Surround uppercase words and abbreviations with this class. - Based on work by Jørgen Arnor Gårdsø Lom [http://twistedintellect.com/] */ - -.caps { - font-variant: small-caps; - letter-spacing: 1px; - text-transform: lowercase; - font-size:1.2em; - line-height:1%; - font-weight:bold; - padding:0 2px; -} diff --git a/website/blueprint/plugins/link-icons/icons/doc.png b/website/blueprint/plugins/link-icons/icons/doc.png deleted file mode 100644 index 834cdfaf48a509ca51d93250fb28dd12e5ea0a13..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 777 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 TVebF6xa6h0np~yK+tB|9OTxo9 diff --git a/website/blueprint/plugins/link-icons/icons/feed.png b/website/blueprint/plugins/link-icons/icons/feed.png deleted file mode 100644 index 315c4f4fa62cb720326ba3f54259666ba3999e42..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 691 zcmV;k0!;mhP)bpQb1=l6TxbDZwj&S={?7%qx-u`rsG(Zp`-rh=e^=%((1yvsuf5d=&62Zj)Y zH&JviNS_F4_Hj|T(1j4$p-!}kixP9&dB4uv^MveG?dGf%sUCoc2!IFxD6wHRA2^dX zXRVk!-qSfk(jcaUKn#RP48(whfPlJUpApdrA!TQi_4D+fVoM;3I0gZ8{=Xv~Po;geVA+Em9@0Wq2 zr>OTZEGR05L=gf1T;ucCxq6Q6EgJiH@@-lVaAlQyw`jIF^c=&IVnj|95hHbE_cnt| zTzZQ?F4Ne@(bH(~&3nM%m)I@ID{@jJ2qZPjr)jhpe9hViOwH5k&|T#EmmL3(vHeUQ zq^!t^Al6JD;=mHq^Bg?J-8-zG2Od7gZbknG;K9czYjPqG*xjPo0k(c4%lPXTpw(qq z@aGMnxtFS(np+2kC} z7P02O874ZkJH$v#nCUVx$({yDN`IX@o2wyvTD#e`qN`_w5<}$3F+_z1iyEv%?$mbQ(# zwJpuiQJP8?X_`#S8b+U_G6=ziYB!xPAcq{)ZJ0bECH@ zYx#`n8^Wzn^J!4>=q^bltNO15ry?0ecSLkjpT@vlid!jk)Fjf7&)q_V5zGs#3N%6* zbW~7Hg=&P0&~Y(|g>$hC9FL?;ttzPDZbpZu9OLb33^e2;FNTGJxScp1&q4M+y2ntQ z?C(=hpU$3~`Thx0eHwi0x`q+!d5k@|0_WHe%sG3e-s^MM`xM-ig!VcIA7H}X1ot~L zg=MLB4w-Q;Bi!!u2|I+Qb;0{{4Q53YX6+4_aXena{nmt*!YG7ua~`qc>o=?@U?rOU znS7%>klzi*muXnbM6i@4FR@s^8vTjDgy&%J?w?`u>NYMDFa_2%0SQ(qJE<3=<8Bzo zfdU60e*y(^$RF%r$kl)p7=7tlCDa$+J7w>}DU(O#~fk>pYuRvHi1E9^msg{tLeV XM&GIRvfA7%00000NkvXXu0mjf&%8>| diff --git a/website/blueprint/plugins/link-icons/icons/pdf.png b/website/blueprint/plugins/link-icons/icons/pdf.png deleted file mode 100644 index 8f8095e46fa4965700afe1f9d065d8a37b101676..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 591 zcmV-V0~O9lw>B8WRlD)Gm}Jrz31u-X&&gn2lvjs=i{7nIaL6v2==uw+8Lcs(8j27 z;|c`rmSv@Lx!heopGP^^Ieb3f=R!%Lpp$}iMS-&P3EJ)s48wrJ_Ni0~k|c47D2nj= z{jS6bt|kFpFf|p5cM`_&0Zh|`rfEp0(}=}lT#(6RpzAsUfxv^LSYX>WlAaN$>)*J5 z0#sE+JRUD8iT9*fz{)_^7@6P&!sEjTcD+I9Z4YjT1`wH@fV{cEvneYGFU%maIEU2s55&K(LixD|{p-uiS@?KNj zk-Go8G$hH6g002ovPDHLkV1hVj1#|!a diff --git a/website/blueprint/plugins/link-icons/icons/visited.png b/website/blueprint/plugins/link-icons/icons/visited.png deleted file mode 100644 index ebf206def2729dae1fa9e8c5c9e5a95b7176c45b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 46990 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 diff --git a/website/blueprint/plugins/link-icons/icons/xls.png b/website/blueprint/plugins/link-icons/icons/xls.png deleted file mode 100644 index b977d7e52e2446ea01201c5c7209ac3a05f12c9f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 663 zcmV;I0%-k-P)^@R5;6x zlTS!gQ5431_q{u#M2 zg&W%y6a}>qj1Z|7Vu&-DW6d~k-n;jnHsjb-q#u0C^W!_5^C=MlKq<8oNCQ6qS00!X z5eI;XP=g!^f}j{hku}E1zZ?XCjE;`p19k(Rh%^AQQ54xysU+ocx$c#f61Z4HnT#3u~FR(3>BnZniMIF4DouI8Hi4u>cAK%EN)5PO(ip3(% zIgBx+QYirR){Z8QwV$9Z(Mpt=L-Or3#bf-G@66}txq0yc*T(zNTBDT0T8rO^JeNbSI-Tzf5!pBioy4NwAN^?iN#{;fH1Jke4Xa`^fR8m z%h6dq%xX)S?7`zae))(Xst^Scp6B8FejQW?RLTM8@0=vnnntuRGBM2dpo>gbCnTD= z^<;=JuqdSf@O>Z8^XdR?s+KEfhDdB_#ahFj^giCtzT(s8kA$AViyTqaAR;KGaLzUU z<=GqA4bRwpX|IG~*x>pZ!@zLr`XQ`od>m(`;jz|M_*1GDO#$7;n74ppb8=eiqh760 x0yt}J1#p`gw$`o!R{d7zU9~!Un@nJV{4bstt4Au+Up@c;002ovPDHLkV1kWhGjjj{ diff --git a/website/blueprint/plugins/link-icons/readme.txt b/website/blueprint/plugins/link-icons/readme.txt deleted file mode 100644 index fc4dc649..00000000 --- a/website/blueprint/plugins/link-icons/readme.txt +++ /dev/null @@ -1,18 +0,0 @@ -Link Icons -* Icons for links based on protocol or file type. - -This is not supported in IE versions < 7. - - -Credits ----------------------------------------------------------------- - -* Marc Morgan -* Olav Bjorkoy [bjorkoy.com] - - -Usage ----------------------------------------------------------------- - -1) Add this line to your HTML: - diff --git a/website/blueprint/plugins/link-icons/screen.css b/website/blueprint/plugins/link-icons/screen.css deleted file mode 100644 index 7b4bef98..00000000 --- a/website/blueprint/plugins/link-icons/screen.css +++ /dev/null @@ -1,40 +0,0 @@ -/* -------------------------------------------------------------- - - link-icons.css - * Icons for links based on protocol or file type. - - See the Readme file in this folder for additional instructions. - --------------------------------------------------------------- */ - -/* Use this class if a link gets an icon when it shouldn't. */ -body a.noicon { - background:transparent none !important; - padding:0 !important; - margin:0 !important; -} - -/* Make sure the icons are not cut */ -a[href^="http:"], a[href^="mailto:"], a[href^="http:"]:visited, -a[href$=".pdf"], a[href$=".doc"], a[href$=".xls"], a[href$=".rss"], -a[href$=".rdf"], a[href^="aim:"] { - padding:2px 22px 2px 0; - margin:-2px 0; - background-repeat: no-repeat; - background-position: right center; -} - -/* External links */ -a[href^="http:"] { background-image: url(icons/external.png); } -a[href^="mailto:"] { background-image: url(icons/email.png); } -a[href^="http:"]:visited { background-image: url(icons/visited.png); } - -/* Files */ -a[href$=".pdf"] { background-image: url(icons/pdf.png); } -a[href$=".doc"] { background-image: url(icons/doc.png); } -a[href$=".xls"] { background-image: url(icons/xls.png); } - -/* Misc */ -a[href$=".rss"], -a[href$=".rdf"] { background-image: url(icons/feed.png); } -a[href^="aim:"] { background-image: url(icons/im.png); } diff --git a/website/blueprint/plugins/rtl/readme.txt b/website/blueprint/plugins/rtl/readme.txt deleted file mode 100644 index 5564c402..00000000 --- a/website/blueprint/plugins/rtl/readme.txt +++ /dev/null @@ -1,10 +0,0 @@ -RTL -* Mirrors Blueprint, so it can be used with Right-to-Left languages. - -By Ran Yaniv Hartstein, ranh.co.il - -Usage ----------------------------------------------------------------- - -1) Add this line to your HTML: - diff --git a/website/blueprint/plugins/rtl/screen.css b/website/blueprint/plugins/rtl/screen.css deleted file mode 100644 index 7db7eb5e..00000000 --- a/website/blueprint/plugins/rtl/screen.css +++ /dev/null @@ -1,110 +0,0 @@ -/* -------------------------------------------------------------- - - rtl.css - * Mirrors Blueprint for left-to-right languages - - By Ran Yaniv Hartstein [ranh.co.il] - --------------------------------------------------------------- */ - -body .container { direction: rtl; } -body .column, body .span-1, body .span-2, body .span-3, body .span-4, body .span-5, body .span-6, body .span-7, body .span-8, body .span-9, body .span-10, body .span-11, body .span-12, body .span-13, body .span-14, body .span-15, body .span-16, body .span-17, body .span-18, body .span-19, body .span-20, body .span-21, body .span-22, body .span-23, body .span-24 { - float: right; - margin-right: 0; - margin-left: 10px; - text-align:right; -} - -body div.last { margin-left: 0; } -body table .last { padding-left: 0; } - -body .append-1 { padding-right: 0; padding-left: 40px; } -body .append-2 { padding-right: 0; padding-left: 80px; } -body .append-3 { padding-right: 0; padding-left: 120px; } -body .append-4 { padding-right: 0; padding-left: 160px; } -body .append-5 { padding-right: 0; padding-left: 200px; } -body .append-6 { padding-right: 0; padding-left: 240px; } -body .append-7 { padding-right: 0; padding-left: 280px; } -body .append-8 { padding-right: 0; padding-left: 320px; } -body .append-9 { padding-right: 0; padding-left: 360px; } -body .append-10 { padding-right: 0; padding-left: 400px; } -body .append-11 { padding-right: 0; padding-left: 440px; } -body .append-12 { padding-right: 0; padding-left: 480px; } -body .append-13 { padding-right: 0; padding-left: 520px; } -body .append-14 { padding-right: 0; padding-left: 560px; } -body .append-15 { padding-right: 0; padding-left: 600px; } -body .append-16 { padding-right: 0; padding-left: 640px; } -body .append-17 { padding-right: 0; padding-left: 680px; } -body .append-18 { padding-right: 0; padding-left: 720px; } -body .append-19 { padding-right: 0; padding-left: 760px; } -body .append-20 { padding-right: 0; padding-left: 800px; } -body .append-21 { padding-right: 0; padding-left: 840px; } -body .append-22 { padding-right: 0; padding-left: 880px; } -body .append-23 { padding-right: 0; padding-left: 920px; } - -body .prepend-1 { padding-left: 0; padding-right: 40px; } -body .prepend-2 { padding-left: 0; padding-right: 80px; } -body .prepend-3 { padding-left: 0; padding-right: 120px; } -body .prepend-4 { padding-left: 0; padding-right: 160px; } -body .prepend-5 { padding-left: 0; padding-right: 200px; } -body .prepend-6 { padding-left: 0; padding-right: 240px; } -body .prepend-7 { padding-left: 0; padding-right: 280px; } -body .prepend-8 { padding-left: 0; padding-right: 320px; } -body .prepend-9 { padding-left: 0; padding-right: 360px; } -body .prepend-10 { padding-left: 0; padding-right: 400px; } -body .prepend-11 { padding-left: 0; padding-right: 440px; } -body .prepend-12 { padding-left: 0; padding-right: 480px; } -body .prepend-13 { padding-left: 0; padding-right: 520px; } -body .prepend-14 { padding-left: 0; padding-right: 560px; } -body .prepend-15 { padding-left: 0; padding-right: 600px; } -body .prepend-16 { padding-left: 0; padding-right: 640px; } -body .prepend-17 { padding-left: 0; padding-right: 680px; } -body .prepend-18 { padding-left: 0; padding-right: 720px; } -body .prepend-19 { padding-left: 0; padding-right: 760px; } -body .prepend-20 { padding-left: 0; padding-right: 800px; } -body .prepend-21 { padding-left: 0; padding-right: 840px; } -body .prepend-22 { padding-left: 0; padding-right: 880px; } -body .prepend-23 { padding-left: 0; padding-right: 920px; } - -body .border { - padding-right: 0; - padding-left: 4px; - margin-right: 0; - margin-left: 5px; - border-right: none; - border-left: 1px solid #eee; -} - -body .colborder { - padding-right: 0; - padding-left: 24px; - margin-right: 0; - margin-left: 25px; - border-right: none; - border-left: 1px solid #eee; -} - -body .pull-1 { margin-left: 0; margin-right: -40px; } -body .pull-2 { margin-left: 0; margin-right: -80px; } -body .pull-3 { margin-left: 0; margin-right: -120px; } -body .pull-4 { margin-left: 0; margin-right: -160px; } - -body .push-0 { margin: 0 18px 0 0; } -body .push-1 { margin: 0 18px 0 -40px; } -body .push-2 { margin: 0 18px 0 -80px; } -body .push-3 { margin: 0 18px 0 -120px; } -body .push-4 { margin: 0 18px 0 -160px; } -body .push-0, body .push-1, body .push-2, -body .push-3, body .push-4 { float: left; } - - -/* Typography with RTL support */ -body h1,body h2,body h3, -body h4,body h5,body h6 { font-family: Arial, sans-serif; } -html body { font-family: Arial, sans-serif; } -body pre,body code,body tt { font-family: monospace; } - -/* Mirror floats and margins on typographic elements */ -body p img { float: right; margin: 1.5em 0 1.5em 1.5em; } -body dd, body ul, body ol { margin-left: 0; margin-right: 1.5em;} -body td, body th { text-align:right; } diff --git a/website/blueprint/print.css b/website/blueprint/print.css deleted file mode 100644 index fdb82208..00000000 --- a/website/blueprint/print.css +++ /dev/null @@ -1,29 +0,0 @@ -/* ----------------------------------------------------------------------- - - - Blueprint CSS Framework 0.9 - http://blueprintcss.org - - * Copyright (c) 2007-Present. See LICENSE for more info. - * See README for instructions on how to use Blueprint. - * For credits and origins, see AUTHORS. - * This is a compressed file. See the sources in the 'src' directory. - ------------------------------------------------------------------------ */ - -/* print.css */ -body {line-height:1.5;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;color:#000;background:none;font-size:10pt;} -.container {background:none;} -hr {background:#ccc;color:#ccc;width:100%;height:2px;margin:2em 0;padding:0;border:none;} -hr.space {background:#fff;color:#fff;visibility:hidden;} -h1, h2, h3, h4, h5, h6 {font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;} -code {font:.9em "Courier New", Monaco, Courier, monospace;} -a img {border:none;} -p img.top {margin-top:0;} -blockquote {margin:1.5em;padding:1em;font-style:italic;font-size:.9em;} -.small {font-size:.9em;} -.large {font-size:1.1em;} -.quiet {color:#999;} -.hide {display:none;} -a:link, a:visited {background:transparent;font-weight:700;text-decoration:underline;} -a:link:after, a:visited:after {content:" (" attr(href) ")";font-size:90%;} \ No newline at end of file diff --git a/website/blueprint/screen.css b/website/blueprint/screen.css deleted file mode 100644 index 9b7c1f29..00000000 --- a/website/blueprint/screen.css +++ /dev/null @@ -1,258 +0,0 @@ -/* ----------------------------------------------------------------------- - - - Blueprint CSS Framework 0.9 - http://blueprintcss.org - - * Copyright (c) 2007-Present. See LICENSE for more info. - * See README for instructions on how to use Blueprint. - * For credits and origins, see AUTHORS. - * This is a compressed file. See the sources in the 'src' directory. - ------------------------------------------------------------------------ */ - -/* reset.css */ -html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, acronym, address, code, del, dfn, em, img, q, dl, dt, dd, ol, ul, li, fieldset, form, label, legend, table, caption, tbody, tfoot, thead, tr, th, td, article, aside, dialog, figure, footer, header, hgroup, nav, section {margin:0;padding:0;border:0;font-weight:inherit;font-style:inherit;font-size:100%;font-family:inherit;vertical-align:baseline;} -article, aside, dialog, figure, footer, header, hgroup, nav, section {display:block;} -body {line-height:1.5;} -table {border-collapse:separate;border-spacing:0;} -caption, th, td {text-align:left;font-weight:normal;} -table, td, th {vertical-align:middle;} -blockquote:before, blockquote:after, q:before, q:after {content:"";} -blockquote, q {quotes:"" "";} -a img {border:none;} - -/* typography.css */ -html {font-size:100.01%;} -body {font-size:75%;color:#222;background:#fff;font-family:"Helvetica Neue", Arial, Helvetica, sans-serif;} -h1, h2, h3, h4, h5, h6 {font-weight:normal;color:#111;} -h1 {font-size:3em;line-height:1;margin-bottom:0.5em;} -h2 {font-size:2em;margin-bottom:0.75em;} -h3 {font-size:1.5em;line-height:1;margin-bottom:1em;} -h4 {font-size:1.2em;line-height:1.25;margin-bottom:1.25em;} -h5 {font-size:1em;font-weight:bold;margin-bottom:1.5em;} -h6 {font-size:1em;font-weight:bold;} -h1 img, h2 img, h3 img, h4 img, h5 img, h6 img {margin:0;} -p {margin:0 0 1.5em;} -p img.left {float:left;margin:1.5em 1.5em 1.5em 0;padding:0;} -p img.right {float:right;margin:1.5em 0 1.5em 1.5em;} -a:focus, a:hover {color:#000;} -a {color:#009;text-decoration:underline;} -blockquote {margin:1.5em;color:#666;font-style:italic;} -strong {font-weight:bold;} -em, dfn {font-style:italic;} -dfn {font-weight:bold;} -sup, sub {line-height:0;} -abbr, acronym {border-bottom:1px dotted #666;} -address {margin:0 0 1.5em;font-style:italic;} -del {color:#666;} -pre {margin:1.5em 0;white-space:pre;} -pre, code, tt {font:1em 'andale mono', 'lucida console', monospace;line-height:1.5;} -li ul, li ol {margin:0;} -ul, ol {margin:0 1.5em 1.5em 0;padding-left:3.333em;} -ul {list-style-type:disc;} -ol {list-style-type:decimal;} -dl {margin:0 0 1.5em 0;} -dl dt {font-weight:bold;} -dd {margin-left:1.5em;} -table {margin-bottom:1.4em;width:100%;} -th {font-weight:bold;} -thead th {background:#c3d9ff;} -th, td, caption {padding:4px 10px 4px 5px;} -tr.even td {background:#e5ecf9;} -tfoot {font-style:italic;} -caption {background:#eee;} -.small {font-size:.8em;margin-bottom:1.875em;line-height:1.875em;} -.large {font-size:1.2em;line-height:2.5em;margin-bottom:1.25em;} -.hide {display:none;} -.quiet {color:#666;} -.loud {color:#000;} -.highlight {background:#ff0;} -.added {background:#060;color:#fff;} -.removed {background:#900;color:#fff;} -.first {margin-left:0;padding-left:0;} -.last {margin-right:0;padding-right:0;} -.top {margin-top:0;padding-top:0;} -.bottom {margin-bottom:0;padding-bottom:0;} - -/* forms.css */ -label {font-weight:bold;} -fieldset {padding:1.4em;margin:0 0 1.5em 0;border:1px solid #ccc;} -legend {font-weight:bold;font-size:1.2em;} -input[type=text], input[type=password], input.text, input.title, textarea, select {background-color:#fff;border:1px solid #bbb;} -input[type=text]:focus, input[type=password]:focus, input.text:focus, input.title:focus, textarea:focus, select:focus {border-color:#666;} -input[type=text], input[type=password], input.text, input.title, textarea, select {margin:0.5em 0;} -input.text, input.title {width:300px;padding:5px;} -input.title {font-size:1.5em;} -textarea {width:390px;height:250px;padding:5px;} -input[type=checkbox], input[type=radio], input.checkbox, input.radio {position:relative;top:.25em;} -form.inline {line-height:3;} -form.inline p {margin-bottom:0;} -.error, .notice, .success {padding:.8em;margin-bottom:1em;border:2px solid #ddd;} -.error {background:#FBE3E4;color:#8a1f11;border-color:#FBC2C4;} -.notice {background:#FFF6BF;color:#514721;border-color:#FFD324;} -.success {background:#E6EFC2;color:#264409;border-color:#C6D880;} -.error a {color:#8a1f11;} -.notice a {color:#514721;} -.success a {color:#264409;} - -/* grid.css */ -.container {width:950px;margin:0 auto;} -.showgrid {background:url(src/grid.png);} -.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 {float:left;margin-right:10px;} -.last {margin-right:0;} -.span-1 {width:30px;} -.span-2 {width:70px;} -.span-3 {width:110px;} -.span-4 {width:150px;} -.span-5 {width:190px;} -.span-6 {width:230px;} -.span-7 {width:270px;} -.span-8 {width:310px;} -.span-9 {width:350px;} -.span-10 {width:390px;} -.span-11 {width:430px;} -.span-12 {width:470px;} -.span-13 {width:510px;} -.span-14 {width:550px;} -.span-15 {width:590px;} -.span-16 {width:630px;} -.span-17 {width:670px;} -.span-18 {width:710px;} -.span-19 {width:750px;} -.span-20 {width:790px;} -.span-21 {width:830px;} -.span-22 {width:870px;} -.span-23 {width:910px;} -.span-24 {width:950px;margin-right:0;} -input.span-1, textarea.span-1, input.span-2, textarea.span-2, input.span-3, textarea.span-3, input.span-4, textarea.span-4, input.span-5, textarea.span-5, input.span-6, textarea.span-6, input.span-7, textarea.span-7, input.span-8, textarea.span-8, input.span-9, textarea.span-9, input.span-10, textarea.span-10, input.span-11, textarea.span-11, input.span-12, textarea.span-12, input.span-13, textarea.span-13, input.span-14, textarea.span-14, input.span-15, textarea.span-15, input.span-16, textarea.span-16, input.span-17, textarea.span-17, input.span-18, textarea.span-18, input.span-19, textarea.span-19, input.span-20, textarea.span-20, input.span-21, textarea.span-21, input.span-22, textarea.span-22, input.span-23, textarea.span-23, input.span-24, textarea.span-24 {border-left-width:1px;border-right-width:1px;padding-left:5px;padding-right:5px;} -input.span-1, textarea.span-1 {width:18px;} -input.span-2, textarea.span-2 {width:58px;} -input.span-3, textarea.span-3 {width:98px;} -input.span-4, textarea.span-4 {width:138px;} -input.span-5, textarea.span-5 {width:178px;} -input.span-6, textarea.span-6 {width:218px;} -input.span-7, textarea.span-7 {width:258px;} -input.span-8, textarea.span-8 {width:298px;} -input.span-9, textarea.span-9 {width:338px;} -input.span-10, textarea.span-10 {width:378px;} -input.span-11, textarea.span-11 {width:418px;} -input.span-12, textarea.span-12 {width:458px;} -input.span-13, textarea.span-13 {width:498px;} -input.span-14, textarea.span-14 {width:538px;} -input.span-15, textarea.span-15 {width:578px;} -input.span-16, textarea.span-16 {width:618px;} -input.span-17, textarea.span-17 {width:658px;} -input.span-18, textarea.span-18 {width:698px;} -input.span-19, textarea.span-19 {width:738px;} -input.span-20, textarea.span-20 {width:778px;} -input.span-21, textarea.span-21 {width:818px;} -input.span-22, textarea.span-22 {width:858px;} -input.span-23, textarea.span-23 {width:898px;} -input.span-24, textarea.span-24 {width:938px;} -.append-1 {padding-right:40px;} -.append-2 {padding-right:80px;} -.append-3 {padding-right:120px;} -.append-4 {padding-right:160px;} -.append-5 {padding-right:200px;} -.append-6 {padding-right:240px;} -.append-7 {padding-right:280px;} -.append-8 {padding-right:320px;} -.append-9 {padding-right:360px;} -.append-10 {padding-right:400px;} -.append-11 {padding-right:440px;} -.append-12 {padding-right:480px;} -.append-13 {padding-right:520px;} -.append-14 {padding-right:560px;} -.append-15 {padding-right:600px;} -.append-16 {padding-right:640px;} -.append-17 {padding-right:680px;} -.append-18 {padding-right:720px;} -.append-19 {padding-right:760px;} -.append-20 {padding-right:800px;} -.append-21 {padding-right:840px;} -.append-22 {padding-right:880px;} -.append-23 {padding-right:920px;} -.prepend-1 {padding-left:40px;} -.prepend-2 {padding-left:80px;} -.prepend-3 {padding-left:120px;} -.prepend-4 {padding-left:160px;} -.prepend-5 {padding-left:200px;} -.prepend-6 {padding-left:240px;} -.prepend-7 {padding-left:280px;} -.prepend-8 {padding-left:320px;} -.prepend-9 {padding-left:360px;} -.prepend-10 {padding-left:400px;} -.prepend-11 {padding-left:440px;} -.prepend-12 {padding-left:480px;} -.prepend-13 {padding-left:520px;} -.prepend-14 {padding-left:560px;} -.prepend-15 {padding-left:600px;} -.prepend-16 {padding-left:640px;} -.prepend-17 {padding-left:680px;} -.prepend-18 {padding-left:720px;} -.prepend-19 {padding-left:760px;} -.prepend-20 {padding-left:800px;} -.prepend-21 {padding-left:840px;} -.prepend-22 {padding-left:880px;} -.prepend-23 {padding-left:920px;} -.border {padding-right:4px;margin-right:5px;border-right:1px solid #eee;} -.colborder {padding-right:24px;margin-right:25px;border-right:1px solid #eee;} -.pull-1 {margin-left:-40px;} -.pull-2 {margin-left:-80px;} -.pull-3 {margin-left:-120px;} -.pull-4 {margin-left:-160px;} -.pull-5 {margin-left:-200px;} -.pull-6 {margin-left:-240px;} -.pull-7 {margin-left:-280px;} -.pull-8 {margin-left:-320px;} -.pull-9 {margin-left:-360px;} -.pull-10 {margin-left:-400px;} -.pull-11 {margin-left:-440px;} -.pull-12 {margin-left:-480px;} -.pull-13 {margin-left:-520px;} -.pull-14 {margin-left:-560px;} -.pull-15 {margin-left:-600px;} -.pull-16 {margin-left:-640px;} -.pull-17 {margin-left:-680px;} -.pull-18 {margin-left:-720px;} -.pull-19 {margin-left:-760px;} -.pull-20 {margin-left:-800px;} -.pull-21 {margin-left:-840px;} -.pull-22 {margin-left:-880px;} -.pull-23 {margin-left:-920px;} -.pull-24 {margin-left:-960px;} -.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float:left;position:relative;} -.push-1 {margin:0 -40px 1.5em 40px;} -.push-2 {margin:0 -80px 1.5em 80px;} -.push-3 {margin:0 -120px 1.5em 120px;} -.push-4 {margin:0 -160px 1.5em 160px;} -.push-5 {margin:0 -200px 1.5em 200px;} -.push-6 {margin:0 -240px 1.5em 240px;} -.push-7 {margin:0 -280px 1.5em 280px;} -.push-8 {margin:0 -320px 1.5em 320px;} -.push-9 {margin:0 -360px 1.5em 360px;} -.push-10 {margin:0 -400px 1.5em 400px;} -.push-11 {margin:0 -440px 1.5em 440px;} -.push-12 {margin:0 -480px 1.5em 480px;} -.push-13 {margin:0 -520px 1.5em 520px;} -.push-14 {margin:0 -560px 1.5em 560px;} -.push-15 {margin:0 -600px 1.5em 600px;} -.push-16 {margin:0 -640px 1.5em 640px;} -.push-17 {margin:0 -680px 1.5em 680px;} -.push-18 {margin:0 -720px 1.5em 720px;} -.push-19 {margin:0 -760px 1.5em 760px;} -.push-20 {margin:0 -800px 1.5em 800px;} -.push-21 {margin:0 -840px 1.5em 840px;} -.push-22 {margin:0 -880px 1.5em 880px;} -.push-23 {margin:0 -920px 1.5em 920px;} -.push-24 {margin:0 -960px 1.5em 960px;} -.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float:right;position:relative;} -.prepend-top {margin-top:1.5em;} -.append-bottom {margin-bottom:1.5em;} -.box {padding:1.5em;margin-bottom:1.5em;background:#E5ECF9;} -hr {background:#ddd;color:#ddd;clear:both;float:none;width:100%;height:.1em;margin:0 0 1.45em;border:none;} -hr.space {background:#fff;color:#fff;visibility:hidden;} -.clearfix:after, .container:after {content:"\0020";display:block;height:0;clear:both;visibility:hidden;overflow:hidden;} -.clearfix, .container {display:block;} -.clear {clear:both;} diff --git a/website/blueprint/src/forms.css b/website/blueprint/src/forms.css deleted file mode 100644 index b4911340..00000000 --- a/website/blueprint/src/forms.css +++ /dev/null @@ -1,65 +0,0 @@ -/* -------------------------------------------------------------- - - forms.css - * Sets up some default styling for forms - * Gives you classes to enhance your forms - - Usage: - * For text fields, use class .title or .text - * For inline forms, use .inline (even when using columns) - --------------------------------------------------------------- */ - -label { font-weight: bold; } -fieldset { padding:1.4em; margin: 0 0 1.5em 0; border: 1px solid #ccc; } -legend { font-weight: bold; font-size:1.2em; } - - -/* Form fields --------------------------------------------------------------- */ - -input[type=text], input[type=password], -input.text, input.title, -textarea, select { - background-color:#fff; - border:1px solid #bbb; -} -input[type=text]:focus, input[type=password]:focus, -input.text:focus, input.title:focus, -textarea:focus, select:focus { - border-color:#666; -} - -input[type=text], input[type=password], -input.text, input.title, -textarea, select { - margin:0.5em 0; -} - -input.text, -input.title { width: 300px; padding:5px; } -input.title { font-size:1.5em; } -textarea { width: 390px; height: 250px; padding:5px; } - -input[type=checkbox], input[type=radio], -input.checkbox, input.radio { - position:relative; top:.25em; -} - -form.inline { line-height:3; } -form.inline p { margin-bottom:0; } - - -/* Success, notice and error boxes --------------------------------------------------------------- */ - -.error, -.notice, -.success { padding: .8em; margin-bottom: 1em; border: 2px solid #ddd; } - -.error { background: #FBE3E4; color: #8a1f11; border-color: #FBC2C4; } -.notice { background: #FFF6BF; color: #514721; border-color: #FFD324; } -.success { background: #E6EFC2; color: #264409; border-color: #C6D880; } -.error a { color: #8a1f11; } -.notice a { color: #514721; } -.success a { color: #264409; } diff --git a/website/blueprint/src/grid.css b/website/blueprint/src/grid.css deleted file mode 100755 index 02a9d0ca..00000000 --- a/website/blueprint/src/grid.css +++ /dev/null @@ -1,280 +0,0 @@ -/* -------------------------------------------------------------- - - grid.css - * Sets up an easy-to-use grid of 24 columns. - - By default, the grid is 950px wide, with 24 columns - spanning 30px, and a 10px margin between columns. - - If you need fewer or more columns, namespaces or semantic - element names, use the compressor script (lib/compress.rb) - --------------------------------------------------------------- */ - -/* A container should group all your columns. */ -.container { - width: 950px; - margin: 0 auto; -} - -/* Use this class on any .span / container to see the grid. */ -.showgrid { - background: url(src/grid.png); -} - - -/* Columns --------------------------------------------------------------- */ - -/* Sets up basic grid floating and margin. */ -.column, .span-1, .span-2, .span-3, .span-4, .span-5, .span-6, .span-7, .span-8, .span-9, .span-10, .span-11, .span-12, .span-13, .span-14, .span-15, .span-16, .span-17, .span-18, .span-19, .span-20, .span-21, .span-22, .span-23, .span-24 { - float: left; - margin-right: 10px; -} - -/* The last column in a row needs this class. */ -.last { margin-right: 0; } - -/* Use these classes to set the width of a column. */ -.span-1 {width: 30px;} - -.span-2 {width: 70px;} -.span-3 {width: 110px;} -.span-4 {width: 150px;} -.span-5 {width: 190px;} -.span-6 {width: 230px;} -.span-7 {width: 270px;} -.span-8 {width: 310px;} -.span-9 {width: 350px;} -.span-10 {width: 390px;} -.span-11 {width: 430px;} -.span-12 {width: 470px;} -.span-13 {width: 510px;} -.span-14 {width: 550px;} -.span-15 {width: 590px;} -.span-16 {width: 630px;} -.span-17 {width: 670px;} -.span-18 {width: 710px;} -.span-19 {width: 750px;} -.span-20 {width: 790px;} -.span-21 {width: 830px;} -.span-22 {width: 870px;} -.span-23 {width: 910px;} -.span-24 {width:950px; margin-right:0;} - -/* Use these classes to set the width of an input. */ -input.span-1, textarea.span-1, input.span-2, textarea.span-2, input.span-3, textarea.span-3, input.span-4, textarea.span-4, input.span-5, textarea.span-5, input.span-6, textarea.span-6, input.span-7, textarea.span-7, input.span-8, textarea.span-8, input.span-9, textarea.span-9, input.span-10, textarea.span-10, input.span-11, textarea.span-11, input.span-12, textarea.span-12, input.span-13, textarea.span-13, input.span-14, textarea.span-14, input.span-15, textarea.span-15, input.span-16, textarea.span-16, input.span-17, textarea.span-17, input.span-18, textarea.span-18, input.span-19, textarea.span-19, input.span-20, textarea.span-20, input.span-21, textarea.span-21, input.span-22, textarea.span-22, input.span-23, textarea.span-23, input.span-24, textarea.span-24 { - border-left-width: 1px; - border-right-width: 1px; - padding-left: 5px; - padding-right: 5px; -} - -input.span-1, textarea.span-1 { width: 18px; } -input.span-2, textarea.span-2 { width: 58px; } -input.span-3, textarea.span-3 { width: 98px; } -input.span-4, textarea.span-4 { width: 138px; } -input.span-5, textarea.span-5 { width: 178px; } -input.span-6, textarea.span-6 { width: 218px; } -input.span-7, textarea.span-7 { width: 258px; } -input.span-8, textarea.span-8 { width: 298px; } -input.span-9, textarea.span-9 { width: 338px; } -input.span-10, textarea.span-10 { width: 378px; } -input.span-11, textarea.span-11 { width: 418px; } -input.span-12, textarea.span-12 { width: 458px; } -input.span-13, textarea.span-13 { width: 498px; } -input.span-14, textarea.span-14 { width: 538px; } -input.span-15, textarea.span-15 { width: 578px; } -input.span-16, textarea.span-16 { width: 618px; } -input.span-17, textarea.span-17 { width: 658px; } -input.span-18, textarea.span-18 { width: 698px; } -input.span-19, textarea.span-19 { width: 738px; } -input.span-20, textarea.span-20 { width: 778px; } -input.span-21, textarea.span-21 { width: 818px; } -input.span-22, textarea.span-22 { width: 858px; } -input.span-23, textarea.span-23 { width: 898px; } -input.span-24, textarea.span-24 { width: 938px; } - -/* Add these to a column to append empty cols. */ - -.append-1 { padding-right: 40px;} -.append-2 { padding-right: 80px;} -.append-3 { padding-right: 120px;} -.append-4 { padding-right: 160px;} -.append-5 { padding-right: 200px;} -.append-6 { padding-right: 240px;} -.append-7 { padding-right: 280px;} -.append-8 { padding-right: 320px;} -.append-9 { padding-right: 360px;} -.append-10 { padding-right: 400px;} -.append-11 { padding-right: 440px;} -.append-12 { padding-right: 480px;} -.append-13 { padding-right: 520px;} -.append-14 { padding-right: 560px;} -.append-15 { padding-right: 600px;} -.append-16 { padding-right: 640px;} -.append-17 { padding-right: 680px;} -.append-18 { padding-right: 720px;} -.append-19 { padding-right: 760px;} -.append-20 { padding-right: 800px;} -.append-21 { padding-right: 840px;} -.append-22 { padding-right: 880px;} -.append-23 { padding-right: 920px;} - -/* Add these to a column to prepend empty cols. */ - -.prepend-1 { padding-left: 40px;} -.prepend-2 { padding-left: 80px;} -.prepend-3 { padding-left: 120px;} -.prepend-4 { padding-left: 160px;} -.prepend-5 { padding-left: 200px;} -.prepend-6 { padding-left: 240px;} -.prepend-7 { padding-left: 280px;} -.prepend-8 { padding-left: 320px;} -.prepend-9 { padding-left: 360px;} -.prepend-10 { padding-left: 400px;} -.prepend-11 { padding-left: 440px;} -.prepend-12 { padding-left: 480px;} -.prepend-13 { padding-left: 520px;} -.prepend-14 { padding-left: 560px;} -.prepend-15 { padding-left: 600px;} -.prepend-16 { padding-left: 640px;} -.prepend-17 { padding-left: 680px;} -.prepend-18 { padding-left: 720px;} -.prepend-19 { padding-left: 760px;} -.prepend-20 { padding-left: 800px;} -.prepend-21 { padding-left: 840px;} -.prepend-22 { padding-left: 880px;} -.prepend-23 { padding-left: 920px;} - - -/* Border on right hand side of a column. */ -.border { - padding-right: 4px; - margin-right: 5px; - border-right: 1px solid #eee; -} - -/* Border with more whitespace, spans one column. */ -.colborder { - padding-right: 24px; - margin-right: 25px; - border-right: 1px solid #eee; -} - - -/* Use these classes on an element to push it into the -next column, or to pull it into the previous column. */ - - -.pull-1 { margin-left: -40px; } -.pull-2 { margin-left: -80px; } -.pull-3 { margin-left: -120px; } -.pull-4 { margin-left: -160px; } -.pull-5 { margin-left: -200px; } -.pull-6 { margin-left: -240px; } -.pull-7 { margin-left: -280px; } -.pull-8 { margin-left: -320px; } -.pull-9 { margin-left: -360px; } -.pull-10 { margin-left: -400px; } -.pull-11 { margin-left: -440px; } -.pull-12 { margin-left: -480px; } -.pull-13 { margin-left: -520px; } -.pull-14 { margin-left: -560px; } -.pull-15 { margin-left: -600px; } -.pull-16 { margin-left: -640px; } -.pull-17 { margin-left: -680px; } -.pull-18 { margin-left: -720px; } -.pull-19 { margin-left: -760px; } -.pull-20 { margin-left: -800px; } -.pull-21 { margin-left: -840px; } -.pull-22 { margin-left: -880px; } -.pull-23 { margin-left: -920px; } -.pull-24 { margin-left: -960px; } - -.pull-1, .pull-2, .pull-3, .pull-4, .pull-5, .pull-6, .pull-7, .pull-8, .pull-9, .pull-10, .pull-11, .pull-12, .pull-13, .pull-14, .pull-15, .pull-16, .pull-17, .pull-18, .pull-19, .pull-20, .pull-21, .pull-22, .pull-23, .pull-24 {float: left; position:relative;} - - -.push-1 { margin: 0 -40px 1.5em 40px; } -.push-2 { margin: 0 -80px 1.5em 80px; } -.push-3 { margin: 0 -120px 1.5em 120px; } -.push-4 { margin: 0 -160px 1.5em 160px; } -.push-5 { margin: 0 -200px 1.5em 200px; } -.push-6 { margin: 0 -240px 1.5em 240px; } -.push-7 { margin: 0 -280px 1.5em 280px; } -.push-8 { margin: 0 -320px 1.5em 320px; } -.push-9 { margin: 0 -360px 1.5em 360px; } -.push-10 { margin: 0 -400px 1.5em 400px; } -.push-11 { margin: 0 -440px 1.5em 440px; } -.push-12 { margin: 0 -480px 1.5em 480px; } -.push-13 { margin: 0 -520px 1.5em 520px; } -.push-14 { margin: 0 -560px 1.5em 560px; } -.push-15 { margin: 0 -600px 1.5em 600px; } -.push-16 { margin: 0 -640px 1.5em 640px; } -.push-17 { margin: 0 -680px 1.5em 680px; } -.push-18 { margin: 0 -720px 1.5em 720px; } -.push-19 { margin: 0 -760px 1.5em 760px; } -.push-20 { margin: 0 -800px 1.5em 800px; } -.push-21 { margin: 0 -840px 1.5em 840px; } -.push-22 { margin: 0 -880px 1.5em 880px; } -.push-23 { margin: 0 -920px 1.5em 920px; } -.push-24 { margin: 0 -960px 1.5em 960px; } - -.push-1, .push-2, .push-3, .push-4, .push-5, .push-6, .push-7, .push-8, .push-9, .push-10, .push-11, .push-12, .push-13, .push-14, .push-15, .push-16, .push-17, .push-18, .push-19, .push-20, .push-21, .push-22, .push-23, .push-24 {float: right; position:relative;} - - -/* Misc classes and elements --------------------------------------------------------------- */ - -/* In case you need to add a gutter above/below an element */ -.prepend-top { - margin-top:1.5em; -} -.append-bottom { - margin-bottom:1.5em; -} - -/* Use a .box to create a padded box inside a column. */ -.box { - padding: 1.5em; - margin-bottom: 1.5em; - background: #E5ECF9; -} - -/* Use this to create a horizontal ruler across a column. */ -hr { - background: #ddd; - color: #ddd; - clear: both; - float: none; - width: 100%; - height: .1em; - margin: 0 0 1.45em; - border: none; -} - -hr.space { - background: #fff; - color: #fff; - visibility: hidden; -} - - -/* Clearing floats without extra markup - Based on How To Clear Floats Without Structural Markup by PiE - [http://www.positioniseverything.net/easyclearing.html] */ - -.clearfix:after, .container:after { - content: "\0020"; - display: block; - height: 0; - clear: both; - visibility: hidden; - overflow:hidden; -} -.clearfix, .container {display: block;} - -/* Regular clearing - apply to column that should drop below previous ones. */ - -.clear { clear:both; } diff --git a/website/blueprint/src/grid.png b/website/blueprint/src/grid.png deleted file mode 100644 index d42a6c32c173bf067ee9fe1aa062afd915fb366c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 195 zcmeAS@N?(olHy`uVBq!ia0vp^8bB;0zy>5M`yHMFDYhhUcbETQz!~xV4p4-%z$3C4 zNPB>>+sSM@AS2n+#W5t}@Y@R;c@HQE9K9f5$RPVWzg=TNdlOHhh{)0WhV|C%K~?jM z*S=OS^3yz4TmAiI`@VAx6Brelo!DAbody p code { *white-space: normal; } - -/* IE 6&7 has problems with setting proper
margins. */ -hr { margin:-8px auto 11px; } - -/* Explicitly set interpolation, allowing dynamically resized images to not look horrible */ -img { -ms-interpolation-mode:bicubic; } - -/* Clearing --------------------------------------------------------------- */ - -/* Makes clearfix actually work in IE */ -.clearfix, .container { display:inline-block; } -* html .clearfix, -* html .container { height:1%; } - - -/* Forms --------------------------------------------------------------- */ - -/* Fixes padding on fieldset */ -fieldset { padding-top:0; } - -/* Makes classic textareas in IE 6 resemble other browsers */ -textarea { overflow:auto; } - -/* Fixes rule that IE 6 ignores */ -input.text, input.title, textarea { background-color:#fff; border:1px solid #bbb; } -input.text:focus, input.title:focus { border-color:#666; } -input.text, input.title, textarea, select { margin:0.5em 0; } -input.checkbox, input.radio { position:relative; top:.25em; } - -/* Fixes alignment of inline form elements */ -form.inline div, form.inline p { vertical-align:middle; } -form.inline label { position:relative;top:-0.25em; } -form.inline input.checkbox, form.inline input.radio, -form.inline input.button, form.inline button { - margin:0.5em 0; -} -button, input.button { position:relative;top:0.25em; } diff --git a/website/blueprint/src/print.css b/website/blueprint/src/print.css deleted file mode 100755 index bbc7948a..00000000 --- a/website/blueprint/src/print.css +++ /dev/null @@ -1,85 +0,0 @@ -/* -------------------------------------------------------------- - - print.css - * Gives you some sensible styles for printing pages. - * See Readme file in this directory for further instructions. - - Some additions you'll want to make, customized to your markup: - #header, #footer, #navigation { display:none; } - --------------------------------------------------------------- */ - -body { - line-height: 1.5; - font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; - color:#000; - background: none; - font-size: 10pt; -} - - -/* Layout --------------------------------------------------------------- */ - -.container { - background: none; -} - -hr { - background:#ccc; - color:#ccc; - width:100%; - height:2px; - margin:2em 0; - padding:0; - border:none; -} -hr.space { - background: #fff; - color: #fff; - visibility: hidden; -} - - -/* Text --------------------------------------------------------------- */ - -h1,h2,h3,h4,h5,h6 { font-family: "Helvetica Neue", Arial, "Lucida Grande", sans-serif; } -code { font:.9em "Courier New", Monaco, Courier, monospace; } - -a img { border:none; } -p img.top { margin-top: 0; } - -blockquote { - margin:1.5em; - padding:1em; - font-style:italic; - font-size:.9em; -} - -.small { font-size: .9em; } -.large { font-size: 1.1em; } -.quiet { color: #999; } -.hide { display:none; } - - -/* Links --------------------------------------------------------------- */ - -a:link, a:visited { - background: transparent; - font-weight:700; - text-decoration: underline; -} - -a:link:after, a:visited:after { - content: " (" attr(href) ")"; - font-size: 90%; -} - -/* If you're having trouble printing relative links, uncomment and customize this: - (note: This is valid CSS3, but it still won't go through the W3C CSS Validator) */ - -/* a[href^="/"]:after { - content: " (http://www.yourdomain.com" attr(href) ") "; -} */ diff --git a/website/blueprint/src/reset.css b/website/blueprint/src/reset.css deleted file mode 100755 index 09d9131a..00000000 --- a/website/blueprint/src/reset.css +++ /dev/null @@ -1,45 +0,0 @@ -/* -------------------------------------------------------------- - - reset.css - * Resets default browser CSS. - --------------------------------------------------------------- */ - -html, body, div, span, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, code, -del, dfn, em, img, q, dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, dialog, figure, footer, header, -hgroup, nav, section { - margin: 0; - padding: 0; - border: 0; - font-weight: inherit; - font-style: inherit; - font-size: 100%; - font-family: inherit; - vertical-align: baseline; -} - -article, aside, dialog, figure, footer, header, -hgroup, nav, section { - display:block; -} - -body { - line-height: 1.5; -} - -/* Tables still need 'cellspacing="0"' in the markup. */ -table { border-collapse: separate; border-spacing: 0; } -caption, th, td { text-align: left; font-weight: normal; } -table, td, th { vertical-align: middle; } - -/* Remove possible quote marks (") from ,
. */ -blockquote:before, blockquote:after, q:before, q:after { content: ""; } -blockquote, q { quotes: "" ""; } - -/* Remove annoying border on linked images. */ -a img { border: none; } diff --git a/website/blueprint/src/typography.css b/website/blueprint/src/typography.css deleted file mode 100644 index a1cfe272..00000000 --- a/website/blueprint/src/typography.css +++ /dev/null @@ -1,106 +0,0 @@ -/* -------------------------------------------------------------- - - typography.css - * Sets up some sensible default typography. - --------------------------------------------------------------- */ - -/* Default font settings. - The font-size percentage is of 16px. (0.75 * 16px = 12px) */ -html { font-size:100.01%; } -body { - font-size: 75%; - color: #222; - background: #fff; - font-family: "Helvetica Neue", Arial, Helvetica, sans-serif; -} - - -/* Headings --------------------------------------------------------------- */ - -h1,h2,h3,h4,h5,h6 { font-weight: normal; color: #111; } - -h1 { font-size: 3em; line-height: 1; margin-bottom: 0.5em; } -h2 { font-size: 2em; margin-bottom: 0.75em; } -h3 { font-size: 1.5em; line-height: 1; margin-bottom: 1em; } -h4 { font-size: 1.2em; line-height: 1.25; margin-bottom: 1.25em; } -h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.5em; } -h6 { font-size: 1em; font-weight: bold; } - -h1 img, h2 img, h3 img, -h4 img, h5 img, h6 img { - margin: 0; -} - - -/* Text elements --------------------------------------------------------------- */ - -p { margin: 0 0 1.5em; } -p img.left { float: left; margin: 1.5em 1.5em 1.5em 0; padding: 0; } -p img.right { float: right; margin: 1.5em 0 1.5em 1.5em; } - -a:focus, -a:hover { color: #000; } -a { color: #009; text-decoration: underline; } - -blockquote { margin: 1.5em; color: #666; font-style: italic; } -strong { font-weight: bold; } -em,dfn { font-style: italic; } -dfn { font-weight: bold; } -sup, sub { line-height: 0; } - -abbr, -acronym { border-bottom: 1px dotted #666; } -address { margin: 0 0 1.5em; font-style: italic; } -del { color:#666; } - -pre { margin: 1.5em 0; white-space: pre; } -pre,code,tt { font: 1em 'andale mono', 'lucida console', monospace; line-height: 1.5; } - - -/* Lists --------------------------------------------------------------- */ - -li ul, -li ol { margin: 0; } -ul, ol { margin: 0 1.5em 1.5em 0; padding-left: 3.333em; } - -ul { list-style-type: disc; } -ol { list-style-type: decimal; } - -dl { margin: 0 0 1.5em 0; } -dl dt { font-weight: bold; } -dd { margin-left: 1.5em;} - - -/* Tables --------------------------------------------------------------- */ - -table { margin-bottom: 1.4em; width:100%; } -th { font-weight: bold; } -thead th { background: #c3d9ff; } -th,td,caption { padding: 4px 10px 4px 5px; } -tr.even td { background: #e5ecf9; } -tfoot { font-style: italic; } -caption { background: #eee; } - - -/* Misc classes --------------------------------------------------------------- */ - -.small { font-size: .8em; margin-bottom: 1.875em; line-height: 1.875em; } -.large { font-size: 1.2em; line-height: 2.5em; margin-bottom: 1.25em; } -.hide { display: none; } - -.quiet { color: #666; } -.loud { color: #000; } -.highlight { background:#ff0; } -.added { background:#060; color: #fff; } -.removed { background:#900; color: #fff; } - -.first { margin-left:0; padding-left:0; } -.last { margin-right:0; padding-right:0; } -.top { margin-top:0; padding-top:0; } -.bottom { margin-bottom:0; padding-bottom:0; } diff --git a/website/changelog.md b/website/changelog.md deleted file mode 100644 index becedbe0..00000000 --- a/website/changelog.md +++ /dev/null @@ -1,882 +0,0 @@ ---- -layout: default -title: Changelog -toc: false ---- - -## Version 2.12.x - -|Date|Description|Ticket| -|----|-----------|------| -|2017/09/22|Allow default batch options to be set|MCO-818| -|2017/09/18|Use native array math to calculate responses more quickly|MCO-818| -|2017/08/02|Correctly determine reply status with JSON serializer|MCO-815| -|2017/06/16|Update stomp gem to 1.4.4 to fix SSL|| -|2017/06/07|Update rubocop/securitycop scans and convert YAML.load to safe_load for facts|MCO-807| - -## Version 2.11.x - -|Date|Description|Ticket| -|----|-----------|------| -|2017/10/26|Release *2.11.4*|| -|2017/09/25|Restarting the mcollective service no longer kills running agent subprocesses|MCO-816| -|2017/09/20|Release *2.11.3*|| -|2017/09/18|Speed up calculation of no responses and unexpected responses|MCO-818| -|2017/08/15|Release *2.11.2*|| -|2017/08/02|Correctly determine reply status in all modes|MCO-815| -|2017/07/18|Release *2.11.1*|| -|2017/06/28|Use OpenSSL::Cipher instead of OpenSSL::Cipher::Cipher to avoid warnings with Ruby 2.4|MCO-813| -|2017/06/28|Use Mutex instead of Thread.exclusive to avoid warnings with Ruby 2.4|MCO-812| -|2017/06/28|Use Integer instead of Fixnum to avoid warnings with Ruby 2.4|MCO-811| -|2017/06/21|Release *2.11.0*|| -|2017/06/21|MCollective client now logs sending a request at INFO level with additional details, and server logs handling those requests at INFO level with similar details to help identify corresponding events.|MCO-784| -|2017/06/21|The default location for mcollective server logs is moved from `/var/log/puppetlabs` to `/var/log/puppetlabs/mcollective` (on Unix). No change has been made to Windows log locations.|MCO-783| -|2017/06/21|MCollective will consider symbols and their string representation equivalent for serializing/deserializing and accessing message keys as long as they're unambiguous.|MCO-799| -|2017/06/21|mcollective-client is now compatible with OpenSSL 1.1.0|MCO-804| -|2017/06/21|systemu has been upgraded to a more recent version that fixes an issue marshaling multi-byte characters.|MCO-806| - - -## Version 2.10.x - -|Date|Description|Ticket| -|----|-----------|------| -|2017/10/26|Release *2.10.6*|| -|2017/09/25|Restarting the mcollective service no longer kills running agent subprocesses|MCO-816| -|2017/06/09|Release *2.10.5*|| -|2017/06/07|Fix acceptance source for JDK on Windows|MCO-808| -|2017/05/11|Release *2.10.4*|| -|2017/04/06|Switch to using YAML.safe_load|MCO-794| -|2017/04/04|Release *2.10.3*|| -|2017/03/30|Allow M::Client users to access M::Message|MCO-790| -|2017/03/09|Release *2.10.2*|| -|2017/02/29|Release *2.10.1*|| -|2017/02/06|Fix regression in --batch option|MCO-785| -|2017/01/23|Release *2.10.0*|| -|2016/12/30|Updates for acceptance testing|MCO-782| -|2016/12/20|Fix broken link on plugin index page|DOCUMENT-618| -|2016/12/15|Fix `mco ping` breakage from prior commit in MCO-777|MCO-777| -|2016/12/08|Remove references to old PE versions|| -|2016/11/21|Wait for all expected responses to rpc, report surprises|MCO-777| -|2016/11/16|Singletarget messaging|MCO-736| -|2016/11/07|Gemfile cleanup|| -|2016/11/04|Use publish_timeout from config|MCO-778| -|2016/11/01|Support PQL in STDIN discovery|MCO-776| - -## Version 2.9.x - -|Date|Description|Ticket| -|----|-----------|------| -|2016/10/24|Release *2.9.1*|| -|2016/09/23|Ping application includes discovery_timeout|MCO-775| -|2016/08/05|Release *2.9.0*|| -|2016/06/24|Update Stomp gem dependency to >= 1.4.1|RE-7302| -|2016/06/13|acceptance: allow plugin versioned install|| -|2016/06/01|Update README link to documentation|| -|2016/05/24|Fix mco plugins installation when using puppet-agent on Debian family systems|MCO-688| -|2016/05/19|Move prepare_installation to run first|MCO-763| -|2016/05/17|Add --no-batch-files option|MCO-762| -|2016/05/17|Runtime username/password input to connect to middleware|MCO-760| - -## Version 2.8.x - -|Date|Description|Ticket| -|----|-----------|------| -|2016/06/27|Release *2.8.9*|MCO-761| -|2016/06/20|Make parsing of quoted strings in discovery filter expressions side effects free|| -|2016/06/20|Do not use eval on unchecked strings in discovery filter expressions|MCO-765| -|2016/05/17|Link to PE docs|(#373)[https://github.com/puppetlabs/marionette-collective/pull/373]| -|2016/05/17|Update installation docs|(#376)[https://github.com/puppetlabs/marionette-collective/pull/376]| -|2016/05/11|Fix ActiveMQ install|(#378)[https://github.com/puppetlabs/marionette-collective/pull/378]| -|2016/05/03|Work around RuboCop parser change causing errors on non UTF-8 compliant strings|(#375)[https://github.com/puppetlabs/marionette-collective/pull/375]| -|2016/05/03|Pin rake to 10.4 required for running tests on ruby 1.8.7|(#375)[https://github.com/puppetlabs/marionette-collective/pull/375]| -|2016/02/25|Release *2.8.8*|| -|2016/02/25|Fix dependency on aio for creating the pidfile directory|MCO-753| -|2016/02/18|Improve pidfile handling to avoid running multiple daemons|MCO-751| -|2016/02/18|Update build targets to LTS debian variants|MCO-634| -|2016/02/05|Update mco.bat file to allow for absence of CONFIG_FILE setting|MCO-748| -|2016/01/22|Add windows support to install.rb|MCO-745| -|2016/01/13|Release *2.8.7*|| -|2015/12/17|Fix systemd logrotate unit|MCO-744| -|2015/12/16|Fix run helper for systems where the path to ruby binary includes spaces|MCO-742| -|2015/12/01|Fix negative data plugin comparisons|MCO-739| -|2015/09/15|Release *2.8.6*|MCO-726| -|2015/09/11|Fix solaris smf service manifest for aio|(#345)[https://github.com/puppetlabs/marionette-collective/pull/345]| -|2015/09/10|Release *2.8.5*|MCO-724| -|2015/08/25|Add condrestart to the suse init script for AIO|RE-11690| -|2015/08/21|Release *2.8.4*|MCO-706| -|2015/08/20|Revert RE-5032|MCO-705| -|2015/08/18|Release *2.8.3*|MCO-689| -|2015/08/05|Add solaris smf service for AIO|MCO-687| -|2015/07/17|Fully qualify the label in the osx mco plist|RE-5032| -|2015/07/13|Set character encoding in the OSX plist|(#326)[https://github.com/puppetlabs/marionette-collective/pull/326]| -|2015/07/03|Fix documentation links to use https|DOCS-2092| -|2015/06/05|Add `mco describe_filter` application|MCO-668| -|2015/06/05|Fix quote handling in compound query language|MCO-668| -|2015/06/04|Add acceptance tests|MCO-671| -|2015/05/19|Release *2.8.2*|MCO-648| -|2015/05/12|Do not build for debian stable or testing (target codenames instead)|MCO-665| -|2015/05/08|Exit non-zero when `mco` is called with a non-existent subcommand|MCO-640| -|2015/05/08|Add legacy `libdir` settings to aio sample config|MCO-641| -|2015/05/08|Downgrade warning on absent `libdir`|MCO-647| -|2015/05/04|Add OSX plist for AIO|MCO-646| -|2015/04/23|Drop lucid from build targets|MCO-638| -|2015/04/13|Drop fedora 19 from build targets|MCO-633| -|2015/03/22|Update AIO SUSE init script|RE-3977| -|2015/03/11|Release *2.8.1*|MCO-601| -|2015/03/05|Maintain version number in-tree|MCO-617| -|2015/02/18|Use updated AIO paths|MCO-594| -|2015/02/10|Fix AIO debian condrestart action|MCO-591| -|2015/02/05|Fix problems with 2.8.0 gem loading|MCO-587| -|2015/02/04|Release *2.8.0*|MCO-561| -|2015/02/02|Add AIO init scripts to ext/aio|MCO-555| -|2015/02/02|Move core plugins into sitelibdir|MCO-583| -|2015/01/29|Prefer configuration files from AIO paths|MCO-560| -|2015/01/29|Use $LOAD_PATH for loading plugins|MCO-315| -|2015/01/28|Replace uses of `autoload` with `require`|MCO-580| -|2015/01/21|Fix `mco facts` in absence of fact|MCO-558| -|2015/01/07|Ensure rubocop failures fail the build|MCO-519| -|2014/12/19|Fix powershell exit code interactions|MCO-550| - - -## Version 2.7.x - -|Date|Description|Ticket| -|----|-----------|------| -|2014/12/02|Release *2.7.0*|MCO-525| -|2014/11/24|Revisit STOMP 1.1 heart-beat defaults|MCO-522| -|2014/11/18|Log the senderid of messages at debug|MCO-521| -|2014/11/11|Add a --connection-timeout to the client options|MCO-464| -|2014/11/11|Add search of `$libdir/mcollective/agent/$agent/$action` to `implemented_by`|MCO-466| -|2014/11/10|`mco plugin package` support for 'lib' layout|MCO-314| -|2014/10/28|Add rubocop style checks to the codebase|MCO-136| -|2014/10/22|Expose target collective to custom discovery plugins|MCO-456| -|2014/10/13|Make windows service_manager.rb helper more vigorous in finding ruby|MCO-465| -|2014/10/02|Reraise exceptions caught by the runner thread in the main thread|MCO-475| -|2014/10/01|Add a collective data plugin|MCO-472| -|2014/10/01|Update windows scripts to pass --daemonize to daemon|MCO-474| -|2014/09/20|Report the version of stomp gem at startup|MCO-470| -|2014/09/19|Removed vendoring of the json gem|MCO-457| - - -## Version 2.6.x - -|Date|Description|Ticket| -|----|-----------|------| -|2014/10/29|Release *2.6.1*|MCO-495| -|2014/10/23|Disable SSLv2 and SSLv3 protocols by default|MCO-489| -|2014/10/16|Expose SSL cipher selection via connector settings|MCO-486| -|2014/08/28|Release *2.6.0*|MCO-422| -|2014/08/20|Fix a race condition in type validator plugin loader|MCO-453| -|2014/08/12|Use a distinct reply queue per request|MCO-443| -|2014/08/07|Add `soft_shutdown_timeout` option|MCO-243| -|2014/08/06|Add documentation of heartbeat options to the connector pages|MCO-175| -|2014/08/01|Move signal handling into threads (ruby 2 compatibility)|MCO-421| -|2014/08/01|Allow LC\_ALL environment variable to be unset|MCO-156| -|2014/08/01|Add `discovery_timeout` to the configuration file|MCO-193| -|2014/08/01|Add `registration_splay` configuration option|MCO-272| -|2014/07/31|Allow for agent loading to be globally defaulted|MCO-408| -|2014/07/31|Change the 'expired message' message to indicate the message is being discarded|MCO-418| -|2014/07/31|Allow the --batch flag to specify percentages|MCO-68| -|2014/07/31|Reopen logfiles on SIGWINCH|MCO-328| -|2014/07/31|Add --sort option to rpc clients|MCO-83| -|2014/07/31|Dynamically decide number of columns for output|PR#215| -|2014/07/31|Specify the --daemonize option in sample init scripts|MCO-416| -|2014/07/29|Allow `limit_targets` to be reset|MCO-93| -|2014/07/29|Fixed rabbitmq reply-to under `use_reply_exchange`|MCO-351| -|2014/07/29|Reworked examples of catching uncatchable errors|MCO-411| -|2014/07/23|Make the base64 decoder more strict|MCO-293| -|2014/07/22|Add support for structured facts|MCO-363| -|2014/07/18|Fix `call_agent_batched` to work with activerecord|MCO-205| -|2014/07/18|Fix rpc response processing for bad replies|MCO-264| -|2014/07/17|Fix direct addressing regression introduced in MCO-360|MCO-410| -|2014/07/17|Require connector plugins to have ddls|MCO-407| -|2014/07/17|Add ddls to `activemq` and `rabbitmq` connectors|MCO-406| -|2014/07/16|Fix halt\_code to return the correct exitcode for `mco ping`|MCO-199| -|2014/07/16|Remove all reference to the 'mcollective' agent|MCO-360| -|2014/07/01|Add --no-daemonize and --daemonize option to mcollectived|MCO-181| -|2014/05/21|Fix a url in the solaris readme|MCO-186| - - -## Version 2.5.x - -|Date|Description|Ticket| -|----|-----------|------| -|2014/07/15|Release *2.5.3*|MCO-368| -|2014/06/20|Address potential flaw in aes security plugin. CVE-2014-3251|MCO-329| -|2014/05/30|Fix data plugin load ordering|MCO-346| -|2014/06/10|Release *2.5.2*|MCO-331| -|2014/05/27|Remove '.' from ruby `$LOAD_PATH` CVE-2014-3248|MCO-311| -|2014/05/14|Release *2.5.1*|MCO-295| -|2014/05/12|Allow stomp login/passcode to be optional|MCO-316| -|2014/05/02|Fix dependencies of rpms from `mco plugin package`|MCO-292| -|2014/05/01|Fix rpmbuild error in `mco plugin package`|MCO-285| -|2014/04/24|Stop install mc-call-agent|MCO-266| -|2014/04/24|Improve line parsing in flatfile discovery|MCO-262| -|2014/04/23|Release *2.5.0*|MCO-246| -|2014/04/23|Deprecate Runner#run method|MCO-265| -|2014/04/23|Drop ubuntu 13.04 (raring ringtail) from the supported package builds|MCO-263| -|2014/04/17|Add ubuntu 14.04 (trusty tahir) to the supported package builds|MCO-189| -|2014/04/10|Release *2.5.0-rc1*|MCO-226| -|2014/04/10|Refactor the runner class|MCO-221| -|2014/04/04|Update rubygems requirement to 1.3.7 or greater|MCO-188| -|2014/04/02|Move exception classes from lib/mcollective.rb to lib/mcollective/exceptions.rb|MCO-215| -|2014/03/26|Plugin plugin - add dist macro to Release field|MCOP-17| -|2014/03/12|Implements exponential back-off at the connector level|MCO-192| -|2014/02/28|Log reciept and contents of non-MESSAGE STOMP frames|MCO-191| -|2014/02/25|Connectors should not suggest STOMP 1.1 heartbeats if the gem cannot support them|MCO-198| -|2014/02/13|Remove Fedora 18 from build targets|MCO-166| - -## Version 2.4.x - -|Date|Description|Ticket| -|----|-----------|------| -|2014/02/10|*Release 2.4.1*|MCO-180| -|2014/02/10|Remove reference to package iteration in #package_information|MCO-179| -|2014/02/07|Update documentation to note that `plugin.rabbitmq.use_reply_exchange` should work from 2.4.1|MCO-174| -|2014/01/30|Improve logging when connector fails to connect|MCO-173| -|2014/01/29|Fix `plugin.rabbitmq.use_reply_exchange` subscription behavior|MCO-172| -|2014/01/23|*Release 2.4.0*|MCO-168| -|2014/01/16|*Release 2.4.0-rc2*|MCO-162| -|2014/01/16|MCollective service doesn't exit on Windows|MCO-158| -|2014/01/09|*Release 2.4.0-rc1*|MCO-152| -|2014/01/08|Turned use of removed options into warnings|MCO-151| -|2014/01/08|Removed i18n spike (#18863)|MCO-138| -|2014/01/07|Fixed a spurious warning in 'mco ping'|MCO-146| -|2014/01/07|Config class does not parse fixnum config parameters correctly|MCO-97| -|2014/01/06|deprecate and remove flattened output|MCO-84| -|2013/12/19|Make audit plugin log output match standard format|MCO-142| -|2013/11/07|Add a modulepackage target to the plugin packager|23099| -|2013/11/06|Fix possible thread leak in Shell|23090| -|2013/11/06|Add a timeout option for system commands|22114| -|2013/11/05|Redo the packaging of mcollective|17067| -|2013/10/30|Add rabbitmq federation support with `plugin.rabbitmq.use_reply_exchange`|22603| -|2013/10/30|Update rabbitmq connector documentation for recent version of rabbitmqadmin|19537| -|2013/10/17|mcollective service does not gracefully exit on windows|20467| -|2013/10/16|Add option to thread client|21910| -|2013/10/16|Publishing time should not be part of the request time|21910| -|2013/10/11|Add a stdin discovery method|22061| -|2013/10/08|Plugin packager doesn't apply --pluginversion option|22790| -|2013/10/07|Mcollective plugins cannot express dependencies|22361| -|2013/10/03|Ability to retrieve multiple facts through rpcutil|21788| -|2013/10/01|Fix packaging for debain/ubuntu with ruby 1.9|16572| -|2013/09/27|Fix buildmacpkg|16786| -|2013/09/27|Fix --nodes 'nodefile' on ruby 1.9.3|22720| -|2013/09/25|MCO Plugin Packager produces more than one source artifact|22316| -|2013/09/06|Fix directed request on subcollectives with rabbit connector|21755| -|2013/08/19|add an install.rb file to mcollective|22220| -|2013/08/02|Support Stomp 1.1 with RabbitMQ and ActiveMQ|15182| -|2013/07/31|Surpress Errno::ESRCH info messages when running shell commands|21779| -|2013/07/03|Improve error reporting when requesting docs for a non existing plugin|21429| -|2013/07/03|Support aggregate plugins in 'mco plugin doc'|18414| -|2013/07/03|Allow the ActiveMQ and RabbitMQ SSL cert paths to be set using environment variables|20550| -|2013/06/23|Gracefully handle whitespaces in the config file before config keys|21407| -|2013/06/19|Ensure the line numbers are printed correctly on both Windows and Unix|20506| -|2013/06/19|Remove the rpchelptemplate and helptemplatedir options|20714| -|2013/06/18|Correctly detect Windows absolute paths|21251| -|2013/06/10|Fix and centralize handling of boolean values for settings|19751| -|2013/06/06|Clone the default values from the DDL to avoid accidental modifications to the cached DDL file|21104| -|2013/06/05|Filter methods on the RPC Client are now idempotent|20233| -|2013/06/04|run() call in an agent can return incorrect Process::Status|17667| -|2013/06/03|Improve debian dependencies so packages can be rebuilt in a chroot|20950| -|2013/05/28|Set expire headers in the ActiveMQ and RabbitMQ message headers|19905| -|2013/05/10|Correctly detect version differences in semver version where the path level is greater 10|20661| -|2013/05/01|Improve behaviour of data matchers when return values are nil|20059| -|2013/04/29|Improve config defaults on windows machines|20388| -|2013/04/18|Enforce valid identity names in the file discovery method|20282| -|2013/04/11|Add direct addressing awareness to the fire and forget request mode|17930| -|2013/03/22|Remove the topicprefix, queueprefix and topicsep options|19673| -|2013/03/21|Remove the plugin.discovery.timeout setting as it's not relevant anymore|19694| -|2013/03/21|Improve error reporting from the rpc application in the light of direct_addressing|19827| -|2013/03/20|Fail with a friendly error message when no libdir is set|19752| -|2013/03/14|Change default RabbitMQ and ActiveMQ ports to 61613|19734| -|2013/03/13|Set correct reply-to headers in the RabbitMQ connector|17034| -|2013/03/12|Pre-populate the data from data plugins like agent replies|19564| -|2013/03/12|Explicitly include StringIO|19367| -|2013/03/12|Enable direct addressing by default|19665| -|2013/02/20|Fix error code collision on PLMC18|19366| -|2013/02/15|Validate arguments supplied to the RPC application and raise errors sooner|19181| - -## Version 2.3.x - -|Date|Description|Ticket| -|----|-----------|------| -|2014/01/08|Turned use of removed options into warnings|MCO-151| -|2014/01/08|Removed i18n spike (#18863)|MCO-138| -|2014/01/07|Fixed a spurious warning in 'mco ping'|MCO-146| -|2014/01/07|Config class does not parse fixnum config parameters correctly|MCO-97| -|2014/01/06|deprecate and remove flattened output|MCO-84| -|2013/12/19|Make audit plugin log output match standard format|MCO-142| -|*2013/11/07*|*Release 2.3.3*|23107| -|2013/11/07|Add a modulepackage target to the plugin packager|23099| -|2013/11/06|Fix possible thread leak in Shell|23090| -|2013/11/06|Add a timeout option for system commands|22114| -|2013/11/05|Redo the packaging of mcollective|17067| -|2013/10/30|Add rabbitmq federation support with `plugin.rabbitmq.use_reply_exchange`|22603| -|2013/10/30|Update rabbitmq connector documentation for recent version of rabbitmqadmin|19537| -|2013/10/17|mcollective service does not gracefully exit on windows|20467| -|2013/10/16|Add option to thread client|21910| -|2013/10/16|Publishing time should not be part of the request time|21910| -|2013/10/11|Add a stdin discovery method|22061| -|2013/10/08|Plugin packager doesn't apply --pluginversion option|22790| -|2013/10/07|Mcollective plugins cannot express dependencies|22361| -|2013/10/03|Ability to retrieve multiple facts through rpcutil|21788| -|2013/10/01|Fix packaging for debain/ubuntu with ruby 1.9|16572| -|2013/09/27|Fix buildmacpkg|16786| -|2013/09/27|Fix --nodes 'nodefile' on ruby 1.9.3|22720| -|2013/09/25|MCO Plugin Packager produces more than one source artifact|22316| -|2013/09/06|Fix directed request on subcollectives with rabbit connector|21755| -|2013/08/19|add an install.rb file to mcollective|22220| -|2013/08/02|Support Stomp 1.1 with RabbitMQ and ActiveMQ|15182| -|2013/07/31|Surpress Errno::ESRCH info messages when running shell commands|21779| -|*2013/07/11*|*Release 2.3.2*|21527| -|2013/07/03|Improve error reporting when requesting docs for a non existing plugin|21429| -|2013/07/03|Support aggregate plugins in 'mco plugin doc'|18414| -|2013/07/03|Allow the ActiveMQ and RabbitMQ SSL cert paths to be set using environment variables|20550| -|2013/06/23|Gracefully handle whitespaces in the config file before config keys|21407| -|2013/06/19|Ensure the line numbers are printed correctly on both Windows and Unix|20506| -|2013/06/19|Remove the rpchelptemplate and helptemplatedir options|20714| -|2013/06/18|Correctly detect Windows absolute paths|21251| -|2013/06/10|Fix and centralize handling of boolean values for settings|19751| -|2013/06/06|Clone the default values from the DDL to avoid accidental modifications to the cached DDL file|21104| -|2013/06/05|Filter methods on the RPC Client are now idempotent|20233| -|2013/06/04|run() call in an agent can return incorrect Process::Status|17667| -|2013/06/03|Improve debian dependencies so packages can be rebuilt in a chroot|20950| -|2013/05/28|Set expire headers in the ActiveMQ and RabbitMQ message headers|19905| -|2013/05/10|Correctly detect version differences in semver version where the path level is greater 10|20661| -|2013/05/01|Improve behaviour of data matchers when return values are nil|20059| -|2013/04/29|Improve config defaults on windows machines|20388| -|2013/04/18|Enforce valid identity names in the file discovery method|20282| -|2013/04/11|Add direct addressing awareness to the fire and forget request mode|17930| -|2013/03/22|Remove the topicprefix, queueprefix and topicsep options|19673| -|2013/03/21|Remove the plugin.discovery.timeout setting as it's not relevant anymore|19694| -|2013/03/21|Improve error reporting from the rpc application in the light of direct_addressing|19827| -|2013/03/20|Fail with a friendly error message when no libdir is set|19752| -|2013/03/14|Change default RabbitMQ and ActiveMQ ports to 61613|19734| -|2013/03/13|Set correct reply-to headers in the RabbitMQ connector|17034| -|2013/03/12|Pre-populate the data from data plugins like agent replies|19564| -|2013/03/12|Explicitly include StringIO|19367| -|2013/03/12|Enable direct addressing by default|19665| -|2013/02/20|Fix error code collision on PLMC18|19366| -|2013/02/15|Validate arguments supplied to the RPC application and raise errors sooner|19181| -|*2013/02/14*|*Release 2.3.1*|19265| -|2013/02/14|Initial work towards internationalization and online help|18663| -|2013/02/14|Update vendored JSON gem for CVE-2013-0269|19265| -|2013/02/13|Restore the ability to set a discovery timeout on a RPC client|19238| -|2013/02/12|Replace underscores in plugin names with dashes to keep Debian happy|19200| -|2013/02/12|Fix package building on certain Debian systems|19141| -|2013/02/12|Remove the stomp connector|19146| -|2013/02/07|Read the client config before trying to use any configuration options|19105| -|2013/01/22|When an argument fails to parse in the rpc application fail rather than continue with unintended consequences|18773| -|2013/01/22|The fix the *--no-response* argument to the rpc application that broke due to 18438|18513| -|2013/01/22|Set *=* dependencies on the various packages that form a plugin rather than *>=*|18758| -|2013/01/21|Improve presentation of the --help output for applications|18447| -|2013/01/21|When a request failed via *reply.fail*, only show the message and not the half built data|18434| -|*2013/01/10*|*Release 2.3.0*|18259| -|2013/01/10|Raise the correct exception when trying to access unknown data items in a Data results|18466| -|2013/01/10|Fix failing documentation generation for data plugins|18437| -|2013/01/09|Correctly support negative boolean flags declared as --[no]-foo|18438| -|2013/01/03|Add the package iteration number as a dependency for the common packages|18273| -|2012/12/21|The libdirs supplied in the config file now has to be absolute paths to avoid issues when daemonising|16018| -|2012/12/20|Logs the error and backtrace when an action fails|16414| -|2012/12/20|Display the values of :optional and :default in DDL generated help|16616| -|2012/12/20|Allow the query string for the get_data action in rpcutil to be 200 chars|18200| -|2012/12/19|Do not fail when packaging non-agent packages using custom paths|17281| -|2012/12/19|Require Ruby > 1.8 in the RPM specs for Ruby 1.9|17149| -|2012/12/18|Allow required inputs to specify default data in DDLs|17615| -|2012/11/12|When disconnecting set the connection to nil|17384| -|2012/11/08|Define a specific buildroot to support RHEL5 systems correctly|17516| -|2012/11/08|Use the correct rpmbuild commands on systems with rpmbuild-md5|17515| -|2012/10/22|Correctly show help for data plugins without any input queries|17137| -|2012/10/22|Allow the rpcutil#get_data action to work with data queries that takes no input|17138| -|2012/10/03|Improve text output when providing custom formats for aggregations|16735| -|2012/10/03|Correctly process supplied formats when displaying aggregate results|16415| -|2012/10/03|Prevent one failing aggregate function from impacting others|16411| -|2012/10/03|When validation fails indicate which input key has the problem|16617| -|2012/09/26|Data queries can be written without any input queries meaning they take no input|16424| -|2012/09/26|Use correct timeout for agent requests when using direct addressing|16569| -|2012/09/26|Allow BigNum data to be used in data plugin replies|16503| -|2012/09/26|Support non string data in the summary aggregate function|16410| - -## Version 2.2.x - -|Date|Description|Ticket| -|----|-----------|------| -|*2013/05/21*|*Release 2.2.4*|20592| -|2013/05/10|Correctly detect version differences in semver version where the path level is greater 10|20661| -|2013/05/07|Support the latest version of the JSON gem|20594| -|2013/04/29|Improve config defaults on windows machines|20388| -|2013/03/13|Set correct reply-to headers in the RabbitMQ connector|17034| -|2013/03/12|Explicitly include StringIO|19367| -|*2013/02/14*|*Release 2.2.3*|19265| -|2013/02/14|Update vendored JSON gem for CVE-2013-0269|19265| -|2013/02/13|Restore the ability to set a discovery timeout on a RPC client|19238| -|2013/02/12|Replace underscores in plugin names with dashes to keep Debian happy|19200| -|2013/02/12|Fix package building on certain Debian systems|19141| -|2013/02/12|Deprecate the stomp connector|19146| -|2013/02/07|Read the client config before trying to use any configuration options|19105| -|2013/01/22|Set *=* dependencies on the various packages that form a plugin rather than *>=*|18758| -|*2013/01/17*|*Release 2.2.2*|18258| -|2013/01/03|Add the package iteration number as a dependency for the common packages|18273| -|2012/12/24|Restore the :any validator|18265| -|2012/12/19|Do not fail when packaging non-agent packages using custom paths|17281| -|2012/12/19|Require Ruby > 1.8 in the RPM specs for Ruby 1.9|17149| -|2012/11/08|Define a specific buildroot to support RHEL5 systems correctly|17516| -|2012/11/08|Use the correct rpmbuild commands on systems with rpmbuild-md5|17515| -|2012/10/22|Correctly show help for data plugins without any input queries|17137| -|2012/10/22|Allow the rpcutil#get_data action to work with data queries that takes no input|17138| -|*2012/10/17*|*Release 2.2.1*|16965| -|2012/10/03|Improve text output when providing custom formats for aggregations|16735| -|2012/10/03|Correctly process supplied formats when displaying aggregate results|16415| -|2012/10/03|Prevent one failing aggregate function from impacting others|16411| -|2012/10/03|When validation fails indicate which input key has the problem|16617| -|2012/09/26|Data queries can be written without any input queries meaning they take no input|16424| -|2012/09/26|Use correct timeout for agent requests when using direct addressing|16569| -|2012/09/26|Allow BigNum data to be used in data plugin replies|16503| -|2012/09/26|Support non string data in the summary aggregate function|16410| -|2012/09/14|Package discovery plugins that was left out for debian|16413| -|*2012/09/13*|*Release 2.2.0*|16323| - -## Version 2.1.x - -|Date|Description|Ticket| -|----|-----------|------| -|2012/09/10|Update the vendored systemu gem|16289| -|2012/09/06|Improve error reporting for empty certificate files|15924| -|2012/09/05|Restore the verbose behavior while building packages|16216| -|2012/09/04|Add a fetch method that mimic Hash#fetch to RPC Results and Requests|16222| -|2012/09/04|Include the required mcollective version in packages that include the requirement|16173| -|2012/08/29|Add a RabbitMQ specific connector plugin|16168| -|2012/08/22|DDL files can now specify which is the minimal version of mcollective they require|15850| -|2012/08/22|Fix a bug when specifying a custom target directory for packages|15956| -|2012/08/22|When producing plugin packages keep the source deb and rpm|15917| -|2012/08/09|Improve error handling in the plugin application|15848| -|2012/08/08|Add the ability to store general usage information in the DDL|15633| -|2012/08/02|Restore the formatting of the progress bar that was broken in 14255|15805| -|2012/08/01|Display an error when no aggregate results could be computed|15793| -|2012/08/01|Create a plugin system for validators|5078| -|2012/07/19|Create a thread safe caching layer and use it to optimize loading of DDL files|15582| -|2012/07/19|Correctly calculate discovery timeout in all cases and simplify logic around this|15602| -|2012/07/17|Update the *name* field in the rpcutil DDL for consistency|15558 -|2012/07/17|Validate requests against the DDL in the agents prior to authorization or calling actions|15557| -|2012/07/17|Refactor the single big DDL class into a class per type of plugin|15109| -|2012/07/16|Default to the configured default discovery method in the RPC client when nothing is supplied|15506| -|2012/07/16|Improve error handling in generate application|15473| -|*2012/07/12*|*Release 2.1.1*|15379| -|2012/07/11|Add a --display option to RPC clients that overrides the DDL display mode|15273| -|2012/07/10|Do not add a metadata to agents created with the generator as they are now deprecated|15445| -|2012/07/03|Correctly parse numeric and boolean data on the CLI in the rpc application|15344| -|2012/07/03|Fix a bug related to parsing regular expressions in compound statements|15323| -|2012/07/02|Update vim snippets in ext for new DDL features|15273| -|2012/06/29|Create a common package for agent packages containing the DDL for servers and clients|15268| -|2012/06/28|Improve parsing of compound filters where the first argument is a class|15271| -|2012/06/28|Add the ability to declare automatic result summarization in the DDL files for agents|15031| -|2012/06/26|Suppress subscribing to reply queues when no reply is expected|15226| -|2012/06/25|Batched RPC requests will now all have the same requestid|15195| -|2012/06/25|Record the request id on M::Client and in the RPC client stats|15194| -|2012/06/24|Use UUIDs for the request id rather than our own weak implementation|15191| -|2012/06/18|The DDL can now define defaults for outputs and the RPC replies are pre-populated|15087| -|2012/06/18|Remove unused agent help code|15084| -|2012/06/18|Remove unused code from the *discovery* agent related to inventory and facts|15083| -|2012/06/18|Nodes will now refuse to load RPC agents without DDL files|15082| -|2012/06/18|The Plugin Name and Type is now available to DDL objects|15076| -|2012/06/15|Add a get_data action to the rpcutil agent that can retrieve data from data plugins|15057| -|2012/06/14|Allow the random selection of nodes to be deterministic|14960| -|2012/06/12|Remove the Client#discovered_req method and add warnings to the documentation about its use|14777| -|2012/06/11|Add a discovery source capable of doing introspection on running agents|14945| -|2012/06/11|Only do identity filter optimisations for the *mc* discovery source|14942| -|*2012/06/08*|*Release 2.1.0*|14846| -|2012/06/07|Force discovery state to be reset when changing collectives in the RPC client|14874| -|2012/06/07|Create code generators for agents and data plugins|14717| -|2012/06/07|Fix the _No response from_ report to be correctly formatted|14868| -|2012/06/07|Sub collectives and direct addressing mode now works correctly|14668| -|2012/06/07|The discovery method is now pluggable, included is one supporting flat files|14255| -|2012/05/28|Add an application to assist shell completion systems with bash and zsh completion plugins|14196| -|2012/05/22|Improve error messages from the packager when a DDL file cannot be found|14595| -|2012/05/17|Add a dependency on stomp to the rubygem|14300| -|2012/05/17|Adjust the ActiveMQ and Stomp connect_timeout to allow IPv4 fall back to happen in dual homed hosts|14496| -|2012/05/16|Add a plugable data source usable in discovery and other plugins|14254| -|2012/05/04|Improve version dependencies and upgrade experience of debian packages|14277| -|2012/05/03|Add the ability for the DDL to load DDL files from any plugin type|14293| -|2012/05/03|Rename the MCollective::RPC::DDL to MCollective::DDL to match its larger role in all plugins|14254| - -## Version 2.0.x - -|Date|Description|Ticket| -|----|-----------|------| -|*2013/02/14*|*Release 2.0.1*|19265| -|2013/02/14|Update vendored JSON gem for CVE-2013-0269|19265| -|2012/05/04|Improve version dependencies and upgrade experience of debian packages|14277| -|*2012/04/30*|*Release 2.0.0*|13900| - -## Version 1.3.x - -|Date|Description|Ticket| -|----|-----------|------| -|2012/04/30|Compound filters when set from the RPC client were not working|14239| -|2012/04/25|Various improvements to the RPM spec file wrt licencing, dependencies etc|9451| -|2012/04/25|Support using rpmbuild-md5 to create RPMs and support Fedora|14159| -|2012/04/25|Improve LSB compliance in the Red Hat and Debian RC scripts|14151| -|2012/04/19|Fix reference to _topicnamesep_ and remove _topicprefix_ from examples|13873| -|2012/04/19|Remove dependency on FPM for building RPM and Deb packages|13573| -|2012/04/18|Improve default output format from the mco script|14056| -|2012/04/17|Remove unintended requirement that only newest stomp gems be used|13978| -|2012/04/12|New init script for Debian that uses LSB functions to start and stop the daemon|13043| -|2012/04/12|Use sed -i in the Rakefile to improve compatibility with OS X|13324| -|2012/04/11|Fix compatibility with Ruby 1.9.1 by specifically loading rbconfig early on|13872| -|*2012/04/05*|*Release 1.3.3*|13599| -|2012/04/04|Use the MCollective::SSL utility class for crypto functions in the SSL security plugin|13615| -|2012/04/02|Support reading public keys from SSL Certificates as well as keys|13534| -|2012/04/02|Move the help template to the common package for both Debian and RedHat|13434| -|2012/03/30|Support Stomp 1.2.2 CA verified connection to ActiveMQ|10596| -|2012/03/27|_mco help rpc_ now shows the help for the rpc application|13350| -|2012/03/22|Add a mco command that creates native OS packaging for plugins|12597| -|2012/03/21|Default to console based logging at warning level for clients|13285| -|2012/03/20|Work around SSL_read errors when using SSL or AES plugins and Stomp+SSL in Ruby < 1.9.3|13207| -|2012/03/16|Improve logging for SSL connections when using Stomp Gem newer than 1.2.0|13165| -|2012/03/14|Simplify handling of signals like TERM and INT and remove pid file on exit|13105| -|2012/03/13|Create a conventional place to store implemented_by scripts|13064| -|2012/03/09|Handle exceptions added to the Stomp 1.1 compliant versions of the Stomp gem|13020| -|2012/03/09|Specifically enable reliable communications while using the pool style syntax|13040| -|2012/03/06|Initial support for the Windows Platform|12555| -|2012/03/05|Application plugins can now disable any of 3 sections of the standard CLI argument parsers|12859| -|2012/03/05|Fix base 64 encoding and decoding of message payloads that would previous raise unexpected exceptions|12950| -|2012/03/02|Treat :hosts and :nodes as equivalents when supplying discovery data, be more strict about flags discover will accept|12852| -|2012/03/02|Allow exit() to be used everywhere in application plugins, not just in the main method|12927| -|2012/03/02|Allow batch mode to be enabled and disabled on demand during the life of a client|12854| -|2012/02/29|Show the progress bar before sending any requests to give users feedback as soon as possible rather than after first result only|12865| -|2012/02/23|Do not log exceptions in the RPC application when a non existing action is called with request parameters|12719| -|2012/02/17|Log miscellaneous Stomp errors at error level rather than debug|12705| -|2012/02/17|Improve subscription tracking by using the subID feature of the Stomp gem and handle duplicate exceptions|12703| -|2012/02/15|Improve error handling in the inventory application for non responsive nodes|12638| -|2012/02/14|Comply to Red Hat guideline by not setting mcollective to start by default after RPM install|9453| -|2012/02/14|Allow building the client libraries as a gem|9383| -|2012/02/13|On Red Hat like systems read /etc/sysconfig/mcollective in the init script to allow modification of the environment|7441| -|2012/02/13|Make the handling of symlinks to the mco script more robust to handle directories with mc- in their name|6275| -|2012/02/01|systemu and therefore MC::Shell can sometimes return nil exit code, the run() method now handles this better by returning -1 exit status|12082| -|2012/01/27|Improve handling of discovery data on STDIN to avoid failures when run without a TTY and without supplying discovery data|12084| -|2012/01/25|Allow the syslog facility to be configured|12109| -|2012/01/13|Add a RPC agent validator to ensure input is one of list of known good values|11935| -|2012/01/09|The printrpc helper did not correctly display empty strings in received output|11012| -|2012/01/09|Add a halt method to the Application framework and standardize exit codes|11280| -|2011/11/21|Remove unintended dependency on _pp_ in the ActiveMQ plugin|10992| -|2011/11/17|Allow reply to destinations to be supplied on the command line or API|9847| -|*2011/11/17*|*Release 1.3.2*|*10830*| -|2011/11/16|Improve error reporting for code errors in application plugins|10883| -|2011/11/15|The limit method is now configurable on each RPC client as well as the config file|7772| -|2011/11/15|Add a --graph option to the ping application that shows response distribution|10864| -|2011/11/14|An ActiveMQ specific connector was added that supports direct connections|7899| -|2011/11/11|SimpleRPC clients now support native batching with --batch|5939| -|2011/11/11|The client now unsubscribes from topics when it's idle minimising the risk of receiving misdirected messages|10670| -|2011/11/09|Security plugins now ignore miss directed messages early thus using fewer resources|10671| -|2011/10/28|Support ruby-1.9.2-p290 and ruby-1.9.3-rc1|10352| -|2011/10/27|callerid, certificate names, and identity names can now only have \w . and - in them|10327| -|2011/10/25|When discovery information is provided always accept it without requiring reset first|10265| -|2011/10/24|Add :number, :integer and :float to the DDL and rpc application|9902| -|2011/10/22|Speed up discovery when limit targets are set|10133| -|2011/10/22|Do not attempt to validate TTL and Message Times on replies in the SSL plugin|10226| -|2011/10/03|Allow the RPC client to raise an exception rather than exit on failure|9360| -|2011/10/03|Allow the TTL of requests to be set in the config file and the SimpleRPC API|9399| -|2011/09/26|Cryptographically secure the TTL and Message Time of requests when using AES and SSL plugins|9400| -|2011/09/20|Update default shipped configurations to provide a better out of the box experience|9452| -|2011/09/20|Remove deprecated mc- scripts|9402| -|2011/09/20|Keep track of messages that has expired and expose the stat in rpcutil and inventory application|9456| -|*2011/09/16*|*Release 1.3.1*|*9133*| -|2011/09/9|Use direct messaging where possible for identity filters and make the rpc application direct aware|8466| -|2011/08/29|Enforce a 60 second TTL on all messages by default|8325| -|2011/08/29|Change the default classes.txt file to be in line with Puppet defaults|9133| -|2011/08/06|Add reload-agents and reload-loglevel commands to the redhat RC script|7730| -|2011/08/06|Avoid reloading the authorization class over and over from disk on each request|8703| -|2011/08/06|Add a boolean validator to SimpleRPC agents|8799| -|2011/08/06|Justify text results better when using printrpc|8807| -|2011/07/22|Add --version to the mco utility|7822| -|2011/07/22|Add missing meta data to the discovery agent|8497| -|2011/07/18|Raise an error if invalid format fact filters are supplied|8419| -|2011/07/14|Add a rich discovery query language|8181| -|2011/07/08|Do not set RUBYLIB in the RC scripts, the OS should do the right thing|8063| -|2011/07/07|Add a -j argument to all SimpleRPC clients that causes printrpc to produce JSON data|8280| -|2011/06/30|Add the ability to do point to point comms for requests affecting small numbers of hosts|7988| -|2011/06/21|Add support for Stomp Gem version 1.1.9 callback based logging|7960| -|2011/06/21|On the server side log missing DDL files at debug and not warning level|7961| -|2011/06/16|Add the ability for nodes to subscribe to per-node queues, off by default|7225| -|2011/06/12|Remove assumptions about middleware structure from the core and move it to the connector plugins|7619| -|*2011/06/08*|*Release 1.3.0*|7796| -|2011/06/07|Exceptions raised during option parsing were not handled and resulted in stack traces|7796| -|2011/06/06|Remove the sshkey, it's being moved to the plugin repository|7794| -|2011/06/02|Correct parsing of MCOLLECTIVE_EXTRA_OPTS in cases where no config related settings were set|7755| -|2011/05/31|Disconnect from the middleware when an application calls exit|7712| -|2011/05/29|Validations failure in RPC agents will now raise the correct exceptions as documented|7711| -|2011/05/25|Make the target collective for registration messages configurable|7650| -|2011/05/24|Rename the connector plugins send method to publish to avoid issues ruby Object#send|7623| -|2011/05/23|Log a warning when the CF file parsing fails rather than raise a whole ruby exception|7627| -|2011/05/23|Allow applications to use the exit method as would normally be expected|7626| -|2011/05/22|Refactor subscribe and unsubscribe so that middleware structure is entirely contained in the connectors|7620| -|2011/05/21|Add the ability for agents to programatically declare if they should work on a node|7583| -|2011/05/20|Improve error reporting in the single application framework|7574| -|2011/05/16|Allow _._ in fact names|7532| -|2011/05/16|Fix compatability issues with RH4 init system|7448| -|2011/05/15|Handle failures from remote nodes better in the inventory app|7524| -|2011/05/06|Revert unintended changes to the Debian rc script|7420| -|2011/05/06|Remove the _test_ agent that was accidentally checked in|7425| - -## Version 1.2.x - -|Date|Description|Ticket| -|----|-----------|------| -|*2011/06/30*|*Release 1.2.1*|8117| -|2011/06/02|Correct parsing of MCOLLECTIVE_EXTRA_OPTS in cases where no config related settings were set|7755| -|2011/05/23|Allow applications to use the exit method as would normally be expected|7626| -|2011/05/16|Allow _._ in fact names|7532| -|2011/05/16|Fix compatability issues with RH4 init system|7448| -|2011/05/15|Handle failures from remote nodes better in the inventory app|7524| -|2011/05/06|Revert unintended changes to the Debian rc script|7420| -|2011/05/06|Remove the _test_ agent that was accidentally checked in|7425| -|*2011/05/04*|*Release 1.2.0*|7227| - -## Version 1.1.x - -|Date|Description|Ticket| -|----|-----------|------| -|2011/05/03|Improve Red Hat RC script by using distro builtin functions|7340| -|2011/05/01|Support setting a priority on Stomp messages|7246| -|2011/04/30|Handle broken and incomplete DDLs better and improve the format of DDL output|7191| -|2011/04/23|Encode the target agent and collective in requests|7223| -|2011/04/20|Make the SSL Cipher used a config option|7191| -|2011/04/20|Add a clear method to the PluginManager that deletes all plugins, improve test isolation|7176| -|2011/04/19|Abstract the creation of request and reply hashes to simplify connector plugin development|5701| -|2011/04/15|Improve the shellsafe validator and add a Util method to do shell escaping|7066| -|2011/04/14|Update Rakefile to have a mail_patches task|6874| -|2011/04/13|Update vendored systemu library for Ruby 1.9.2 compatability|7067| -|2011/04/12|Fix failing tests on Ruby 1.9.2|7067| -|2011/04/11|Update the DDL documentation to reflect the _mco help_ command|7042| -|2011/04/11|Document the use filters on the CLI|5917| -|2011/04/11|Improve handling of unknown facts in Util#has_fact? to avoid exceptions about nil#clone|6956| -|2011/04/11|Correctly set timeout on the discovery agent to 5 seconds as default|7045| -|2011/04/11|Let rpcutil#agent_inventory supply _unknown_ for missing values in agent meta data|7044| -|*2011/04/07*|*Release 1.1.4*|6952| -|2011/03/28|Correct loading of vendored JSON gem|6877| -|2011/03/28|Show collective and sub collective info in the inventory application|6872| -|2011/03/23|Disconnect from the middleware when mcollectived disconnects|6821| -|2011/03/21|Update rpcutil ddl file to be less strict about valid fact names|6764| -|2011/03/22|Support reading configuration from configfir/plugin.d for plugins|6623| -|2011/03/21|Update default configuration files for subcollectives|6741| -|2011/03/16|Add the ability to implement actions using external scripts|6705| -|2011/03/15|Port mc-controller to the Application framework and deprecate the exit command|6637| -|2011/03/13|Only cache registration and discovery agents, handle the rest as new instances|6692| -|2011/03/08|PluginManager can now create new instances on demand for a plugin type|6622| -|*2011/03/07*|*Release 1.1.3*|6609| -|2011/03/04|Rename /usr/sbin/mc to /usr/bin/mco|6578| -|2011/03/01|Wrap rpcclient in applications ensuring that options is always set|6308| -|2011/02/28|Make communicating with the middleware more robust by including send calls in timeouts|6505| -|2011/02/28|Create a wrapper to safely run shell commands avoiding zombies|6392| -|2011/02/19|Introduce Subcollectives for network partitioning|5967| -|2011/02/19|Improve error handling when parsing arguments in the rpc application|6388| -|2011/02/19|Fix error logging when file_logger creation fails|6387| -|2011/02/17|Correctly parse MCOLLECTIVE_EXTRA_OPTS in the new unified binary framework|6354| -|2011/02/15|Allow the signing key and Debian distribution to be customized|6321| -|2011/02/14|Remove inadvertently included package.ddl|6313| -|2011/02/14|Handle missing libdirs without crashing|6306| -|*2011/02/14*|*Release 1.1.2*|6303| -|2011/02/13|Surpress replies to SimpleRPC clients who did not request results|6305| -|2011/02/11|Fix Debian packaging error due to the same file in multiple packages|6276| -|2011/02/11|The application framework will now disconnect from the middleware for consistancy|6292| -|2011/02/11|Returning _nil_ from a registration plugin will skip registration|6289| -|2011/02/11|Set loglevel to warn by default if not specified in the config file|6287| -|2011/02/10|Fix backward compatability with empty fact strings|6278| -|*2011/02/07*|*Release 1.1.1*|6080| -|2011/02/02|Load the DDL from disk once per printrpc call and not for every result|5958| -|2011/02/02|Include full Apache 2 license text|6113| -|2011/01/31|Create a new single executable application framework|5897| -|2011/01/30|Fix backward compatibility with old foo=/bar/ style fact searches|5985| -|2011/01/30|Documentation update to reflect correct default identity behavior|6073| -|2011/01/29|Let the YAML file force fact reloads when the files update|6057| -|2011/01/29|Add the ability for fact plugins to force fact invalidation|6057| -|2011/01/29|Document an approach to disable type validation in the DDL|6066| -|2011/01/19|Add basic filters to the mc-ping command|5933| -|2011/01/19|Add a ping action to the rpcutil agent|5937| -|2011/01/17|Allow MC::RPC#printrpc to print single results|5918| -|2011/01/16|Provide SimpleRPC style results when accessing the MC::Client results directly|5912| -|2011/01/11|Add an option to Base64 encode the STOMP payload|5815| -|2011/01/11|Fix a bug with forcing all facts to be strings|5832| -|2011/01/08|When using reload_agents or USR1 signal no agents would be reloaded|5808| -|2011/01/04|Use the LSB based init script on SUSE|5762| -|2011/01/04|Remove the use of a Singleton in the logging class|5749| -|2011/01/02|Add AES+RSA security plugin|5696| -|2010/12/31|Security plugins now have access to the callerid of the message they are replying to|5745| -|2010/12/30|Allow - in fact names|5727| -|2010/12/29|Treat machines that fail security validation like ones that did not respond|5700| -|*2010/12/29*|*Release 1.1.0*|5695| -|2010/12/28|Remove trailing whitespace from all source files|5702| -|2010/12/28|Adjust the logfile audit format to include local time and all on one line|5694| -|2010/12/26|Improve the SimpleRPC fact_filter helper to support new fact operators|5678| -|2010/12/25|Increase the rpcutil timeout to allow for slow facts|5679| -|2010/12/25|Allow for network and fact source latency when calculating client timeout|5676| -|2010/12/25|Remove MCOLLECTIVE_TIMEOUT and MCOLLECTIVE_DTIMEOUT environment vars in favour of MCOLLECTIVE_EXTRA_OPTS|5675| -|2010/12/25|Refactor the creation of the options hash so other tools don't need to know the internal formats|5672| -|2010/12/21|The fact plugin format has been changed and simplified, the base now provides caching and thread safety|5083| -|2010/12/20|Add parameters <=, >=, <, >, !=, == and =~ to fact selection|5084| -|2010/12/14|Add experimental sshkey security plugin|5085| -|2010/12/13|Log a startup message showing version and log level|5538| -|2010/12/13|Add a console logger|5537| -|2010/12/13|Logging is now plugable and a syslog plugin was provided|5082| -|2010/12/13|Allow libdir to be an array of directories for agents and ddl files|5253| -|2010/12/13|The progress bar will now intelligently figure out the terminal dimentions|5524| - -## Version 1.0.x - -|Date|Description|Ticket| -|----|-----------|------| -|*2011/02/16*|*Release 1.0.1*|6342| -|2011/02/02|Include full Apache 2 license text|6113| -|2011/01/29|The YAML fact plugin kept deleted facts in memory|6056| -|2011/01/04|Use the LSB based init script on SUSE|5762| -|2010/12/30|Allow - in fact names|5727| -|2010/12/29|Treat machines that fail security validation like ones that did not respond|5700| -|2010/12/25|Allow for network and fact source latency when calculating client timeout|5676| -|2010/12/25|Increase the rpcutil timeout to allow for slow facts|5679| -|*2010/12/13*|*Release 1.0.0*|5453| - -## Version 0.4.x - -|Date|Description|Ticket| -|----|-----------|------| -|2010/12/04|Remove the LSB requirements for RedHat systems|5451| -|2010/11/23|Make the YAML fact source thread safe and force all facts to strings|5377| -|2010/11/23|Add get_config_item to rpcutil to retrieve a running config value from a server|5376| -|2010/11/20|Convert mc-facts into a SimpleRPC client|5371| -|2010/11/18|Added GPG signing to Rake packaging tasks (SIGNED=1)|5355| -|2010/11/17|Improve error messages from clients in the case of failure|5329| -|2010/11/17|Add helpers to disconnect from the middleware and update all bundled clients|5328| -|2010/11/16|Correct LSB provides and requires in default init script|5222| -|2010/11/16|Input validation on rpcutil has been improved to match all valid facts|5320| -|2010/11/16|Add the ability to limit the results to a subset of hosts|5306| -|2010/11/15|Add fire and forget mode to SimpleRPC custom_request|5305| -|2010/11/09|General connection settings to the Stomp connector was ignored|5245| -|*2010/10/18*|*Release version 0.4.10*| | -|2010/10/18|Document exit command to mc-controller|152| -|2010/10/13|Log messages that don't pass the filters at debug level|149| -|2010/10/03|Preserve options in cases where RPC::Client instances exist in the same program|148| -|2010/09/30|Add the ability to set different types of callerid in the PSK plugin|145| -|2010/09/30|Improve Ruby 1.9.x compatibility|142| -|2010/09/29|Improve error handling in registration to avoid high CPU usage loops|143| -|*2010/09/21*|*Release version 0.4.9*| | -|2010/09/20|Improve Debian packaging task|140| -|2010/09/20|Add :boolean type support to the DDL|138| -|2010/09/19|Refactor MCollective::RPC to add less unneeded stuff to Object|137| -|2010/09/18|Prevent duplicate config loading with multiple clients active|136| -|2010/09/18|Rotate the log file by default, keeping 5 x 2MB files|135| -|2010/09/18|Write a overview document detailing security of the collective|131| -|2010/09/18|Add MCollective.version, set it during packaging and include it in the rpcutil agent|134| -|2010/09/13|mc-inventory now use SimpleRPC and the rpcutil agent and display server stats|133| -|2010/09/13|Make the path to the rpc-help.erb configurable and set sane default|130| -|2010/09/13|Make the configfile used available in the Config class and add to rpcutil|132| -|2010/09/12|Rework internal statistics and add a rpcutil agent|129| -|2010/09/12|Fix internal memory structures related to agent meta data|128| -|2010/08/24|Update the OpenBSD port for changes in 0.4.8 tarball|125| -|2010/08/23|Fix indention/block error in M:R:Stats|124| -|2010/08/23|Fix permissions in the RPM for files in /etc|123| -|2010/08/23|Fix language in two error messages|122| -|*2010/08/20*|*Release version 0.4.8*| | -|2010/08/19|Fix missing help template in debian packages|90| -|2010/08/18|Clean up some hardlink warnings in the Rakefile|117| -|2010/08/18|Include the website in the main repo and add a simple Rake task|118| -|2010/08/17|Handle exceptions for missing plugins better|115| -|2010/08/17|Add support for ~/.mcollective as a config file|114| -|2010/08/07|SSL security plugin can use either YAML or Marshal|94| -|2010/08/06|Multiple YAML files can now be used as fact source|112| -|2010/08/06|Allow log level to be adjusted at run time with USR2|113| -|2010/07/31|Add basic report scripting support to mc-inventory|111| -|2010/07/06|Removed 'rpm' from the default rake task|109| -|2010/07/06|Add redhat-lsb to the server RPM dependencies|108| -|*2010/06/29*|*Release version 0.4.7*| | -|2010/06/27|Change default factsource to Yaml|106| -|2010/06/27|Added VIM snippets to create DDLs and Agents|102| -|2010/06/26|DDL based help now works better with Symbols in in/output|105| -|2010/06/23|Whitespace at the end of config lines are now stripped|100| -|2010/06/22|printrpc will now inject some colors into results|99| -|2010/06/22|Recover from syntax and other errors in agents|98| -|2010/06/17|The agent a MC::RPC::Client is working on is now available|97| -|2010/06/17|Integrate the DDL with data display helpers like printrpc|92| -|2010/06/15|Avoid duplicate topic subscribes in complex clients|95| -|2010/06/15|Catch some unhandled exceptions in RPC Agents|96| -|2010/06/15|Fix missing help template file from RPM|90| -|*2010/06/14*|*Release version 0.4.6* | | -|2010/06/12|Qualify the Process class to avoid clashes in the discovery agent|88| -|2010/06/12|Add mc-inventory which shows agents, classes and facts for a node|87| -|2010/06/11|mc-facts now supports standard filters|86| -|2010/06/11|Add connection pool retry options and ssl for connection|85| -|2010/06/11|Add support for specifying multiple stomp hosts for failover|84| -|2010/06/10|Tighten up handling of filters to avoid nil's getting into them|83| -|2010/06/09|Sort the mc-facts output to be more readable|82| -|2010/06/08|Fix deprecation warnings in newer Stomp gems|81| -|*2010/06/03*|*Release version 0.4.5* | | -|2010/06/01|Improve the main discovery agent by adding facts and classes to its inventory action|79| -|2010/05/30|Add various helpers to get reports as text instead of printing them|43| -|2010/05/30|Add a custom_request method to call SimpleRPC agents with your own discovery|75| -|2010/05/30|Refactor RPC::Client to be more generic and easier to maintain|75| -|2010/05/29|Fix a small scoping issue in Security::Base|76| -|2010/05/25|Add option --no-progress to disable progress bar for SimpleRPC|74| -|2010/05/23|Add some missing dependencies to the RPMs|72 | -|2010/05/22|Add an option _:process_results_ to the client|71| -|2010/05/13|Fix help output that still shows old branding|70| -|2010/04/27|The supplied generic stompclient now accepts STOMP_PORT in the environment|68 | -|2010/04/26|Add a SimpleRPC Client helper to reset filters|64 | -|2010/04/26|Listen for signal USR1 and reload all agents from disk|65 | -|2010/04/12|Add SimpleRPC Authorization support|63| -|*2010/04/03*|*Release version 0.4.4* | | -|2010/03/27|Make it easier to construct SimpleRPC requests to use with the standard client library|60 | -|2010/03/27|Manipulating the filters via the helper methods will force rediscovery|59 | -|2010/03/23|Prevent Activesupport when brought in by Facter from breaking our logs|57 | -|2010/03/23|Clean up logging for messages not targeted at us|56 | -|2010/03/19|Add exception handling to the registration base class|55 | -|2010/03/03|Use /usr/bin/env ruby instead of hardcoded paths|54| -|2010/02/17|Improve mc-controller and document it|46| -|2010/02/08|Remove some close coupling with Stomp to easy creating of other connectors|49| -|2010/02/01|Made the discovery agent timeout configurable using plugin.discovery.timeout|48| -|2010/01/25|mc-controller now correctly loads/reloads agents.|45| -|2010/01/25|Building packages has been improved to ensure rdocs are always included|44 | -|*2010/01/24*|*Release version 0.4.3* | | -|2010/01/23|Handle ctrl-c during discovery without showing exceptions to users|34 | -|2010/01/21|Force all facts in the YAML fact source to be strings|41 | -|2010/01/19|Add auditing to SimpleRPC clients and Agents | | -|2010/01/18|The SRPM we provide will now build outside of the Rake environment|40| -|2010/01/18|Add a _fail!_ method to RPC::Agent|37| -|2010/01/18|mc-rpc can now be used without supplying arguments|38 | -|2010/01/18|Don't raise an error if no user/pass is given to the stomp connector, try unauthenticated mode|35| -|2010/01/17|Improve error message when Regex validation failed on SimpleRPC input|36| -|*2010/01/13*|*Release version 0.4.2* | | -|2010/01/13|New packaging for Debian provided by Riccardo Setti|29| -|2010/01/07|Improved LSB compliance of the init script - thanks Riccardo Setti|32| -|2010/01/07|Multiple calls to SimpleRPC client would reset discovered hosts|31| -|2010/01/04|Timeouts can now be changed with MCOLLECTIVE_TIMEOUT and MCOLLECTIVE_DTIMEOUT environment vars|25| -|2010/01/04|Specify class and fact filters easier with the new -W or --with option|27 | -|2010/01/04|Added COPYING file to RPMs and tarball|28| -|2010/01/04|Make shorter filter options -C, -I, -A and -F|26| -|*2010/01/02*|*Release version 0.4.1* | | -|2010/01/02|Added hooks to plug into the processing of requests, also enabled setting meta data and timeouts|14| -|2010/01/02|Created readers for @config and @logger in the SimpleRPC agent|23| -|2009/12/30|Don't send out any requests if no nodes were discovered|17| -|2009/12/30|Added :discovered and :discovered_nodes to client stats|20| -|2009/12/30|Add a empty_filter? helper to the RPC mixin|18| -|2009/12/30|Fix formatting bug with progress bar|21| -|2009/12/29|Simplify mc-rpc command line|16| -|2009/12/29|Fix layout issue when printing hosts that did not respond|15| -|*2009/12/29*|*Release version 0.4.0* | | -|2009/12/28|Add support for other configuration management systems in the --with-class filters|13| -|2009/12/28|Add a Util.empty_filter? to test for an empty filter| | -|2009/12/27|Create a new client framework - SimpleRPC|6| -|2009/12/27|Add support for multiple filters of the same type|3| - -## Version 0.3.x - -|Date|Description|Ticket| -|----|-----------|------| -|*2009/12/17*|*Release version 0.3.0* | | -|2009/12/16|Improvements for newer versions of Ruby where TERM signal was not handled|7| -|2009/12/07|MCollective::Util is now a module and plugins can drop in util classes in the plugin dir| | -|2009/12/07|The Rakefile now works with rake provided on Debian 4 systems|2| -|2009/12/07|Improvements in the RC script for Debian and older Ubuntu systems|5| - -## Version 0.2.x - -|Date|Description|Ticket| -|2009/12/01|Release version 0.2.0| | diff --git a/website/configure/client.md b/website/configure/client.md deleted file mode 100644 index 66bfb0fe..00000000 --- a/website/configure/client.md +++ /dev/null @@ -1,611 +0,0 @@ ---- -title: "MCollective » Configure » Clients" -layout: default ---- - -[server_config_ssl_plugin]: ./server.html#ssl-plugin-settings -[discovery]: /mcollective/reference/ui/filters.html -[middleware]: /mcollective/deploy/middleware/ -[activemq_tls_verified]: /mcollective/deploy/middleware/activemq.html#tls-credentials -[activemq_connector]: /mcollective/reference/plugins/connector_activemq.html -[rabbitmq_connector]: /mcollective/reference/plugins/connector_rabbitmq.html -[security_overview]: /mcollective/security.html -[ssl_plugin]: /mcollective/reference/plugins/security_ssl.html -[discovery_plugins]: /mcollective/reference/plugins/discovery.html -[subcollectives]: /mcollective/reference/basic/subcollectives.html -[security_aes]: /mcollective/reference/plugins/security_aes.html - -{% capture badbool %}**Note:** Use these exact values only; do not use "true" or "false."{% endcapture %} - -{% capture pluginname %}**Note:** Capitalization of plugin names doesn't matter; MCollective normalizes it before loading the plugin.{% endcapture %} - -{% capture path_separator %}system path separator (colon \[`:`\] on \*nix, semicolon \[`;`\] on Windows){% endcapture %} - - - -This document describes MCollective client configuration in MCollective 2.0.0 and higher. Older versions may lack certain features. - - -Example / Index ------ - -The following is an example MCollective client config file showing all of the major groups of settings. All of the setting names styled as links can be clicked, and will take you down the page to a full description of that setting. - -[See below the example for a full description of the config file location and format.](#the-client-config-files) - -
# ~/.mcollective
-# or
-# /etc/mcollective/client.cfg
-
-# Connector settings (required):
-# -----------------------------
-
-connector = activemq
-direct_addressing = 1
-
-# ActiveMQ connector settings:
-plugin.activemq.max_reconnect_attempts = 5
-plugin.activemq.pool.size = 1
-plugin.activemq.pool.1.host = middleware.example.net
-plugin.activemq.pool.1.port = 61614
-plugin.activemq.pool.1.user = mcollective
-plugin.activemq.pool.1.password = secret
-plugin.activemq.pool.1.ssl = 1
-plugin.activemq.pool.1.ssl.ca = /var/lib/puppet/ssl/certs/ca.pem
-plugin.activemq.pool.1.ssl.cert = /var/lib/puppet/ssl/certs/web01.example.com.pem
-plugin.activemq.pool.1.ssl.key = /var/lib/puppet/ssl/private_keys/web01.example.com.pem
-plugin.activemq.pool.1.ssl.fallback = 0
-
-# RabbitMQ connector settings:
-plugin.rabbitmq.vhost = /mcollective
-plugin.rabbitmq.pool.size = 1
-plugin.rabbitmq.pool.1.host = middleware.example.net
-plugin.rabbitmq.pool.1.port = 61613
-# ... etc., similar to activemq settings
-
-# Security plugin settings (required):
-# -----------------------------------
-
-securityprovider = ssl
-
-# SSL plugin settings:
-plugin.ssl_server_public = /Users/nick/mcollective.d/credentials/certs/mcollective-servers.pem
-plugin.ssl_client_private = /Users/nick/mcollective.d/credentials/private_keys/nick-mco.pem
-plugin.ssl_client_public = /Users/nick/mcollective.d/credentials/certs/nick-mco.pem
-
-# PSK plugin settings:
-plugin.psk = j9q8kx7fnuied9e
-
-# Interface settings (optional):
-# ------------------------------
-
-# Discovery settings:
-
-default_discovery_method = mc
-# default_discovery_options = /etc/mcollective/nodes.txt
-
-# Performance settings:
-
-direct_addressing_threshold = 10
-ttl = 60
-discovery_timeout = 2
-publish_timeout = 2
-threaded = false
-connection_timeout = 3
-
-# Miscellaneous settings:
-
-color = 1
-rpclimitmethod = first
-
-# Subcollectives (optional):
-# -----------------------------------
-
-collectives = mcollective,uk_collective
-main_collective = mcollective
-
-# Advanced settings and platform defaults:
-# -----------------------------------
-
-logger_type = console
-loglevel = warn
-logfile = /var/log/mcollective.log
-keeplogs = 5
-max_log_size = 2097152
-logfacility = user
-libdir = /usr/libexec/mcollective
-rpchelptemplate = /etc/mcollective/rpc-help.erb
-helptemplatedir = /etc/mcollective
-ssl_cipher = aes-256-cbc
-
-
- - -The Client Config File(s) ------ - -The `mco` client is usually run interactively from an admin workstation, but can also be run by scripts on a server. - -These cases have different needs. In the first, the configuration and credentials should be per-user; in the second, they should be at the system level with restricted visibility of the credentials. MCollective solves this by having two locations for the client configuration: - -* `~/.mcollective` --- A `.mcollective` file in the current user's home directory. If this file exists, `mco` will use it instead of the global config file. -* `/etc/puppetlabs/mcollective/client.cfg` --- A global client config file, which will be used (by versions >= 2.8.1) if `~/.mcollective` doesn't exist. If you are using this file, read access to it and the external credentials it refers to should be strictly controlled, probably limited to the root user. -* `/etc/mcollective/client.cfg` --- A global client config file, which will be used if `~/.mcollective` and `/etc/puppetlabs/mcollective/client.cfg` don't exist. If you are using this file, read access to it and the external credentials it refers to should be strictly controlled, probably limited to the root user. - -### File Format - -Each line consists of a setting, an equals sign, and a value: - - # setting = value - connector = activemq - -The spaces on either side of the equals sign are optional. Lines starting with a `#` are comments. - -> **Note on Boolean Values:** MCollective's config code does not have consistent handling of boolean values. Many of the core settings will accept values of `1/0` and `y/n`, but will fail to handle `true/false`; additionally, each plugin can handle boolean values differently, and some of them do not properly handle the `y/n` values accepted by the core settings. -> -> Nearly all known plugins and core settings accept `1` and `0`. Until further notice, you should always use these for all boolean settings, as no other values are universally safe. - -### Plugin Config Directory (Optional) - -Many of MCollective's settings are named with the format `plugin..`. These settings can optionally be put in separate files, in the `/etc/mcollective/plugin.d/` directory. If you are using the mco client in system-level scripts, this can let the client and the server daemon share some settings, but it isn't particularly useful when you're doing per-user client configuration, and there's no equivalent `plugin.d` directory in the user's home directory. - -To move a `plugin..` setting to an external file, put it in `/etc/mcollective/plugin.d/.cfg`, and use only the `` segment of the setting. So this: - - # /etc/mcollective/server.cfg - plugin.puppet.splay = true - -...is equivalent to: - - # /etc/mcollective/plugin.d/puppet.cfg - splay = true - -Note that this doesn't work for settings like `plugin.psk`, since they have no `` segment; a setting must have at least three segments to go in a plugin.cfg file. - -### Best Practices - -Due to the user-centric nature of client configuration and the need to keep client private keys secure and isolated, it doesn't make as much sense to manage client config files with Puppet (or other config management software). In fact, creating a Puppet-based client configuration toolkit is likely to be a non-trivial effort. - -Instead, we recommend manually distributing a partial configuration file with most of the connector settings filled out, and allowing your admin users to finish their configuration based on their own credentials. Include hints for the settings they'll need to fill in: - - plugin.ssl_client_private = - - -Required Settings ------ - -### Connector Settings - -
connector = activemq
-direct_addressing = 1
-
-# ActiveMQ connector settings:
-plugin.activemq.pool.size = 1
-plugin.activemq.pool.1.host = middleware.example.net
-plugin.activemq.pool.1.port = 61614
-plugin.activemq.pool.1.user = mcollective
-plugin.activemq.pool.1.password = secret
-plugin.activemq.pool.1.ssl = 1
-# When ssl == 1:
-plugin.activemq.pool.1.ssl.ca = /var/lib/puppet/ssl/certs/ca.pem
-plugin.activemq.pool.1.ssl.cert = /var/lib/puppet/ssl/certs/web01.example.com.pem
-plugin.activemq.pool.1.ssl.key = /var/lib/puppet/ssl/private_keys/web01.example.com.pem
-plugin.activemq.pool.1.ssl.fallback = 0
-
-# RabbitMQ connector settings:
-plugin.rabbitmq.vhost = /mcollective
-plugin.rabbitmq.pool.size = 1
-plugin.rabbitmq.pool.1.host = middleware.example.net
-plugin.rabbitmq.pool.1.port = 61613
-# ... etc., similar to activemq settings
-
-
- - -MCollective always requires a connector plugin. The connector plugin is determined by the [middleware][] you chose for your deployment. Each connector plugin will have additional settings it requires. - -> #### Shared Configuration -> -> * All servers and clients must use the same connector plugin, and its settings must be configured compatibly. -> * You must use the right connector plugin for your [choice of middleware][middleware]. -> * The hostname and port must match what the middleware is using. The username and password must be valid login accounts on the middleware. If you are using [CA-verified TLS][activemq_tls_verified], the certificate must be signed by the same CA the middleware is using. - -#### `connector` - -Which connector plugin to use. This is determined by your [choice of middleware][middleware]. - -- _Default:_ `activemq` -- _Allowed values:_ `activemq`, `rabbitmq`, or the name of a third-party connector plugin. {{ pluginname }} - - -#### `direct_addressing` - -Whether your middleware supports direct point-to-point messages. **This should usually be turned on,** and is enabled by default. The built-in `activemq` and `rabbitmq` connectors both support direct addressing, as does the external `redis` connector. (The older `stomp` connector, however, does not.) - -- _Default:_ `1` -- _Allowed values:_ `1`, `0`, `y`, `n` --- {{ badbool }} - -#### ActiveMQ Connector Settings - -ActiveMQ is the main middleware we recommend for MCollective. The ActiveMQ connector can use multiple servers as a failover pool; if you have only one server, just use a pool size of `1`. - -> **Note:** This is only a summary of the most commonly used ActiveMQ settings; there are about ten more settings that can be used to tune the connector's performance. [See the ActiveMQ connector documentation][activemq_connector] for more complete details. - -- **`plugin.activemq.pool.size`** --- How many ActiveMQ servers to attempt to use. _Default:_ (nothing) -- **`plugin.activemq.pool.1.host`** --- The hostname of the first ActiveMQ server. (Note that additional servers use the same settings as the first, incrementing the number.) _Default:_ (nothing) -- **`plugin.activemq.pool.1.port`** --- The Stomp port of the first ActiveMQ server. _Default:_ `61613` or `6163`, depending on the MCollective version. -- **`plugin.activemq.pool.1.user`** --- The ActiveMQ user account to connect as. If the `STOMP_USER` environment variable is set, MCollective will use its value instead of this setting. -- **`plugin.activemq.pool.1.password`** --- The password for the user account being used. If the `STOMP_PASSWORD` environment variable is set, MCollective will use its value instead of this setting. -- **`plugin.activemq.pool.1.ssl`** --- Whether to use TLS when connecting to ActiveMQ. _Default:_ `0`; _allowed:_ `1/0`, `true/false`, `yes/no` -- **`plugin.activemq.pool.1.ssl.fallback`** --- _(When `ssl == 1`)_ Whether to allow unverified TLS if the ca/cert/key settings aren't set. _Default:_ `0`; _allowed:_ `1/0`, `true/false`, `yes/no` -- **`plugin.activemq.pool.1.ssl.ca`** --- _(When `ssl == 1`)_ The CA certificate to use when verifying ActiveMQ's certificate. Must be the fully-qualified path to a `.pem` file. _Default:_ (nothing) -- **`plugin.activemq.pool.1.ssl.cert`** --- _(When `ssl == 1`)_ The certificate to present when connecting to ActiveMQ. Must be the fully-qualified path to a `.pem` file. As of version 2.3.2 MCollective will also check the environment variable `MCOLLECTIVE_ACTIVEMQ_POOL1_SSL_CERT` for -the client's ssl cert. _Default:_ (nothing) -- **`plugin.activemq.pool.1.ssl.key`** --- _(When `ssl == 1`)_ The private key corresponding to this node's certificate. Must be the fully-qualified path to a `.pem` file. As of version 2.3.2 MCollective will also check the environment variable `MCOLLECTIVE_ACTIVEMQ_POOL1_SSL_KEY` for -the client's ssl key. _Default:_ (nothing) - -#### RabbitMQ Connector Settings - -The RabbitMQ connector uses very similar settings to the ActiveMQ connector, with the same `.pool.1.host` style of setting names. - -[See the RabbitMQ connector documentation][rabbitmq_connector] for more complete details. - - -([↑ Back to top](#content)) - - -### Security Plugin Settings - -
securityprovider = ssl
-
-# SSL plugin settings:
-plugin.ssl_server_public = /Users/nick/mcollective.d/credentials/certs/mcollective-servers.pem
-plugin.ssl_client_private = /Users/nick/mcollective.d/credentials/private_keys/nick-mco.pem
-plugin.ssl_client_public = /Users/nick/mcollective.d/credentials/certs/nick-mco.pem
-
-# PSK plugin settings:
-plugin.psk = j9q8kx7fnuied9e
-
-
- -MCollective always requires a security plugin. (Although they're called security plugins, they actually handle more, including message serialization.) Each security plugin will have additional settings it requires. - -> #### Shared Configuration -> -> All servers and clients must use the same security plugin, and its settings must be configured compatibly. - -It's possible to write new security plugins, but most people use one of the three included in MCollective: - -- **SSL:** The best choice for most users. Provides very good security when combined with TLS on the connector plugin (see above). -- **PSK:** Poor security, but easy to configure; fine for proof-of-concept deployments. -- **AES:** Complex to configure, and carries a noticable performance cost in large networks. Only suitable for certain use cases, like where TLS on the middleware is impossible. - -For a full-system look at how security works in MCollective, see [Security Overview][security_overview]. - - -#### `securityprovider` - -Which security plugin to use. - -- _Default:_ `psk` -- _Allowed values:_ `ssl`, `psk`, `aes_security`, or the name of a third-party security plugin. {{ pluginname }} - -#### SSL Plugin Settings - -> **Note:** This security plugin requires you to manage and distribute SSL credentials. [See the SSL security plugin page][ssl_plugin] for full details. In summary: -> -> * All servers share **one** "server" keypair. They must all have a copy of the public key and private key. -> * Every admin user must have a copy of the server public key. -> * Every admin user has their own "client" keypair. -> * Every server must have a copy of **every** authorized client public key. - -All of these settings have **no default,** and must be set for the SSL plugin to work. - -- **`plugin.ssl_server_public`** --- The fully-qualified path to the server public key file, which must be in `.pem` format. -- **`plugin.ssl_client_private`** --- The fully-qualified path to the client private key file, which must be in `.pem` format. -- **`plugin.ssl_client_public`** --- The fully-qualified path to the client public key file, which must be in `.pem` format. - -The server uses different settings, which are covered in the [server config reference][server_config_ssl_plugin]: - -- `plugin.ssl_server_private` -- `plugin.ssl_server_public` -- `plugin.ssl_client_cert_dir` - - -#### PSK Plugin Settings - -> **Note:** The only credential used by this plugin is a single shared password, which all servers and admin users have a copy of. - -- **`plugin.psk`** --- The shared passphrase. If the `MCOLLECTIVE_PSK` environment variable is set, MCollective will use its value instead of this setting. - - -([↑ Back to top](#content)) - - - - -Interface Settings ------ - -
# Discovery settings:
-
-default_discovery_method = mc
-# default_discovery_options = /etc/mcollective/nodes.txt
-
-# Performance settings:
-
-direct_addressing_threshold = 10
-ttl = 60
-
-# Miscellaneous settings:
-
-color = 1
-rpclimitmethod = first
-
-
- - -These settings affect the user-facing behavior of the `mco` client. They generally have sensible defaults and can be ignored, but you can also modify them to match your day-to-day usage. - -### Discovery - -The mco client always "discovers" a list of nodes before issuing requests. For broadcast requests, this is the list of expected responses to the request, and the client will stop waiting once every node has replied. For directed requests, the list is used as the list of destinations as well as the list of expected responses. - -Discovery is [performed with command-line filters][discovery], which can operate on identity, facts, classes, agent plugins, or custom data plugins. - -The default `mc` discovery method uses dummy MCollective messages; this is maximally versatile and requires no central inventory, but entails a wait for responses before mco can send the request. There are other discovery plugins available, all of which trade features for speed. - - -#### `default_discovery_method` - -The default method to use for [discovery][]; you can specify discovery methods per command with the `--dm METHOD` option. - -Discovery methods are provided by [discovery plugins][discovery_plugins]; run `mco plugin doc` and see the "Discovery Methods" section to see a list of available discovery plugins on your system. - -- _Default:_ `mc` -- _Allowed values:_ Any installed discovery plugin - -#### `default_discovery_options` - -Options to pass to the discovery plugin. This acts as a default value for the `--do OPTIONS` command-line flag. - -**Most discovery methods do not use this,** and there isn't a common format for its value; it's parsed independently by each discovery plugin. - -The only common discovery method that uses this setting is `flatfile`, where it should be the filename of a list of nodes (with one node identity per line). Setting the following options: - - default_discovery_method = flatfile - default_discovery_options = /etc/mcollective/nodes.txt - -...is the equivalent of specifying the option `--nodes /etc/mcollective/nodes.txt` (or the pair `--dm flatfile --do /etc/mcollective/nodes.txt`) for every command, except those where you explicitly specify something like `--dm mc`. - -- _Default:_ (nothing) - - -### Performance - -#### `direct_addressing_threshold` - -If [direct addressing](#directaddressing) is enabled and [discovery][] returns few nodes --- less than or equal to the value of this setting --- `mco` will automatically translate a broadcast request into several direct requests. This saves load on your whole infrastructure, since uninvolved servers will not have to deserialize and validate the message. - -- _Default:_ `10` -- _Allowed values:_ Any positive integer - - -#### `ttl` - -The default TTL for requests. You can specify TTL per command with the `--ttl` option. - -Any server that receives a request after its TTL has expired will reject it. Since the recommend SSL security plugin signs the TTL, it cannot be altered or spoofed; this provides some protection from replay attacks. - -- _Default:_ `60` -- _Allowed values:_ Any positive integer - -#### `discovery_timeout` - -Control the timeout for how long to discover nodes. This can be -useful to increase for larger environments. - -- _Default:_ `2` -- _Allowed values:_ Any positive integer - -#### `publish_timeout` - -Increase the timeout for how long the request publishing step can -take. This can be useful to increase for larger environments. - -- _Default:_ `2` -- _Allowed values:_ Any positive integer - -#### `threaded` - -If [threaded](#threaded) mode is enabled, the client will spawn a -receiving thread independent of the thread that sends requests, -allowing responses to be handled as the are available, rather than -after making the requests. This can greatly increase the number of -nodes that can be addressed at one time when using direct addressing. - -- _Default_: `false` -- _Allowed values:_ Any boolean value - -#### `connection_timeout` - -If specified, the client will abort if the connection has not been -established after `connection_timeout` seconds. - -- _Default_: unspecified (no timeout) -- _Allowed values:_ Any positive integer - -### Miscellaneous - -#### `color` - -Whether to use color when displaying text on the console. - -- _Default:_ `1` on \*nix systems; `0` on Windows -- _Allowed values:_ `1`, `0`, `y`, `n` --- {{ badbool }} - -#### `rpclimitmethod` - -How `mco` should choose which nodes to message, when using the `--limit-nodes` option. - -- _Default:_ `first` -- _Allowed values:_ `first`, `random` - - -([↑ Back to top](#content)) - - -Optional Features ------ - -### Subcollectives - -
collectives = mcollective,uk_collective
-main_collective = mcollective
-
-
- -Subcollectives provide an alternate way of dividing up the servers in a deployment. They are mostly useful because the middleware can be made aware of them, which enables traffic flow and access restrictions. In multi-datacenter deployments, this can save bandwidth costs and give some extra security. - -* [See the Subcollectives and Partitioning page][subcollectives] for more details and an example of site partitioning. - -Subcollective membership is managed **on the server side,** by each server's config file. A given server can join any number of collectives, and will respond to commands from any of them. - -On the client side, mco will only send requests to subcollectives that are mentioned in its config file. It will default to sending requests to its `main_collective`. - -> #### Shared Configuration -> -> If you are using any additional collectives (besides the default `mcollective` collective), your middleware must be configured to permit traffic on those collectives. See the middleware deployment guide for your specific middleware to see how to do this: -> -> * ActiveMQ: [Subcollective topic/queue names](/mcollective/deploy/middleware/activemq.html#topic-and-queue-names) --- [Multi-subcollective authorization example](/mcollective/deploy/middleware/activemq.html#detailed-restrictions-with-multiple-subcollectives) - - -#### `collectives` - -A comma-separated list (spaces OK) of [subcollectives][] mco can send requests to. - -- _Default:_ `mcollective` -- _Sample value:_ `mcollective,uk_collective` - -#### `main_collective` - -The default collective to send requests to; requests without an explicit collective will go here. You can specify another collective on the command line with the `--target` option. - -- _Default:_ (the first value of [the `collectives` setting](#collectives), usually `mcollective`) - - -([↑ Back to top](#content)) - - - -Advanced Settings ------ - -These settings can generally be ignored, as appropriate values should have been set by the package you installed MCollective with. - -### Logging - -
logger_type = console
-loglevel = warn
-logfile = /var/log/mcollective.log
-keeplogs = 5
-max_log_size = 2097152
-logfacility = user
-
-
- -By default, the `mco` client logs directly to the console with a loglevel of `warn`. This is generally what you want. You can get more information by setting the `loglevel` to `info` or `debug`, and you can redirect the log to a file by changing the `logger_type`. - -The other log settings are mostly applicable to the server daemon, rather than the client. Some settings only apply if you are using log files, and some only apply if you are using syslog. - -#### `logger_type` - -How the MCollective server daemon should log. You generally want to use a file or syslog. - -- _Default:_ `file` -- _Allowed values:_ `file`, `syslog`, `console` - -#### `loglevel` - -How verbosely to log. - -- _Default:_ `info` -- _Allowed values:_ In increasing order of verbosity: `fatal`, `error` , `warn`, `info`, `debug` - -#### `logfile` - -_When `logger_type == file`_ - -Where the log file should be stored. - -- _Default:_ (nothing; the package's default config file usually sets a platform-appropriate value) -- _Sample value:_ `/var/log/mcollective.log` - -#### `keeplogs` - -_When `logger_type == file`_ - -The number of log files to keep when rotating. - -- _Default:_ `5` - -#### `max_log_size` - -_When `logger_type == file`_ - -Max size in bytes for log files before rotation happens. - -- _Default:_ `2097152` - -#### `logfacility` - -_When `logger_type == syslog`_ - -The syslog facility to use. - -- _Default:_ `user` - - -([↑ Back to top](#content)) - - -### Platform Defaults - -
libdir = /usr/libexec/mcollective
-rpchelptemplate = /etc/mcollective/rpc-help.erb
-helptemplatedir = /etc/mcollective
-ssl_cipher = aes-256-cbc
-
-
- -#### `libdir` - -Where to look for plugins. Should be a single path or a list of paths separated by the {{ path_separator }}. - -This setting is optional from 2.8.0 onwards. - -- _Default:_ None -- _Sample value:_ `/usr/libexec/mcollective:/opt/mcollective` - - -#### `rpchelptemplate` - -The path to one particular ERB template used for part of MCollective's interactive help. - -- _Default:_ If it exists, `rpc-help.erb` in the same directory as the current config file; otherwise, `/etc/mcollective/rpc-help.erb` - -#### `helptemplatedir` - -The path to a directory containing all of MCollective's ERB help templates. - -This setting is generally only useful if you installed MCollective without a package, in which case the help templates may be stored with the source instead of installed in the `/etc/mcollective` directory. - -- _Default:_ The directory containing the `rpchelptemplate` file, usually `/etc/mcollective` -- _Allowed values:_ Any fully qualified path on disk - -#### `ssl_cipher` - -The cipher to use for encryption. This is currently only relevant if you are using the [AES security plugin][security_aes]. - -This setting should be a standard OpenSSL cipher string. See `man enc` for a list. - -- _Default:_ `aes-256-cbc` diff --git a/website/configure/server.md b/website/configure/server.md deleted file mode 100644 index 17a0d42f..00000000 --- a/website/configure/server.md +++ /dev/null @@ -1,800 +0,0 @@ ---- -title: "MCollective » Configure » Servers" -layout: default ---- - - -[middleware]: /mcollective/deploy/middleware/ -[filters]: /mcollective/reference/ui/filters.html -[plugin_directory]: https://docs.puppetlabs.com/mcollective/plugin_directory/ -[subcollectives]: /mcollective/reference/basic/subcollectives.html -[registration]: /mcollective/reference/plugins/registration.html -[puppetdb]: /puppetdb/ -[security_plugin]: #security-plugin-settings -[auditing]: /mcollective/simplerpc/auditing.html -[authorization]: /mcollective/simplerpc/authorization.html -[actionpolicy]: https://docs.puppetlabs.com/mcollective/plugin_directory/authorization_action_policy.html -[security_aes]: /mcollective/reference/plugins/security_aes.html -[security_overview]: /mcollective/security.html -[ssl_plugin]: /mcollective/reference/plugins/security_ssl.html -[activemq_tls_verified]: /mcollective/reference/integration/activemq_ssl.html#ca-verified-tls -[activemq_connector]: /mcollective/reference/plugins/connector_activemq.html -[rabbitmq_connector]: /mcollective/reference/plugins/connector_rabbitmq.html -[stdlib]: http://forge.puppetlabs.com/puppetlabs/stdlib -[client_config_ssl_plugin]: ./client.html#ssl-plugin-settings - -{% capture badbool %}**Note:** Use these exact values only; do not use "true" or "false."{% endcapture %} - -{% capture pluginname %}**Note:** Capitalization of plugin names doesn't matter; MCollective normalizes it before loading the plugin.{% endcapture %} - -{% capture path_separator %}system path separator (colon \[`:`\] on \*nix, semicolon \[`;`\] on Windows){% endcapture %} - - - -This document describes MCollective server configuration in MCollective 2.0.0 and higher. Older versions may lack certain fetaures. - - -Example / Index ------ - -The following is an example MCollective server config file showing all of the major groups of settings. All of the setting names styled as links can be clicked, and will take you down the page to a full description of that setting. - -[See below the example for a full description of the config file location and format.](#the-server-config-files) - -
# /etc/mcollective/server.cfg
-
-# Connector settings (required):
-# -----------------------------
-
-connector = activemq
-direct_addressing = 1
-
-# ActiveMQ connector settings:
-plugin.activemq.pool.size = 1
-plugin.activemq.pool.1.host = middleware.example.net
-plugin.activemq.pool.1.port = 61614
-plugin.activemq.pool.1.user = mcollective
-plugin.activemq.pool.1.password = secret
-plugin.activemq.pool.1.ssl = 1
-plugin.activemq.pool.1.ssl.ca = /var/lib/puppet/ssl/certs/ca.pem
-plugin.activemq.pool.1.ssl.cert = /var/lib/puppet/ssl/certs/web01.example.com.pem
-plugin.activemq.pool.1.ssl.key = /var/lib/puppet/ssl/private_keys/web01.example.com.pem
-plugin.activemq.pool.1.ssl.fallback = 0
-plugin.activemq.stomp_1_0_fallback = 0
-plugin.activemq.heartbeat_interval = 30
-plugin.activemq.max_hbread_fails = 2
-plugin.activemq.max_hbrlck_fails = 0
-
-# RabbitMQ connector settings:
-plugin.rabbitmq.vhost = /mcollective
-plugin.rabbitmq.pool.size = 1
-plugin.rabbitmq.pool.1.host = middleware.example.net
-# ... etc., similar to activemq settings
-
-# Security plugin settings (required):
-# -----------------------------------
-
-securityprovider = ssl
-
-# SSL plugin settings:
-plugin.ssl_client_cert_dir = /etc/mcollective.d/clients
-plugin.ssl_server_private = /etc/mcollective.d/server_private.pem
-plugin.ssl_server_public = /etc/mcollective.d/server_public.pem
-
-# PSK plugin settings:
-plugin.psk = j9q8kx7fnuied9e
-
-# Facts, identity, and classes (recommended):
-# ------------------------------------------
-
-factsource = yaml
-plugin.yaml = /etc/mcollective/facts.yaml
-fact_cache_time = 300
-
-identity = web01.example.com
-
-classesfile = /var/lib/puppet/state/classes.txt
-
-# Registration (recommended):
-# -----------------------
-
-registerinterval = 600
-registration_splay = true
-registration = agentlist
-registration_collective = mcollective
-
-# Subcollectives (optional):
-# -------------------------
-
-collectives = mcollective,uk_collective
-main_collective = mcollective
-
-# Auditing (optional):
-# -------------------
-
-rpcaudit = 1
-rpcauditprovider = logfile
-plugin.rpcaudit.logfile = /var/log/mcollective-audit.log
-
-# Authorization (optional):
-# ------------------------
-
-rpcauthorization = 1
-rpcauthprovider = action_policy
-
-# Logging:
-# -------
-
-logger_type = file
-loglevel = info
-logfile = /var/log/mcollective.log
-keeplogs = 5
-max_log_size = 2097152
-logfacility = user
-
-# Platform defaults:
-# -----------------
-
-daemonize = 1
-activate_agents = true
-soft_shutdown = false
-soft_shutdown_timeout = 5
-libdir = /usr/libexec/mcollective
-ssl_cipher = aes-256-cbc
-
-
- - -([↑ Back to top](#content)) - - - - - - -The Server Config File(s) ------ - -### Main Config File - -MCollective servers are configured with the `server.cfg` file located at `/etc/puppetlabs/mcollective/server.cfg` or `/etc/mcollective/server.cfg`. It contains MCollective's core settings, as well as settings for the various plugins. - -> **Warning:** This file contains sensitive credentials, and should only be readable by the root user, or whatever user the MCollective daemon runs as. - -### File Format - -Each line consists of a setting, an equals sign, and a value: - - # setting = value - connector = activemq - -The spaces on either side of the equals sign are optional. Lines starting with a `#` are comments. - -> **Note on Boolean Values:** MCollective's config code does not have consistent handling of boolean values. Many of the core settings will accept values of `1/0` and `y/n`, but will fail to handle `true/false`; additionally, each plugin can handle boolean values differently, and some of them do not properly handle the `y/n` values accepted by the core settings. -> -> Nearly all known plugins and core settings accept `1` and `0`. Until further notice, you should always use these for all boolean settings, as no other values are universally safe. - -### Plugin Config Directory (Optional) - -Many of MCollective's settings are named with the format `plugin..`. These settings can optionally be put in separate files, in the `/etc/mcollective/plugin.d/` directory. Note the directory `/etc/mcollective/plugin.d` is determined relative to the configuration file in use, if you were to use `/etc/puppetlabs/mcollective/server.cfg` then `/etc/puppetlabs/mcollective/plugin.d` would be consulted. - -To move a `plugin..` setting to an external file, put it in `/etc/mcollective/plugin.d/.cfg`, and use only the `` segment of the setting. So this: - - # /etc/mcollective/server.cfg - plugin.puppet.splay = true - -...is equivalent to: - - # /etc/mcollective/plugin.d/puppet.cfg - splay = true - -Note that this doesn't work for settings like `plugin.psk`, since they have no `` segment; a setting must have at least three segments to go in a plugin.cfg file. - -### Best Practices - -You should manage your MCollective servers' config files with config management software (such as Puppet). While most settings in a deployment are the same, several should be different for each server, and managing these differences manually is impractical. - -If your deployment is fairly simple and there is little division of responsibility (e.g. having one group in charge of MCollective core and another group in charge of several agent plugins), then you can manage the config file with a simple template. - -If your deployment is large or complex, or you expect it to become so, you should manage MCollective settings as individual resources, as this is the only practical way to divide responsibilities within a single file. - -Below is an example of how to do this using the `file_line` type from the [puppetlabs/stdlib module][stdlib]: - -{% highlight ruby %} - # /etc/puppet/modules/mcollective/manifests/setting.pp - define mcollective::setting ($setting = $title, $target = '/etc/mcollective/server.cfg', $value) { - validate_re($target, '\/(plugin\.d\/[a-z]+|server)\.cfg\Z') - $regex_escaped_setting = regsubst($setting, '\.', '\\.', 'G') # assume dots are the only regex-unsafe chars in a setting name. - - file_line {"mco_setting_${title}": - path => $target, - line => "${setting} = ${value}", - match => "^ *${regex_escaped_setting} *=.*$", - } - } - - # /etc/puppet/modules/mcollective_core/manifests/server/connector.pp - # ... - # Connector settings: - mcollective::setting { - 'connector': - value => 'activemq'; - 'direct_addressing': - value => '1'; - 'plugin.activemq.pool.size': - value => '1'; - 'plugin.activemq.pool.1.host': - value => $activemq_server; - 'plugin.activemq.pool.1.port': - value => '61614'; - 'plugin.activemq.pool.1.user': - value => $activemq_user; - 'plugin.activemq.pool.1.password': - value => $activemq_password; - 'plugin.activemq.pool.1.ssl': - value => '1'; - 'plugin.activemq.pool.1.ssl.fallback': - value => '1'; - } - # ... -{% endhighlight %} - -([↑ Back to top](#content)) - - -Required Settings ------ - -### Connector Settings - - -
connector = activemq
-direct_addressing = 1
-
-# ActiveMQ connector settings:
-plugin.activemq.pool.size = 1
-plugin.activemq.pool.1.host = middleware.example.net
-plugin.activemq.pool.1.port = 61614
-plugin.activemq.pool.1.user = mcollective
-plugin.activemq.pool.1.password = secret
-plugin.activemq.pool.1.ssl = 1
-# When ssl == 1:
-plugin.activemq.pool.1.ssl.ca = /var/lib/puppet/ssl/certs/ca.pem
-plugin.activemq.pool.1.ssl.cert = /var/lib/puppet/ssl/certs/web01.example.com.pem
-plugin.activemq.pool.1.ssl.key = /var/lib/puppet/ssl/private_keys/web01.example.com.pem
-plugin.activemq.pool.1.ssl.fallback = 0
-# STOMP 1.1 heartbeat settings
-plugin.activemq.stomp_1_0_fallback = 0
-plugin.activemq.heartbeat_interval = 30
-plugin.activemq.max_hbread_fails = 2
-plugin.activemq.max_hbrlck_fails = 0
-
-# RabbitMQ connector settings:
-plugin.rabbitmq.vhost = /mcollective
-plugin.rabbitmq.pool.size = 1
-plugin.rabbitmq.pool.1.host = middleware.example.net
-plugin.rabbitmq.pool.1.port = 61613
-# ... etc., similar to activemq settings
-
-
- - -MCollective always requires a connector plugin. The connector plugin is determined by the [middleware][] you chose for your deployment. Each connector plugin will have additional settings it requires. - -> #### Shared Configuration -> -> * All servers and clients must use the same connector plugin, and its settings must be configured compatibly. -> * You must use the right connector plugin for your [choice of middleware][middleware]. -> * The hostname and port must match what the middleware is using. The username and password must be valid login accounts on the middleware. If you are using [CA-verified TLS][activemq_tls_verified], the certificate must be signed by the same CA the middleware is using. - -#### `connector` - -Which connector plugin to use. This is determined by your [choice of middleware][middleware]. - -- _Default:_ `activemq` -- _Allowed values:_ `activemq`, `rabbitmq`, or the name of a third-party connector plugin. {{ pluginname }} - - -#### `direct_addressing` - -Whether your middleware supports direct point-to-point messages. **This should usually be turned on,** and is enabled by default. The built-in `activemq` and `rabbitmq` connectors both support direct addressing, as does the external `redis` connector. (The older `stomp` connector, however, does not.) - -- _Default:_ `1` -- _Allowed values:_ `1`, `0`, `y`, `n` --- {{ badbool }} - -#### ActiveMQ Connector Settings - -ActiveMQ is the main middleware we recommend for MCollective. The ActiveMQ connector can use multiple servers as a failover pool; if you have only one server, just use a pool size of `1`. - -> **Note:** This is only a summary of the most commonly used ActiveMQ settings; there are about ten more settings that can be used to tune the connector's performance. [See the ActiveMQ connector documentation][activemq_connector] for more complete details. - -- **`plugin.activemq.pool.size`** --- How many ActiveMQ servers to attempt to use. _Default:_ (nothing) -- **`plugin.activemq.pool.1.host`** --- The hostname of the first ActiveMQ server. (Note that additional servers use the same settings as the first, incrementing the number.) _Default:_ (nothing) -- **`plugin.activemq.pool.1.port`** --- The Stomp port of the first ActiveMQ server. _Default:_ `61613` or `6163`, depending on the MCollective version. -- **`plugin.activemq.pool.1.user`** --- The ActiveMQ user account to connect as. If the `STOMP_USER` environment variable is set, MCollective will use its value instead of this setting. -- **`plugin.activemq.pool.1.password`** --- The password for the user account being used. If the `STOMP_PASSWORD` environment variable is set, MCollective will use its value instead of this setting. -- **`plugin.activemq.pool.1.ssl`** --- Whether to use TLS when connecting to ActiveMQ. _Default:_ `0`; _allowed:_ `1/0`, `true/false`, `yes/no` -- **`plugin.activemq.pool.1.ssl.fallback`** --- _(When `ssl == 1`)_ Whether to allow unverified TLS if the ca/cert/key settings aren't set. _Default:_ `0`; _allowed:_ `1/0`, `true/false`, `yes/no` -- **`plugin.activemq.pool.1.ssl.ca`** --- _(When `ssl == 1`)_ The CA certificate to use when verifying ActiveMQ's certificate. Must be the path to a `.pem` file. _Default:_ (nothing) -- **`plugin.activemq.pool.1.ssl.cert`** --- _(When `ssl == 1`)_ The certificate to present when connecting to ActiveMQ. Must be the path to a `.pem` file. _Default:_ (nothing) -- **`plugin.activemq.pool.1.ssl.key`** --- _(When `ssl == 1`)_ The private key corresponding to this node's certificate. Must be the path to a `.pem` file. _Default:_ (nothing) -- **`plugin.activemq.stomp_1_0_fallback`** --- Whether to fall back to STOMP 1.0 when attempting a STOMP 1.1 connection. _Default:_ true; _allowed_: boolean -- **`plugin.activemq.heartbeat_interval`** --- The minimum period to heartbeat the connection. _Default_: (nothing); _allowed_: positive integer. - -> **Note:** We recommend that everyone using the ActiveMQ or RabbitMQ connector configure `plugin.activemq.heartbeat_interval` and disable `plugin.activemq.stomp_1_0_fallback` -> -> We do this to work around potential problems in the underlying network protocols: -> -> * STOMP 1.0 connections are idle when no messages are being sent. -> * Many firewalls will kill idle TCP connections after a while, which can cause nodes to drop out of the deployment at seemingly random times. -> * MCollective sets the keep-alive flag on its TCP connections, but most default OS configurations only send the first keep-alive packet after about two hours, so this doesn’t really fix the problem. Nodes may still disappear for an hour or so, then come back. -> -> We used to recommend using `registerinterval` for this, but the support for STOMP 1.1 heartbeats is now mature enough to use this is preference. Heartbeats are also a good deal lighter in terms of network traffic and server load in comparison to sending a registration message. - -#### RabbitMQ Connector Settings - -The RabbitMQ connector uses very similar settings to the ActiveMQ connector, with the same `.pool.1.host` style of setting names. - -[See the RabbitMQ connector documentation][rabbitmq_connector] for more complete details. - - -([↑ Back to top](#content)) - - -### Security Plugin Settings - -
securityprovider = ssl
-
-# SSL plugin settings:
-plugin.ssl_client_cert_dir = /etc/mcollective/clients
-plugin.ssl_server_private = /etc/mcollective/server_private.pem
-plugin.ssl_server_public = /etc/mcollective/server_public.pem
-
-# PSK plugin settings:
-plugin.psk = j9q8kx7fnuied9e
-
-
- -MCollective always requires a security plugin. (Although they're called security plugins, they actually handle more, including message serialization.) Each security plugin will have additional settings it requires. - -> #### Shared Configuration -> -> All servers and clients must use the same security plugin, and its settings must be configured compatibly. - -It's possible to write new security plugins, but most people use one of the three included in MCollective: - -- **SSL:** The best choice for most users. Provides very good security when combined with TLS on the connector plugin (see above). -- **PSK:** Poor security, but easy to configure; fine for proof-of-concept deployments. -- **AES:** Complex to configure, and carries a noticable performance cost in large networks. Only suitable for certain use cases, like where TLS on the middleware is impossible. - -For a full-system look at how security works in MCollective, see [Security Overview][security_overview]. - - -#### `securityprovider` - -Which security plugin to use. - -- _Default:_ `psk` -- _Allowed values:_ `ssl`, `psk`, `aes_security`, or the name of a third-party security plugin. {{ pluginname }} - -#### SSL Plugin Settings - -> **Note:** This security plugin requires you to manage and distribute SSL credentials. [See the SSL security plugin page][ssl_plugin] for full details. In summary: -> -> * All servers share **one** "server" keypair. They must all have a copy of the public key and private key. -> * Every admin user must have a copy of the server public key. -> * Every admin user has their own "client" keypair. -> * Every server must have a copy of **every** authorized client public key. - -All of these settings have **no default,** and must be set for the SSL plugin to work. - -- **`plugin.ssl_server_private`** --- The path to the server private key file, which must be in `.pem` format. -- **`plugin.ssl_server_public`** --- The path to the server public key file, which must be in `.pem` format. -- **`plugin.ssl_client_cert_dir`** --- A directory containing every authorized client public key. - -The client uses different settings, which are covered in the [client config reference][client_config_ssl_plugin]: - -- `plugin.ssl_server_public` -- `plugin.ssl_client_private` -- `plugin.ssl_client_public` - - -#### PSK Plugin Settings - -> **Note:** The only credential used by this plugin is a single shared password, which all servers and admin users have a copy of. - -- **`plugin.psk`** --- The shared passphrase. If the `MCOLLECTIVE_PSK` environment variable is set, MCollective will use its value instead of this setting. - - -([↑ Back to top](#content)) - - -Recommended Features ------ - -### Facts, Identity, and Classes - -
factsource = yaml
-plugin.yaml = /etc/mcollective/facts.yaml
-fact_cache_time = 300
-
-identity = web01.example.com
-
-classesfile = /var/lib/puppet/state/classes.txt
-
-
- -MCollective clients use filters to discover nodes and limit commands. (See [Discovery Filters][filters] for more details.) These filters can use a variety of per-server metadata, including **facts, identity,** and **classes.** - -* **Facts:** A collection of key/value data about a server's hardware and software. (E.g. `memorytotal = 8.00 GB`, `kernel = Darwin`, etc.) -* **Identity:** The name of the node. -* **Classes:** The Puppet classes applied to this node. Classes are useful as metadata because they describe what _roles_ a server fills at your site. - -None of these settings are mandatory, but MCollective is less useful without them. - -#### `identity` - -The node's name or identity. This **should** be unique for each node, but does not **need** to be. - -- _Default:_ The value of Ruby's `Socket.gethostname` method, which is usually the server's FQDN. -- _Sample value:_ `web01.example.com` -- _Allowed values:_ Any string containing only alphanumeric characters, hyphens, and dots --- i.e. matching the regular expression `/\A[\w\.\-]+\Z/` - -#### `classesfile` - -A file with a list of classes applied by your configuration management system. This should be a plain text file containing one class name per line. - -Puppet automatically writes a class file, which can be found by running `sudo puppet apply --configprint classfile`. Other configuration tools may be able to write a similar file; see their documentation for details. - -- _Default:_ `/var/lib/puppet/state/classes.txt` - - -#### `factsource` - -Which fact plugin to use. - -MCollective includes a fact plugin called `yaml`. Most users should use this default, set [the `plugin.yaml` setting (see below)](#pluginyaml), and arrange to fill the file it relies on. - -Other fact plugins are available in the [plugin directory][plugin_directory]. These may require settings of their own. - -- _Default:_ `yaml` -- _Allowed values:_ The name of any installed fact plugin, with the `_facts` suffix trimmed off. {{ pluginname }} - -#### `plugin.yaml` - -_When `factsource == yaml`_ - -The fact file(s) to load for [the default `yaml` fact plugin](#factsource). - -- _Default:_ (nothing) -- _Sample value:_ `/etc/mcollective/facts.yaml` -- _Valid values:_ A single path, or a list of paths separated by the {{ path_separator }}. - -**Notes:** - -The default `yaml` fact plugin reads cached facts from a file, which should be a simple YAML hash. If multiple files are specified, they will be merged. (Later files override prior ones if there are conflicting values.) - -**The user is responsible for populating the fact file;** by default it is empty, and MCollective has no facts. - -If you are using Puppet and Facter, you can populate it by putting something like the following in your puppet master's `/etc/puppet/manifests/site.pp`, outside any node definition: - -{% highlight ruby %} - # /etc/puppet/manifests/site.pp - file{"/etc/mcollective/facts.yaml": - owner => root, - group => root, - mode => 400, - loglevel => debug, # reduce noise in Puppet reports - content => inline_template("<%= scope.to_hash.reject { |k,v| k.to_s =~ /(uptime_seconds|timestamp|free)/ }.to_yaml %>"), # exclude rapidly changing facts - } -{% endhighlight %} - -#### `fact_cache_time` - -How long (in seconds) to cache fact results before refreshing from source. This can be ignored unless you're using a non-default `factsource`. - -- _Default:_ `300` - -([↑ Back to top](#content)) - - -### Node Registration - -
registerinterval = 600
-registration_splay = true
-registration = agentlist
-registration_collective = mcollective
-
-
- -By default, registration is disabled, due to [`registerinterval`](#registerinterval) being set to 0. - -Optionally, MCollective servers can [send periodic heartbeat messages][registration] containing some inventory information. This can provide a central inventory at sites that don't already use something like [PuppetDB][], and can also be used as a simple passive monitoring system. - -The default registration plugin, `agentlist`, sends a standard SimpleRPC command over the MCollective middleware, to be processed by some server with an agent called `registration` installed. You would need to ensure that the `registration` agent is extremely performant (due to the volume of message it will receive) and installed on a limited number of servers. If your [middleware][] supports detailed permissions, you must also ensure that it allows servers to send commands to the registration agent ([ActiveMQ instructions](/mcollective/deploy/middleware/activemq.html#detailed-restrictions)). - -Some registration plugins (e.g. `redis`) can insert data directly into the inventory instead of sending an RPC message. This is a flexible system, and the user is in charge of deciding what to build with it, if anything. If all you need is a searchable inventory, [PuppetDB][] is probably closer to your needs. - -#### `registerinterval` - -How long (in seconds) to wait between registration messages. Setting this to 0 disables registration. - -- _Default:_ `0` - -#### `registration_splay` - -Whether to delay up to `registerinterval` when sending the initial -registration message. This can reduce load spikes on your middleware -if you choose to restart your agents in batches. - -- _Default:_ false -- _Allowed values:_ A boolean value - -#### `registration` - -The [registration plugin][registration] to use. - -This plugin must be installed on the server sending the message, and will dictate what the message contains. The default `agentlist` plugin will only send a list of the installed agents. See [Registration Plugins][registration] for more details. - -- _Default:_ `agentlist` -- _Allowed values:_ The name of any installed registration plugin. {{ pluginname }} - -#### `registration_collective` - -Which subcollective to send registration messages to, when using a SimpleRPC-based registration plugin. - -- _Default:_ (the value of [`main_collective`](#maincollective), usually `mcollective`) - - -([↑ Back to top](#content)) - - - -Optional Features ------ - -### Subcollectives - -
collectives = mcollective,uk_collective
-main_collective = mcollective
-
-
- -Subcollectives provide an alternate way of dividing up the servers in a deployment. They are mostly useful because the middleware can be made aware of them, which enables traffic flow and access restrictions. In multi-datacenter deployments, this can save bandwidth costs and give some extra security. - -* [See the Subcollectives and Partitioning page][subcollectives] for more details and an example of site partitioning. - -Subcollective membership is managed **on the server side,** by each server's config file. A given server can join any number of collectives, and will respond to commands from any of them. - -> #### Shared Configuration -> -> If you are using any additional collectives (besides the default `mcollective` collective), your middleware must be configured to permit traffic on those collectives. See the middleware deployment guide for your specific middleware to see how to do this: -> -> * ActiveMQ: [Subcollective topic/queue names](/mcollective/deploy/middleware/activemq.html#topic-and-queue-names) --- [Multi-subcollective authorization example](/mcollective/deploy/middleware/activemq.html#detailed-restrictions-with-multiple-subcollectives) - - -#### `collectives` - -A comma-separated list (spaces OK) of [subcollectives][] this server should join. - -- _Default:_ `mcollective` -- _Sample value:_ `mcollective,uk_collective` - -#### `main_collective` - -The main collective for this server. Currently, this is only used as the default value for the [`registration_collective`](#registrationcollective) setting. - -- _Default:_ (the first value of [the `collectives` setting](#collectives), usually `mcollective`) - - -([↑ Back to top](#content)) - - -### Auditing - -
rpcaudit = 1
-rpcauditprovider = logfile
-plugin.rpcaudit.logfile = /var/log/mcollective-audit.log
-
-
- -Optionally, MCollective can log the SimpleRPC agent commands it receives from admin users, recording both the command itself and some identifying information about the user who issued it. The caller ID of a command is determined by the [security plugin][security_plugin] being used. - -MCollective ships with a local logging audit plugin, called `Logfile`, which saves audit info to a local file (different from the daemon log file). Log lines look like this: - - 2010-12-28T17:09:03.889113+0000: reqid=319719cc475f57fda3f734136a31e19b: reqtime=1293556143 caller=cert=nagios@monitor1 agent=nrpe action=runcommand data={:process_results=>true, :command=>"check_mailq"} - -Tthere are central loggers available from [the plugin directory][plugin_directory], and you can also write your own audit plugins; see [Writing Auditing Plugins][auditing] for more information. - - -#### `rpcaudit` - -Whether to enable [SimpleRPC auditing][Auditing] for all SimpleRPC agent commands. - -- _Default:_ `0` -- _Allowed values:_ `1`, `0`, `y`, `n` --- {{ badbool }} - -#### `rpcauditprovider` - -The name of the audit plugin to use whenever SimpleRPC commands are received. - -- _Default:_ (nothing) -- _Sample value:_ `logfile` -- _Allowed values:_ The name of any installed audit plugin. {{ pluginname }} - - -#### `plugin.rpcaudit.logfile` - -_When `rpcauditprovider == logfile`_ - -The file to write to when using the `logfile` audit plugin. **Note:** this file is not automatically rotated. - -- _Default:_ `/var/log/mcollective-audit.log` - - -([↑ Back to top](#content)) - - -### Authorization - -
rpcauthorization = 1
-rpcauthprovider = action_policy
-
-
- -Optionally, MCollective can refuse to execute agent commands unless they meet some requirement. The exact requirement is defined by an [authorization plugin][authorization]. - -See [SimpleRPC Authorization][authorization] for more details, including how to enable authorization for only certain agents. - -The [actionpolicy][] plugin, which is provided in the [plugin directory][plugin_directory], is fairly popular and seems to meet many users' needs, when combined with a [security plugin][security_plugin] that provides a verified caller ID (such as the SSL plugin). [See its documentation][actionpolicy] for details. - -#### `rpcauthorization` - -Whether to enable [SimpleRPC authorization][Authorization] globally. - -- _Default:_ `0` -- _Allowed values:_ `1`, `0`, `y`, `n` --- {{ badbool }} - -#### `rpcauthprovider` - -The plugin to use when globally managing authorization. See [SimpleRPC Authorization][authorization] for more details. - -- _Default:_ (nothing) -- _Sample value:_ `action_policy` -- _Allowed values:_ The name of any installed authorization plugin. This uses different capitalization/formatting rules from the other plugin settings: if the name of the plugin (in the code) has any interior capital letters (e.g. `ActionPolicy`), you should use a lowercase value for the setting but insert an underscore before the place where the interior capital letter(s) would have gone (e.g. `action_policy`). If the name contains no interior capital letters, simply use a lowercase value with no other changes. - - -([↑ Back to top](#content)) - - -Advanced Settings ------ - -### Logging - -
logger_type = file
-loglevel = info
-logfile = /var/log/mcollective.log
-keeplogs = 5
-max_log_size = 2097152
-logfacility = user
-
-
- -The MCollective server daemon can log to its own log file (which it will automatically rotate), or to the syslog. It can also log directly to the console, if you are running it in the foreground instead of daemonized. - -Some of the settings below only apply if you are using log files, and some only apply if you are using syslog. - -#### `logger_type` - -How the MCollective server daemon should log. You generally want to use a file or syslog. - -- _Default:_ `file` -- _Allowed values:_ `file`, `syslog`, `console` - -#### `loglevel` - -How verbosely to log. - -- _Default:_ `info` -- _Allowed values:_ In increasing order of verbosity: `fatal`, `error` , `warn`, `info`, `debug` - -#### `logfile` - -_When `logger_type == file`_ - -Where the log file should be stored. - -- _Default:_ (nothing; the package's default config file usually sets a platform-appropriate value) -- _Sample value:_ `/var/log/mcollective.log` - -#### `keeplogs` - -_When `logger_type == file`_ - -The number of log files to keep when rotating. - -- _Default:_ `5` - -#### `max_log_size` - -_When `logger_type == file`_ - -Max size in bytes for log files before rotation happens. - -- _Default:_ `2097152` - -#### `logfacility` - -_When `logger_type == syslog`_ - -The syslog facility to use. - -- _Default:_ `user` - - -([↑ Back to top](#content)) - - -### Platform Defaults - -
daemonize = 1
-activate_agents = true
-soft_shutdown = false
-soft_shutdown_timeout = 5
-libdir = /usr/libexec/mcollective
-ssl_cipher = aes-256-cbc
-
-
- -These settings generally shouldn't be changed by the user, but their values may vary by platform. The package you used to install MCollective should have created a config file with platform-appropriate values for these settings. - -#### `daemonize` - -Whether to fork and run the MCollective server daemon in the background. - -This depends on your platform's init system. For example, newer Ubuntu releases require this to be false, while RHEL-derived systems require it to be true. - -- _Default:_ `0` -- _Allowed values:_ `1`, `0`, `y`, `n` --- {{ badbool }} - -#### `activate_agents` - -When set to false, requires each agent be enabled individually with -their `plugin.$plugin_name.activate_agent` setting. - -- _Default:_ true -- _Allowed values:_ Any boolean value - -#### `soft_shutdown` - -When set to true, soft_shutdown will delay the exit of the daemon -until all running agents have either ran to completion or timed out. - -- _Default:_ false -- _Allowed values:_ Any boolean value - -#### `soft_shutdown_timeout` - -When set, soft_shutdown will terminate outstanding agents after this -amount of time has elapsed. - -- _Default:_ unset -- _Allowed values:_ A positive integer - -#### `libdir` - -Where to look for plugins. Should be a single path or a list of paths separated by the {{ path_separator }}. - -This setting is optional from 2.8.0 onwards. - -- _Default:_ None -- _Sample value:_ `/usr/libexec/mcollective:/opt/mcollective` - -#### `ssl_cipher` - -The cipher to use for encryption. This is currently only relevant if you are using the [AES security plugin][security_aes]. - -This setting should be a standard OpenSSL cipher string. See `man enc` for a list. - -- _Default:_ `aes-256-cbc` diff --git a/website/deploy/demo.md b/website/deploy/demo.md deleted file mode 100644 index b8083283..00000000 --- a/website/deploy/demo.md +++ /dev/null @@ -1,26 +0,0 @@ ---- -layout: default -title: "MCollective » Deploy » Demo Toolkit" -subtitle: "Demo MCollective With Vagrant" -toc: false ---- - -[mco_vagrant]: https://github.com/ripienaar/mcollective-vagrant -[readme]: https://github.com/ripienaar/mcollective-vagrant/blob/master/README.md -[vagrant_download]: http://www.vagrantup.com/downloads.html -[vagrant_install]: http://docs.vagrantup.com/v2/installation/index.html -[vagrant_docs]: http://docs.vagrantup.com - - -> If you've never used MCollective before, start here. - -This is a Vagrant-based demo environment that you can quickly get running on your own hardware. On a reasonably powerful server (around 32GB of memory), you can easily run a couple dozen nodes; on a modern-ish laptop, you can probably get at least five, maybe as many as ten. - -The demo creates a fully functional MCollective deployment. It has a lightweight middleware and low security, but enables all major MCollective features, including direct addressing, multiple discovery methods, and several agent plugins. - -Getting Started ------ - -1. [Download Vagrant][vagrant_download] and [install it][vagrant_install] on the machine that will be hosting the demo VMs. You may need to install VirtualBox first. -2. Go to the [mcollective-vagrant repository on GitHub][mco_vagrant]. Clone the repo to your host machine, and follow the instructions in the [README][]. - diff --git a/website/deploy/index.md b/website/deploy/index.md deleted file mode 100644 index 223cd2f2..00000000 --- a/website/deploy/index.md +++ /dev/null @@ -1,87 +0,0 @@ ---- -title: "MCollective » Deployment" -layout: default ---- - -[deploy_summary_in_page]: #deployment-process -[vagrant_demo]: ./demo.html -[standard_deploy]: ./standard.html -[middleware]: ./middleware/ -[install]: ./install.html -[config_server]: /mcollective/configure/server.html -[config_client]: /mcollective/configure/client.html -[plugin_deploy]: ./plugins.html -[puppet]: /puppet/ - -Summary ------ - -Getting started with MCollective takes more than just installing --- it has multiple components, with interdependent configuration, installed across your infrastructure. - -Which is to say: Deploying MCollective isn't difficult, but it requires some planning. You should play with a **demo,** **read** a little, then **deploy.** - -### Demo - -* If you've never used MCollective before, [try the Vagrant-based demo toolkit][vagrant_demo]. It will quickly get you a full MCollective environment with a dozen or so nodes, so you can get used to the client interface and perhaps write a simple agent plugin. - -### Read - -* [Read the deployment overview](/mcollective/overview_components.html) to meet the components of MCollective (servers, clients, and middleware). -* Understand the steps of deploying MCollective ([see summary below][deploy_summary_in_page]). - -### Deploy - -* [Follow the "standard deployment" getting started guide][standard_deploy]. These conventions will get you a very secure and high-performance starter deployment. You can customize MCollective in depth later when you need to. -* If you have special needs and know what you're doing, you can design a custom deployment. Decide on a middleware type and topology, decide on a security plugin, and follow the steps of the deployment process outlined below. - -> ### Puppet Enterprise -> -> Puppet Enterprise includes MCollective, and automates the entire deployment process. See [the PE orchestration documentation][pe_orchestration] for more details; see [the PE installation instructions][pe_install] to install PE. -> -> * Puppet Enterprise 3.0 ships with MCollective 2.2.4. -> * Puppet Enterprise 2.8 ships with MCollective 1.2.1. - -[pe_orchestration]: /pe/latest/orchestration_overview.html -[pe_install]: /pe/latest/install_basic.html - - -Deployment Process ------ - -In general, you need to do the following to deploy MCollective: - -1. Collect credentials and global configuration -2. [Deploy and configure middleware][middleware] -3. [Install MCollective][install] (on both servers and admin workstations) -4. [Configure servers][config_server] -5. [Configure clients][config_client] -6. [Deploy plugins][plugin_deploy] - -The [standard deployment getting started guide][standard_deploy] goes into greater detail on each of these steps, and describes the deployment that most users should start with. - -If you are deploying in an unusual way or at a very large scale, you will probably still want to use the standard guide as a starting point. At each major step, you can take detours to configure, e.g., multiple middleware servers, alternate middleware, multiple subcollectives, etc. - -Best Practices ------ - -### Use Configuration Management - -**Use [Puppet][] or some other form of configuration management to deploy MCollective.** It is the textbook example for why you need config management: - -* It has multiple components that run on many different machines. -* It has pieces of global configuration that must be set harmoniously, everywhere. -* Most of its settings are identical for machines in a given role (e.g. every server), but some of its settings have per-system differences. This is easy to manage with a template, and incredibly frustrating to manage by hand. -* Its configuration _will_ change over time, and the changes affect many many systems at once. (New/updated agents must be deployed to all servers; when a new admin user is introduced, every server must be made aware of their permissions.) - -In summary, its configuration requirements are strict, and configuration drift will cause it to stop working. Use Puppet. - -We don't currently have drop-in Puppet code for deploying MCollective, so you'll have to build several parts of your deployment yourself. In the [standard deployment getting-started guide][standard_deploy], we give suggestions on Puppet code wherever possible. We also hope to have something a bit more standardized in the future. - -### Design For Human Capabilities - -The most succinct way to say this is: Don't build a 10,000 node collective with no subcollectives. - -Beyond a certain population size, messing up an important command gets very expensive. It also becomes hard to understand and process the data a normal command returns. - -After about 1000 or 2000 nodes, it's best to to split the deployment into subcollectives and have commands default to a subset of machines, reserving whole-infrastructure commands for the cases where they're explicitly needed. You can also make commands targeting many machines safer by running them with `--batch SIZE`, which offers a chance to cancel out if you make a mistake. - diff --git a/website/deploy/install.md b/website/deploy/install.md deleted file mode 100644 index 8e769cd6..00000000 --- a/website/deploy/install.md +++ /dev/null @@ -1,190 +0,0 @@ ---- -title: "Install MCollective" -layout: default ---- - -[pe_orchestration]: {{pe}}/orchestration_overview.html -[pe_install]: {{pe}}/install_basic.html -[mco_tags]: https://github.com/puppetlabs/marionette-collective/tags -[git_repo]: https://github.com/puppetlabs/marionette-collective -[server_config_best_practices]: /mcollective/configure/server.html#best-practices -[init_lsb]: https://github.com/puppetlabs/marionette-collective/blob/master/mcollective.init -[init_debian]: https://github.com/puppetlabs/marionette-collective/blob/master/ext/debian/mcollective.init -[init_redhat]: https://github.com/puppetlabs/marionette-collective/blob/master/ext/redhat/mcollective.init -[standard]: ./standard.html -[middleware]: ./middleware/ -[activemq_connector]: /mcollective/reference/plugins/connector_activemq.html -[activemq_port_config]: /mcollective/deploy/middleware/activemq.html#transport-connectors -[config_server]: /mcollective/configure/server.html -[config_client]: /mcollective/configure/client.html -[deploy_plugins]: ./plugins.html -[enable_repos]: {{puppet}}/reference/puppet_collections.html -[server_libdir]: /mcollective/configure/server.html#platform-defaults -[client_libdir]: /mcollective/configure/client.html#platform-defaults -[deploy]: ./index.html -[semver]: http://semver.org -[puppet-agent]: {{puppet}}/reference/about_agent.html - -> **Note:** This page is about installing MCollective, which is part of the larger deployment process. See the [MCollective deployment index][deploy] for the complete picture. -> -> Puppet Enterprise includes MCollective and automates the deployment process. See its [orchestration documentation][pe_orchestration] for details about using MCollective to orchestrate your Puppet Enterprise infrastructure, and its [installation instructions][pe_install] for help installing PE. -> -> For the versions of Puppet agent components that ship with Puppet Enterprise, including the version of MCollective, see [What Gets Installed and Where]({{pe}}/install_what_and_where.html#agent-components-on-all-nodes). For the components shipped with open source Puppet, see [About Puppet Agent][puppet-agent]. - -To install MCollective: - -1. Install and start your middleware, and configure your firewalls. See the [pre-install instructions](#pre-install) for details. -2. Install the `puppet-agent` package on servers, and then make sure the `mcollective` service is running. -3. Install the `puppet-agent` package on admin workstations. - -Most Debian-like and Red Hat-like systems, as well as Windows and macOS, can [use the official `puppet-agent` package](#installing-with-the-official-packages) to install MCollective and other Puppet components and prerequisites. - -If your systems can't use the official package, [check the system requirements](#system-requirements) and either [build your own](#rolling-custom-rpm-and-debian-packages) or [run from source](#running-from-source). - -### Best practices - -Use site-wide configuration management software to install and configure MCollective. Since you'll need to install the server daemon on every node in your deployment, and since you'll want each node to be running the same version, you should generally use Puppet or something like it to install MCollective. - -### Semantic versioning - -All of our open source projects --- including Puppet, Puppet Server, PuppetDB, Facter, and Hiera --- use [semantic versioning ("semver")][semver] for their version numbers. This means that in an `x.y.z` version number, the "y" increases if new features are introduced and the "x" increases if existing features change or get removed. - -Our semver promises only refer to the code in a single project; it's possible for packaging or interactions with new "y" releases of other projects to cause new behavior in a "z" upgrade of Puppet. - -> **Historical note:** In Puppet versions prior to 3.0.0 and Facter versions prior to 1.7.0, we didn't use semantic versioning. - -## Pre-install - -1. Deploy your [middleware system][middleware] before installing MCollective. -2. Make sure each server's firewall allows MCollective to initiate connections with the middleware server. The port depends on your deployment plan; with the recommended [ActiveMQ connector][activemq_connector], this is usually either 61614 for Stomp/TLS (recommended) or 61613 for unencrypted Stomp, depending on [how you configured ActiveMQ's transport connectors][activemq_port_config]. - -{% capture postinstall %}[configure the server daemon][config_server], [configure admin workstations][config_client], and [deploy plugins][deploy_plugins]. See the [standard deployment getting started guide][standard] for details.{% endcapture %} - -([↑ Back to top](#content)) - -## System requirements - -MCollective can run on almost any \*nix operating system, as well as on Microsoft Windows. It requires Ruby 2.1 or later. - -MCollective also requires version **1.2.2 or higher** of the Stomp rubygem. - -## Installing with the official packages - -Puppet provides an official pre-built [`puppet-agent`][puppet-agent] for Windows, macOS, and the most common Linux-based operating systems. This package installs MCollective along with [Puppet]({{puppet}}), its tools, and its prerequisites. - -### Install MCollective - -Install the `puppet-agent` package using your operating system's package manager. For details, follow the [Puppet installation process]({{puppet}}/reference/install_pre.html). - -### Enable the MCollective service - -Ensure that the `mcollective` service is running and is enabled to start at boot. The `mcollective` package installs an init script that works with your system's service management tools. - -At this point, MCollective is installed and running, but can't connect to the middleware, accept commands, or execute actions. You should now {{ postinstall }} - -### Example - -As suggested [in the best practices](#best-practices) and [server configuration reference][server_config_best_practices], use configuration management software like Puppet to deploy MCollective on your nodes. This can be done with a simple or modified package/file/service pattern. - -The example below uses a modified pattern that assumes that you: - -- [Manage settings as resources][server_config_best_practices] in a different class. -- Maintain your own package repository with tested versions of the MCollective packages, allowing you to safely use `ensure => latest` to automatically update packages. -- Deployed all servers with this local repository enabled. -- Manage client workstations separately, and only automate deployment on your servers. - -``` ruby -class mcollective { - # Install - package {'mcollective': - ensure => latest, - } - - # Run - service {'mcollective': - ensure => running, - enable => true, - require => Package['mcollective'], - } - - # Restart the service when any settings change - Package['mcollective'] -> Mcollective::Setting <| |> ~> Service['mcollective'] -} -``` - -For details about the relationship and collector syntax used to restart the MCollective service on setting changes, see the [collector]({{puppet}}/reference/lang_collectors.html) and [chaining arrow]({{puppet}}/reference/lang_relationships.html#chaining-arrows) references. - -([↑ Back to top](#content)) - -## Rolling custom RPM and Debian packages - -If you use a system that doesn't have an official `puppet-agent` package, you can build your own `puppet-agent` package. For more information, see the [puppetlabs/puppet-agent repository on GitHub](https://github.com/puppetlabs/puppet-agent). - -([↑ Back to top](#content)) - -## Running from source - -In addition to using our packages or building your own, you can also build MCollective directly from source. - -### Obtain the source - -Get a copy of the MCollective source by cloning [the GitHub repo][git_repo] or [downloading a tarball][mco_tags]. - -### Install Ruby and the Stomp gem - -Install Ruby, and make sure that your system's Ruby version meets [MCollective's system requirements](#system-requirements). Also, install **version 1.2.2 or higher** of the `stomp` gem. - -### Add `mcollective/lib` to Ruby's load path - -Ruby must be able to load the contents of the `lib` directory in the MCollective source. There are two main ways to do this: - -- Recursively copy the contents of `lib` into the `site_ruby` directory -- Put the MCollective source somewhere like `/opt` and use the `RUBYLIB` environment variable to add it to Ruby's load path. - -### Copy `mcollective/plugins` - -MCollective ships with a set of plugins that it requires for basic functionality. These do not live in its normal lib path, but rather in an external directory specified by the `libdir` setting in MCollective's [server][server_libdir] and [client][client_libdir] configuration files. - -Copy the contents of this `plugins` directory to a platform-appropriate place, and **remember the location** for your post-install configuration because you need to specify it in the `libdir` setting. - -- Windows platforms put plugins in `C:\ProgramData\PuppetLabs\mcollective\plugins`. -- All other systems put plugins in `/opt/puppetlabs/mcollective/plugins`. - -Other platforms might use different conventions. - -> **Note:** MCollective expects its `libdir` to contain a single directory named `mcollective`, which then contains the rest of the plugin directories. Don't put your plugins in the _parent directory_ of the directory that MCollective checks. -> -> **Example:** -> -> # /etc/puppetlabs/mcollective/server.cfg -> # ... -> libdir = /opt/puppetlabs/mcollective/plugins -> -> - **Good:** `/opt/puppetlabs/mcollective/plugins/mcollective/agent/discovery.rb` -> - **Bad:** `/opt/puppetlabs/mcollective/plugins/agent/discovery.rb` - -### Add `mcollective/bin` to the path - -The root user on each server must be able to execute the `mcollectived` binary. Administrative users must be able to execute the `mco` binary. You should either link these to someplace like `/usr/local/bin` and `/usr/local/sbin`, or add the directory they live in to the appropriate users' `PATH` environment variable. - -> **Note:** If you're using Puppet Enterprise, only the `peadmin` user can run the `mco` command. For more information, see the [PE MCollective actions]({{pe}}/orchestration_invoke_cli.html) documentation. - -### Roll your own init script - -There are several example init scripts in the MCollective source: - -- [A basic LSB-compliant init script][init_lsb] -- [A Red Hat-like init script][init_redhat] -- [A Debian-like init script][init_debian] - -The `mcollective/ext` directory contains additional files that might help you install and configure the MCollective service on your platform. - -At this point, MCollective is installed and running but cannot connect to the middleware, accept commands, or execute any actions. You should now {{ postinstall }} - -You won't be able to count on the official package's sensible defaults for MCollective's configuration files, so set the logging and `libdir` settings after installation. - -### Installing from source on Windows - -We currently have no instructions for installing MCollective from source on Windows. You should investigate the `ext/windows` directory in the MCollective source. - -([↑ Back to top](#content)) diff --git a/website/deploy/middleware/activemq.md b/website/deploy/middleware/activemq.md deleted file mode 100644 index 31ccf6e7..00000000 --- a/website/deploy/middleware/activemq.md +++ /dev/null @@ -1,700 +0,0 @@ ---- -title: "MCollective » Deploy » Middleware » ActiveMQ Config" -subtitle: "ActiveMQ Config Reference for MCollective Users" -layout: default ---- - -[Wildcard]: http://activemq.apache.org/wildcards.html -[minimal_example]: http://github.com/puppetlabs/marionette-collective/raw/master/ext/activemq/examples/single-broker/activemq.xml -[maximal_example]: https://github.com/puppetlabs/marionette-collective/tree/master/ext/activemq/examples/multi-broker -[template_example]: TODO -[apache_activemq_config_docs]: http://activemq.apache.org/version-5-xml-configuration.html - -[subcollectives]: /mcollective/reference/basic/subcollectives.html -[activemq_connector]: /mcollective/reference/plugins/connector_activemq.html -[mcollective_connector_tls]: /mcollective/reference/integration/activemq_ssl.html - - -Summary ------ - -Apache ActiveMQ is the primary middleware we recommend with MCollective. It's good software, but its XML config file is large and unwieldy, and you may need to edit many sections of it in a complex MCollective deployment. This reference guide attempts to describe every major ActiveMQ setting that matters to MCollective. - -### How to Use This Page - -* This page **doesn't** describe the complete format of the activemq.xml config file, and will sometimes use incomplete shorthand to describe elements of it. -* You should definitely refer to an example config file while reading, so you can see each element's full syntax in context. -* You don't need to read this entire page when setting up a new deployment. We recommend that you: - * Start with an example config file (see directly below). - * Make heavy use of the table of contents above. - * Skim the sections of this page you currently care about, and edit your config as needed. - * Refer back to this page later when you need to expand your broker infrastructure. -* If you are a new user, we recommend that you: - * Start with the [single-broker example config][minimal_example]. - * Change the [user account passwords](#authentication-users-and-groups). - * [Set up TLS](#tls-credentials). - -### Example Config Files - -We have several. - -* [Minimal config example][minimal_example] --- single broker -* [Maximal config example][maximal_example] --- multi-broker - - - - -> **Note:** Some config data needs to be set in both MCollective and ActiveMQ; your configuration of one will affect the other. In this page, we call out that information with headers labeled "Shared Configuration." - -### Version Limits - -This document is about the "new" MCollective/ActiveMQ interface, which means it requires the following: - -* MCollective 2.0.0 or newer -* ActiveMQ 5.5.0 or newer -* Stomp gem 1.2.2 or newer -* The [activemq connector][activemq_connector] plugin (included with MCollective 2.0.0 and newer) - -([↑ Back to top](#content)) - -How MCollective Uses ActiveMQ ------ - -MCollective connects to ActiveMQ over the Stomp protocol, and presents certain credentials: - -* It provides a username and password, with which ActiveMQ can do what it pleases. -* If TLS is in use, it will also present a certificate (and verify the ActiveMQ server's certificate). - -Once allowed to connect, MCollective will use the Stomp protocol to create subscriptions. It will then produce and consume a lot of traffic on queues and topics whose names begin with `mcollective`. (See "Topic and Queue Names" directly below.) - -### Absolute Minimum Requirements - -ActiveMQ defaults to believing that it is routing traffic between processes in a single JVM instance: it doesn't assume it is connected to the network, and it uses a loose-to-nonexistent security model. - -This means that if you do nothing but enable Stomp traffic, MCollective will work fine. (Albeit with terrible security and eventual out-of-control memory usage.) - -### Topic and Queue Names - -MCollective uses the following destination names. This list uses standard [ActiveMQ destination wildcards][wildcard]. "COLLECTIVE" is the name of the collective being used; by default, this is `mcollective`, but if you are using [subcollectives][], each one is implemented as an equal peer of the default collective. - -Topics: - -- `ActiveMQ.Advisory.>` (built-in topics that all ActiveMQ producers and consumers need all permissions on) -- `COLLECTIVE.*.agent` (for each agent plugin, where the `*` is the name of the plugin) - -Queues: - -- `COLLECTIVE.nodes` (used for direct addressing; this is a single destination that uses JMS selectors, rather than a group of destinations) -- `COLLECTIVE.reply.>` (where the continued portion is a request ID) - -> #### Shared Configuration -> -> Subcollectives must also be configured in the MCollective client and server config files. ActiveMQ must allow traffic on any subcollective that MCollective servers and clients expect to use. - -([↑ Back to top](#content)) - -Config File Location and Format ------ - -ActiveMQ's config is usually called activemq.xml, and is kept in ActiveMQ's configuration directory (`/etc/activemq` with Puppet Labs's Red Hat-like packages, or a subdirectory of `/etc/activemq/instances-enabled` with the standard Debian or Ubuntu packages). Any other files referenced in activemq.xml will be looked for in the same directory. - -The config file is in Java's Beans XML format. Note that all of the settings relevant to MCollective are located inside activemq.xml's `` element. - -This document won't describe the complete format of the activemq.xml config file, and will sometimes use incomplete shorthand to describe elements of it. You should definitely [refer to an example config file](#example-config-files) while reading, so you can see each element's full syntax in context. - -You can also read external documentation for a more complete understanding. - -> **Bug Warning:** In ActiveMQ 5.5, the first-level children of the `` element must be arranged in alphabetical order. There is no good reason for this behavior, and it was fixed in ActiveMQ 5.6. - -### External ActiveMQ Documentation - -* [Apache ActiveMQ Documentation][apache_activemq_config_docs] - -### Wildcards - -You'll see a lot of [ActiveMQ destination wildcards][wildcard] below. In short: - -* Segments in a destination name are separated with dots (`.`) -* A `*` represents _one segment_ (i.e. any sequence of non-dot characters) -* A `>` represents _the whole rest of the name_ after a prefix - - -([↑ Back to top](#content)) - -Required Settings ------ - -One way or another, you must set all of the following. - -### Transport Connector(s) - -It's generally best to only enable the transport connectors you need. For example, if you're using Stomp over TLS, don't leave a bare Stomp transport open. If you're not using a network of brokers, close the OpenWire transport. - -The `name` attribute of a transport connector doesn't seem to matter as long as it's locally unique. - -#### Stomp - -ActiveMQ must listen over the network for Stomp connections; otherwise, MCollective can't reach it. Enable this with a `` element inside the `` element. We generally recommend using TLS. - -{% highlight xml %} - - - -{% endhighlight %} - -* Note that the protocol/port/arguments for Stomp URIs can differ: - * Unencrypted: `stomp+nio://0.0.0.0:61613` - * CA-verified TLS (ActiveMQ ≥ 5.9): `stomp+nio+ssl://0.0.0.0:61614?needClientAuth=true&transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2` - * CA-verified TLS: `stomp+ssl://0.0.0.0:61614?needClientAuth=true&transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2` - * Anonymous TLS (ActiveMQ ≥ 5.9): `stomp+nio+ssl://0.0.0.0:61614?needClientAuth=false&transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2` - * Anonymous TLS: `stomp+ssl://0.0.0.0:61614?transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2` -* You can choose to restrict the interface/hostname to use instead of listening on `0.0.0.0`. - - -> **If you are using TLS,** note that you must also: -> -> * [Configure ActiveMQ's TLS credentials](#tls-credentials) (see below) -> * [Configure MCollective to use TLS credentials][mcollective_connector_tls] - -#### OpenWire - -If you are using a network of brokers instead of just one ActiveMQ server, they talk to each other over OpenWire, and will all need a transport connector for that protocol too: - -{% highlight xml %} - -{% endhighlight %} - -* Note that the protocol/port/arguments for OpenWire URIs can differ: - * Unencrypted: `tcp://0.0.0.0:61616` - * CA-verified TLS: `ssl://0.0.0.0:61617?needClientAuth=true&transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2` - * Anonymous TLS: `ssl://0.0.0.0:61617&transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2` -* You can choose to restrict the interface/hostname to use instead of listening on `0.0.0.0`. - - - -#### Standard Ports for Stomp and OpenWire - -Alas, there aren't any; just a rough consensus. - -* 61613 for unencrypted Stomp -* 61614 for Stomp with TLS -* 61616 for unencrypted OpenWire -* 61617 for OpenWire with TLS - -All of our documentation assumes these ports. - -> #### Shared Configuration -> -> MCollective needs to know the following: -> -> * The port to use for Stomp traffic -> * The hostname or IP address to reach ActiveMQ at -> * Whether to use TLS -> -> In a network of brokers, the other ActiveMQ servers need to know the following: -> -> * The port to use for OpenWire traffic -> * The hostname or IP address to reach peer ActiveMQ servers at -> * Whether to use TLS - - -### Reply Queue Pruning - -MCollective sends replies on uniquely-named, single-use queues with names like `mcollective.reply.`. These have to be deleted after about five minutes, lest they clog up ActiveMQ's available memory. By default, queues live forever, so you have to configure this. - -Use a `` element for `*.reply.>` queues, with `gcInactiveDestinations` set to true and `inactiveTimoutBeforeGC` set to 300000 ms (five minutes). - -{% highlight xml %} - - - - - - - - -{% endhighlight %} - -### Disable Producer Flow Control on Topics - -In the example above, you can also see that `producerFlowControl` is set to false for all topics. This is highly recommended; setting it to true can cause MCollective servers to appear blocked when there's heavy traffic. - - -([↑ Back to top](#content)) - -Recommended Settings ------ - -### TLS Credentials - -If you are using TLS in either your Stomp or OpenWire [transport connectors](#transport-connectors), ActiveMQ needs a keystore file, a truststore file, and a password for each: - -{% highlight xml %} - - - -{% endhighlight %} - -**Note:** This example is for CA-verified TLS. If you are using anonymous TLS, you may optionally skip the truststore attributes. - -The redundant nested `` elements are not a typo; for some reason ActiveMQ actually needs this. - -ActiveMQ will expect to find these files in the same directory as activemq.xml. - -> #### Creating a Keystore and Truststore -> -> There is a [separate guide that covers how to create keystores.][activemq_keystores] - -[activemq_keystores]: ./activemq_keystores.html - - -### Authentication (Users and Groups) - -When they connect, MCollective clients and servers provide a username, password, and optionally an SSL certificate. ActiveMQ can use any of these to authenticate them. - -By default, ActiveMQ ignores all of these and has no particular concept of "users." Enabling authentication means ActiveMQ will only allow users with proper credentials to connect. It also gives you the option of setting up per-destination authorization (see below). - -You set up authentication by adding the appropriate element to the `` element. [See also the ActiveMQ docs about authentication][activemq_security]. In summary: - -- `simpleAuthenticationPlugin` defines users directly in activemq.xml. It's well-tested and easy. It also requires you to edit activemq.xml and restart the broker every time you add a new user. The activemq.xml file will contain sensitive passwords and must be protected. -- `jaasAuthenticationPlugin` lets you use external text files (or even an LDAP database) to define users and groups. You need to make a `login.config` file in the ActiveMQ config directory, and possibly two more files. You can add users and groups without restarting the broker. The external users file will contain sensitive passwords and must be protected. -- `jaasCertificateAuthenticationPlugin` ignores the username and password that MCollective presents; instead, it reads the distinguished name of the certificate and maps that to a username. It requires TLS, a `login.config` file, and two other external files. It is also impractical unless your servers are all using the same SSL certificate to connect to ActiveMQ; the currently recommended approach of re-using Puppet certificates makes this problematic, but you can probably ship credentials around and figure out a way to make it work. This is not currently well-tested with MCollective. - -[activemq_security]: http://activemq.apache.org/security.html - -The example below uses `simpleAuthenticationPlugin`. - -{% highlight xml %} - - - - - - - - - -{% endhighlight %} - -This creates two users: `mcollective` and `admin`. Note that unless you set up authorization (see below), these users have the exact same capabilities. - -> #### Shared Configuration -> -> MCollective servers and clients both need a username and password to use when connecting. That user **must** have appropriate permissions (see "Authorization," directly below) for that server or client's role. - - - -### Authorization (Group Permissions) - -By default, ActiveMQ allows everyone to **read** from any topic or queue, **write** to any topic or queue, and create (**admin**) any topic or queue. - -By setting rules in an `` element, you can regulate things a bit. Some notes: - -* Authorization is done **by group.** -* The exact behavior of authorization doesn't seem to be documented anywhere. Going by observation, it appears that ActiveMQ first tries the most specific rule available, then retreats to less specific rules. This means if a given group isn't allowed an action by a more specific rule but is allowed it by a more general rule, it still gets authorized to take that action. If you have any solid information about how this works, please email us at . -* MCollective creates subscriptions before it knows whether there will be any content coming. That means any role able to **read** from or **write** to a destination must also be able to **admin** that destination. Think of "admin" as a superset of both read and write. - -#### Simple Restrictions - -The following example grants all permissions on destinations beginning with `mcollective` to everyone in group `mcollective`: - -{% highlight xml %} - - - - - - - - - - - - - - - - -{% endhighlight %} - -This means admins can issue commands and MCollective servers can read those commands and reply. However, it also means that servers can issue commands. If you're using MCollective's [SSL security plugin][ssl_plugin] (or another security provider with strong client authentication), this is generally fine: servers could potentially _receive_ bogus commands, but will reject any commands that don't come from an authorized client user. - -[ssl_plugin]: /mcollective/reference/plugins/security_ssl.html - -Note that the `everyone` group (as seen in the `ActiveMQ.Advisory.>` topics) **isn't special.** You need to manually make sure all users are members of it. ActiveMQ does not appear to have any kind of real wildcard "everyone" group, unfortunately. - -#### Detailed Restrictions - -The following example splits permissions along a simple user/server model: - -{% highlight xml %} - - - - - - - - - - - - - - - - - - - - -{% endhighlight %} - -This means admins can issue commands and MCollective servers can read those commands and reply. This time, though, servers can't issue commands. The exception is the `mcollective.registration.agent` destination, which servers DO need the ability to write to if you've turned on registration. - -Admins, of course, can also read commands and reply as though they were servers, since they have power over the entire `mcollective.>` destination set. This isn't considered much of an additional security risk, considering that admins can already control your entire infrastructure. - -#### Detailed Restrictions with Multiple Subcollectives - -Both of the above examples assume only a single `mcollective` collective. If you are using additional [subcollectives][] (e.g. `uk_collective`, `us_collective`, etc.), their destinations will start with their name instead of `mcollective`. If you need to separately control authorization for each collective, it's best to use a template to do so, so you can avoid repeating yourself. - -{% highlight xml %} - - - - - - - - - - - <%- @collectives.each do |collective| -%> - - - - - - - <%- end -%> - - - - - - - -{% endhighlight %} - -This example divides your users into several groups: - -* `admins` is the "super-admins" group, who can command all servers. -* `servers` is the "super-servers" group, who can receive and respond to commands on any collective they believe themselves to be members of. -* `COLLECTIVE-admins` can only command servers on their specific collective. (Since all servers are probably members of the default `mcollective` collective, the `mcollective-admins` group are sort of the "almost-super-admins.") -* `COLLECTIVE-servers` can only receive and respond to commands on their specific collective. - -Thus, when you define your users in the [authentication setup](#authentication-users-and-groups), you could allow a certain user to command both the EU and UK collectives (but not the US collective) with `groups="eu_collective-admins,uk_collective-admins"`. You would probably want most servers to be "super-servers," since each server already gets to choose which collectives to ignore. - -#### MCollective's Exact Authorization Requirements - -As described above, any user able to read OR write on a destination must also be able to admin that destination. - -Topics: - -- `ActiveMQ.Advisory.>` --- Everyone must be able to read and write. -- `COLLECTIVE.*.agent` --- Admin users must be able to write. Servers must be able to read. -- `COLLECTIVE.registration.agent` --- If you're using registration, servers must be able to read and write. Otherwise, it can be ignored. - -Queues: - -- `COLLECTIVE.nodes` --- Admin users must be able to write. Servers must be able to read. -- `COLLECTIVE.reply.>` --- Servers must be able to write. Admin users must be able to read. - - -> #### Shared Configuration -> -> Subcollectives must also be configured in the MCollective client and server config files. If you're setting up authorization per-subcollective, ActiveMQ must allow traffic on any subcollective that MCollective servers and clients expect to use. - - -([↑ Back to top](#content)) - -Settings for Networks of Brokers ------ - -You can group multiple ActiveMQ servers into networks of brokers, and they can route local MCollective traffic amongst themselves. There are a lot of reasons to do this: - -* Scale --- we recommend a maximum of about 800 MCollective servers per ActiveMQ broker, and multiple brokers let you expand past this. -* High availability --- MCollective servers and clients can attempt to connect to mupltiple brokers in a failover pool. -* Partition resilience --- if an inter-datacenter link goes down, each half's local MCollective system will still work fine. -* Network isolation and traffic limiting --- if your clients default to only sending messages to local machines, you can get better performance in the most common case while still being able to command a global collective when you need to. -* Security --- destination filtering can prevent certain users from sending requests to certain datacenters. - -This is naturally more complicated than configuring a single broker. - -Designing your broker network's topology is beyond the scope of this reference. The [ActiveMQ Clusters guide](/mcollective/reference/integration/activemq_clusters.html) has a brief description of an example network; see [the ActiveMQ docs][NetworksOfBrokers] for more detailed info. For our purposes, we assume you have already decided: - -* Which ActiveMQ brokers can communicate with each other. -* What kinds of traffic should be excluded from other brokers. - -[NetworksOfBrokers]: http://activemq.apache.org/networks-of-brokers.html - -### Broker Name - -_Required._ - -The main `` element has a `brokerName` attribute. In single-broker deployments, this can be anything and defaults to `localhost`. In a network of brokers, each broker's name must be globally unique across the deployment; duplicates can cause message loops. - -{% highlight xml %} - -{% endhighlight %} - -### OpenWire Transports - -_Required._ - -All participants in a network of brokers need OpenWire network transports enabled. [See "Transport Connectors" above](#transport-connectors) for details. - -### Network Connectors - -_Required._ - -If you are using a network of brokers, you need to configure which brokers can talk to each other. - -This configuration can get complex! Here's why: - -* _This_ is where you define your brokers' network topology: in the sum total of _all_ of the `` elements across _all_ of your brokers. (This is massively shared configuration --- you can't determine the network topology by looking at the connectors on any single broker.) -* Each network connector connects two brokers. Think of a network connector as a run of cable between two routers. -* For reasons that are hard to summarize, every link between two brokers must consist of **two** network connectors: - * One reserved for **topics,** with `conduitSubscriptions` set to true (its default value) and queue traffic excluded. - * One reserved for **queues,** with `conduitSubscriptions` set to false and topic traffic excluded. - - It's basically as though you had one cable that could only carry UDP and another cable that could only carry TCP. We don't know why it's built like this, but it's easy to get tripped up on, so please watch out. -* Network connectors can be bidirectional (`duplex="true"`) or unidirectional (`duplex="false"`). Bidirectional is easier to deal with, since you only have to configure the connectors on **one** participant in each link, and the other participant will automatically configure itself. (If you used unidirectional connectors, you'd end up using FOUR network connectors per link, with two on each participant, due to the topic/queue split mentioned above.) - -As you can see, there are a lot of ways to do this, especially since this configuration is a direct reflection of your network topology. As an example, a simple star topology could be configured in the following ways: - -* The hub broker has two bidirectional connectors for _each_ leaf broker. None of the leaf brokers need any connectors configured. This centralizes the configuration, but the configuration is relatively complex --- you'd need to maintain a list of all leaf nodes. -* _Every_ leaf broker has two bidirectional connectors pointed toward the hub. This spreads configuration across many nodes, but the configuration is relatively simple, since each leaf broker just needs its own name and the URI of the hub. -* Every leaf broker has two unidirectional connectors pointed toward the hub, and the hub has two unidirectional connectors pointed at each leaf node. Worst of both worlds; we don't know of a good reason to do this. - -Network connectors are configured with `` elements inside the `` element. Note that the queues connector excludes topics and vice-versa. - -{% highlight xml %} - - - - - - - - - - - - -{% endhighlight %} - -Notes: - -* The `name` attribute on each connector **must** be globally unique. Easiest way to do that is to combine the pair of hostnames involved with the word "queues" or "topics." -* If you're using TLS for OpenWire, you'll need to change the URIs to something like `static:(ssl://stomp2.example.com:61617)` --- note the change of both protocol and port. -* The network TTL is **the number of network hops** that messages and subscriptions are allowed to pass through. You will need to adjust the TTL to match your network's topology. In a ring, it would be the number of brokers minus one; in a star, it would be two. -* A username and password are required. The broker with the `` connects to the other broker as this user. This user should have **full rights** on **all** queues and topics, unless you really know what you're doing. (See [authentication](#authentication-users-and-groups) and [authorization](#authorization-group-permissions) above.) -* Alternately, you can set up two uni-directional connectors on both brokers; see the ActiveMQ documentation linked above for more details. - - -### Destination Filtering - -_Optional._ - -If you want to prevent certain traffic from leaving a given datacenter, you can do so with `` or `` elements **inside each `` element.** This is mostly useful for reducing noise (which can potentially save you money, depending on network topology and datacenter locations), but it can also serve security purposes. Generally, you'll be filtering on [subcollectives][], which, as described above, begin their destination names with the name of the collective. - -Both types of filter element can contain `` and `` elements, with their `physicalName` attributes defining a destination name with the normal wildcards. Since MCollective uses both queues and topics, you'll usually write a pair of rules for any exclusion or inclusion. - -**Remember to retain the all-queues/all-topics exclusions as [shown above](#network-connectors).** - -#### Examples - -Assume a star network topology. - -This topology can be achieved by either having each edge broker connect to the central broker, or having the central broker connect to each edge broker. You can achieve the same filtering in both situations, but with slightly different configuration. The two examples below have similar but not identical effects; the ramifications are subtle, and we _really_ recommend reading the external ActiveMQ documentation if you've come this far in your deployment scale. - -If your central broker is connecting to the UK broker, and you want it to only pass on traffic for the global `mcollective` collective and the UK-specific `uk_collective` collective: - -{% highlight xml %} - - - - - - -{% endhighlight %} - -In this case, admin users connected to the central broker can command nodes on the `uk_collective`, but admin users connected to the UK broker can't command nodes on the `us_collective`, etc. - -Alternately, if your UK broker is connecting to your central broker and you want it to refrain from passing on UK-specific traffic that no one outside that datacenter cares about: - -{% highlight xml %} - - - - -{% endhighlight %} - -In this case, admin users connected to the central broker **cannot** command nodes on the `uk_collective`; it's expected that they'll be issuing commands to the main `mcollective` collective if they need to (and are authorized to) cross outside their borders. - -([↑ Back to top](#content)) - -Tuning ------ - -The minor adjustments listed below (turn off dedicated task runner, increase heap, and increase memory and temp usage in activemq.xml) will generally let you reach about 800 MCollective nodes connected to a single ActiveMQ server, depending on traffic and usage patterns, number of topics and queues, etc. Any more detailed tuning is beyond the scope of this reference, and is likely to be unnecessary for your deployment. - -### Don't Use Dedicated Task Runner - -Set `-Dorg.apache.activemq.UseDedicatedTaskRunner=false` when starting ActiveMQ. MCollective creates a lot of queues and topics, so _not_ using a thread per destination will save you a lot of memory usage. - -This setting is **not** configured in activemq.xml; it's an extra argument to the JVM, which should be provided when ActiveMQ starts up. The place to put this varies, depending on the package you installed ActiveMQ with; it usually goes in the wrapper config file. Check your init script for clues about this file's location. With the common TanukiWrapper scripts, it would look something like this: - - wrapper.java.additional.4=-Dorg.apache.activemq.UseDedicatedTaskRunner=false - -### Increase JVM Heap if Necessary - -Likewise, the max heap is usually configured in the wrapper config file (`wrapper.java.maxmemory=512`) or on the command line (`-Xmx512m`). - -### Memory and Temp Usage for Messages (`systemUsage`) - -Since ActiveMQ expects to be embedded in another JVM application, it won't automatically fill up the heap with messages; it has extra limitations on how much space to take up with message contents. - -As your deployment gets bigger, you may need to increase the `` and `` elements in the `` element. Unfortunately, we lack a lot of solid data for what to actually set these to. Most users leave the defaults for memory and temp until they have problems, then double the defaults and see whether their problems go away. This isn't perfectly effecient, but anecdotally it appears to work. - -The many redundant nested elements are not a typo; for some reason, ActiveMQ seems to require this. - -{% highlight xml %} - - - - - - - - - - - - - - -{% endhighlight %} - - -([↑ Back to top](#content)) - -Boilerplate ------ - -There's little reason to care about these settings in most conditions, but they're in the example config files anyway. - -### Persistence - -MCollective rarely uses this. It's only necessary in networks of brokers, where it is used to prevent routing loops. Leave it enabled; it has no notable performance penalty and its disk usage is limited by the `` element described above. - -{% highlight xml %} - - - -{% endhighlight %} - -### Management Context - -This is for monitoring. MCollective doesn't use this and the examples have it turned off, but you may want it for your own purposes. - -{% highlight xml %} - - - - - -{% endhighlight %} - -### Statistics Broker - -MCollective doesn't use this. - -{% highlight xml %} - - - - - -{% endhighlight %} - - -### Jetty (Web Consoles, APIs, etc.) - -The activemq.xml file will often either contain Jetty settings or import them from another file. MCollective doesn't use this. If you're not using it to manage ActiveMQ, leaving it enabled may be a security risk. Note that this configuration is **outside** the `` element. - -{% highlight xml %} - - -{% endhighlight %} - -([↑ Back to top](#content)) diff --git a/website/deploy/middleware/activemq_keystores.md b/website/deploy/middleware/activemq_keystores.md deleted file mode 100644 index bd9b3692..00000000 --- a/website/deploy/middleware/activemq_keystores.md +++ /dev/null @@ -1,273 +0,0 @@ ---- -title: "MCollective » Deploy » Middleware » ActiveMQ Keystores" -subtitle: "Setting Up Keystores For ActiveMQ" -layout: default ---- - -[tls]: ./activemq.html#tls-credentials - -Since ActiveMQ runs on the JVM, [configuring it to use TLS encryption/authentication][tls] requires a pair of Java keystores; it can't just use the normal PEM format certificates and keys used by Puppet and MCollective. - -Java keystores require some non-obvious steps to set up, so this guide provides full instructions, including both a [manual method](#manually-creating-keystores) and a [Puppet method](#creating-keystores-with-puppet). - - -## Step 0: Obtain Certificates and Keys - -ActiveMQ needs the following credentials: - -* A copy of the site's CA certificate -* A certificate signed by the site's CA -* A private key to match its certificate - -These can come from anywhere, but the CA has to match the one used by MCollective. - -The easiest approach is to re-use your site's Puppet cert infrastructure, since it's already everywhere and has tools for issuing and signing arbitrary certificates. - -As ever, remember to **protect the private key.** - -### Option A: Re-Use the Node's Puppet Agent Certificate - -On your ActiveMQ server: - -* Locate the ssldir by running `sudo puppet agent --configprint ssldir`. -* Copy the following files to a working directory, making sure to give unique names to the cert and private key: - * `/certs/ca.pem` - * `/certs/.pem` - * `/private_keys/.pem` - -### Option B: Get a New Certificate from the Puppet CA - -On your CA puppet master: - -* Run `sudo puppet cert generate activemq.example.com`, substituting some name for your ActiveMQ server. The certname must be the hostname that resolves to your ActiveMQ server. -* Locate the ssldir by running `sudo puppet master --configprint ssldir`. -* Retrieve the following files and copy them to a working directory on your ActiveMQ server, making sure to give unique names to the cert and private key: - * `/certs/ca.pem` - * `/certs/activemq.example.com.pem` - * `/private_keys/activemq.example.com.pem` - -### Option C: Do Whatever You Want - -If you have some other CA infrastructure, you can use that instead. - -You can now: - -* [Manually create the keystores](#manually-creating-keystores) -* [Use Puppet to create the keystores](#creating-keystores-with-puppet) - -## Manually Creating Keystores - -We need a **"truststore"** and a **"keystore."** We also need a **password** for each. (You can use the same password for both stores.) - -Remember the password(s) for later, because it needs to [go in the activemq.xml file][tls]. - -### Step 1: Truststore - -> **Note:** The truststore is only required for CA-verified TLS. If you are using anonymous TLS, you may skip it. - -The truststore determines which certificates are allowed to connect to ActiveMQ. If you import a CA cert into it, ActiveMQ will trust any certificate signed by that CA. - -> You could also _not_ import a CA, and instead import every individual certificate you want to allow. If you do that, you're on your own, but the commands should be similar. - -In the working directory where you copied your PEM-format credentials, run the following command. Replace `ca.pem` with whatever you named your copy of the CA cert, and use the password when requested. - -{% highlight console %} -$ sudo keytool -import -alias "My CA" -file ca.pem -keystore truststore.jks -Enter keystore password: -Re-enter new password: -. -. -. -Trust this certificate? [no]: y -Certificate was added to keystore -{% endhighlight %} - -The truststore is now done. - -If you want, you can compare fingerprints: - -{% highlight console %} -$ sudo keytool -list -keystore truststore.jks -Enter keystore password: - -Keystore type: JKS -Keystore provider: SUN - -Your keystore contains 1 entry - -my ca, Mar 30, 2012, trustedCertEntry, -Certificate fingerprint (MD5): 99:D3:28:6B:37:13:7A:A2:B8:73:75:4A:31:78:0B:68 - -$ sudo openssl x509 -in ca.pem -fingerprint -md5 -MD5 Fingerprint=99:D3:28:6B:37:13:7A:A2:B8:73:75:4A:31:78:0B:68 -{% endhighlight %} - - -### Step 2: Keystore - -The keystore contains the ActiveMQ broker's certificate and private key, which it uses to identify itself to the applications that connect to it. - -In the working directory where you copied your PEM-format credentials, run the following commands. Substitute the names of your key and certificate files where necessary, and the common name of your ActiveMQ server's certificate for `activemq.example.com`. - -> **Note about passwords:** These commands refer to an "export" password, a "source" password, and a "destination" password. **All of these passwords must be the same.** - -{% highlight console %} -$ sudo cat activemq_private.pem activemq_cert.pem > temp.pem -$ sudo openssl pkcs12 -export -in temp.pem -out activemq.p12 -name activemq.example.com -Enter Export Password: -Verifying - Enter Export Password: -$sudo keytool -importkeystore -destkeystore keystore.jks -srckeystore activemq.p12 -srcstoretype PKCS12 -alias activemq.example.com -Enter destination keystore password: -Re-enter new password: -Enter source keystore password: -{% endhighlight %} - -The keystore is now done. - -If you want, you can compare fingerprints: - -{% highlight console %} -$ sudo keytool -list -keystore keystore.jks -Enter keystore password: - -Keystore type: JKS -Keystore provider: SUN - -Your keystore contains 1 entry - -activemq.example.com, Mar 30, 2012, PrivateKeyEntry, -Certificate fingerprint (MD5): 7E:2A:B4:4D:1E:6D:D1:70:A9:E7:20:0D:9D:41:F3:B9 - -$ sudo openssl x509 -in activemq_cert.pem -fingerprint -md5 -MD5 Fingerprint=7E:2A:B4:4D:1E:6D:D1:70:A9:E7:20:0D:9D:41:F3:B9 -{% endhighlight %} - -### Step 3: Finish - -Move the keystore and truststore to ActiveMQ's config directory. Make sure they are owned by the ActiveMQ user and unreadable to any other users. [Configure ActiveMQ to use them in its `sslContext`.][tls] **Double-check** that you've made activemq.xml world-unreadable, since it now contains sensitive credentials. - -## Creating Keystores with Puppet - -If you're managing your ActiveMQ server with Puppet anyway, you can use the [puppetlabs/java_ks module](http://forge.puppetlabs.com/puppetlabs/java_ks) to handle the format conversion. - -This approach is more work for a single permanent ActiveMQ server, but less work if you intend to deploy multiple ActiveMQ servers or eventually change the credentials. - -### Step 1: Install the `java_ks` Module - -On your puppet master, run `sudo puppet module install puppetlabs/java_ks`. - -### Step 2: Create a Puppet Class - -The class to manage the keystores should do the following: - -* Make sure the PEM cert and key files are present and protected. -* Declare a pair of `java_ks` resources. -* Manage the mode and ownership of the keystore files. - -The code below is an example, but it will work fine if you put it in a module (example file location in the first comment) and set its parameters when you declare it. The name of the class (and its home module) can be changed as needed. - - -{% highlight ruby %} - # /etc/puppet/modules/activemq/manifests/keystores.pp - class activemq::keystores ( - $keystore_password, # required - - # User must put these files in the module, or provide other URLs - $ca = 'puppet:///modules/activemq/ca.pem', - $cert = 'puppet:///modules/activemq/cert.pem', - $private_key = 'puppet:///modules/activemq/private_key.pem', - - $activemq_confdir = '/etc/activemq', - $activemq_user = 'activemq', - ) { - - # ----- Restart ActiveMQ if the SSL credentials ever change ----- - # ----- Uncomment if you are fully managing ActiveMQ with Puppet. ----- - - # Package['activemq'] -> Class[$title] - # Java_ks['activemq_cert:keystore'] ~> Service['activemq'] - # Java_ks['activemq_ca:truststore'] ~> Service['activemq'] - - - # ----- Manage PEM files ----- - - File { - owner => root, - group => root, - mode => 0600, - } - file {"${activemq_confdir}/ssl_credentials": - ensure => directory, - mode => 0700, - } - file {"${activemq_confdir}/ssl_credentials/activemq_certificate.pem": - ensure => file, - source => $cert, - } - file {"${activemq_confdir}/ssl_credentials/activemq_private.pem": - ensure => file, - source => $private_key, - } - file {"${activemq_confdir}/ssl_credentials/ca.pem": - ensure => file, - source => $ca, - } - - - # ----- Manage Keystore Contents ----- - - # Each keystore should have a dependency on the PEM files it relies on. - - # Truststore with copy of CA cert - java_ks { 'activemq_ca:truststore': - ensure => latest, - certificate => "${activemq_confdir}/ssl_credentials/ca.pem", - target => "${activemq_confdir}/truststore.jks", - password => $keystore_password, - trustcacerts => true, - require => File["${activemq_confdir}/ssl_credentials/ca.pem"], - } - - # Keystore with ActiveMQ cert and private key - java_ks { 'activemq_cert:keystore': - ensure => latest, - certificate => "${activemq_confdir}/ssl_credentials/activemq_certificate.pem", - private_key => "${activemq_confdir}/ssl_credentials/activemq_private.pem", - target => "${activemq_confdir}/keystore.jks", - password => $keystore_password, - require => [ - File["${activemq_confdir}/ssl_credentials/activemq_private.pem"], - File["${activemq_confdir}/ssl_credentials/activemq_certificate.pem"] - ], - } - - - # ----- Manage Keystore Files ----- - - # Permissions only. - # No ensure, source, or content. - - file {"${activemq_confdir}/keystore.jks": - owner => $activemq_user, - group => $activemq_user, - mode => 0600, - require => Java_ks['activemq_cert:keystore'], - } - file {"${activemq_confdir}/truststore.jks": - owner => $activemq_user, - group => $activemq_user, - mode => 0600, - require => Java_ks['activemq_ca:truststore'], - } - - } -{% endhighlight %} - - -### Step 3: Assign the Class to the ActiveMQ Server - -...using your standard Puppet site tools. - -### Step 4: Finish - -[Configure ActiveMQ to use the keystores in its `sslContext`][tls], probably with the Puppet template you're using to manage activemq.xml. **Double-check** that you've made activemq.xml world-unreadable, since it now contains sensitive credentials. diff --git a/website/deploy/middleware/index.md b/website/deploy/middleware/index.md deleted file mode 100644 index 4c910e7d..00000000 --- a/website/deploy/middleware/index.md +++ /dev/null @@ -1,76 +0,0 @@ ---- -title: "MCollective » Deploy » Middleware" -subtitle: "MCollective: Middleware Options" -layout: default ---- - -[redis_security]: http://redis.io/topics/security -[deploy]: ../index.html -[standard]: ../standard.html -[activemq]: http://activemq.apache.org/ -[activemq_config]: /mcollective/deploy/middleware/activemq.html -[activemq_example_links]: /mcollective/deploy/middleware/activemq.html#example-config-files -[server_config]: /mcollective/configure/server.html -[client_config]: /mcollective/configure/client.html -[activemq_connector]: /mcollective/reference/plugins/connector_activemq.html -[rabbitmq]: http://www.rabbitmq.com/ -[rabbitmq_connector]: /mcollective/reference/plugins/connector_rabbitmq.html -[redis]: http://redis.io/ -[security_overview]: /mcollective/security.html -[redis_connector]: https://github.com/ripienaar/mc-plugins/tree/master/connector/redis -[redis_comments]: https://github.com/ripienaar/mc-plugins/blob/master/connector/redis/redis.rb -[stomp_connector]: /mcollective/reference/plugins/connector_stomp.html -[overview]: /mcollective/overview_components.html - -Summary ------ - -MCollective needs a publish/subscribe middleware system of some kind for all communications. When deploying MCollective, you need to: - -* Pick a **middleware type** -* Get a **connector plugin** that supports it (note that ActiveMQ and RabbitMQ plugins are already included with MCollective's core install) -* **Deploy and configure** the middleware server(s) -* **Configure the connector plugin** on all MCollective servers and clients - -> **Note:** Configuring the middleware and connector is only one step of a many-step deployment process. See [the deployment index][deploy] for a map of our deployment documentation, [the overview of components and configuration][overview] for a summary of the components and roles in an MCollective deployment, and [the standard deployment getting started guide][standard] for a walkthrough of the deployment process. - -MCollective supports the following middleware systems: - - -ActiveMQ ------ - -[Apache ActiveMQ][activemq] is an open-source message broker that runs on the JVM; typically it's installed with a wrapper script and init script that allow it to be managed as a normal OS service. MCollective talks to ActiveMQ using the Stomp protocol. - -**This is the main middleware recommended for use with MCollective:** it performs extremely well, it's the most well-tested option, its security features are powerful and flexible enough to suit nearly all needs, and it can scale by clustering once a deployment gets too big (we recommend ~800 nodes per ActiveMQ server as a maximum). Its main drawback is that it can be frustrating to configure; to help mitigate that, we provide a detailed ActiveMQ config reference in our own docs (see below). - -The ActiveMQ connector ships with MCollective's core and is available by default. - -* See [the MCollective ActiveMQ Config Reference][activemq_config] for information about configuring ActiveMQ to suit MCollective's needs. The best way to use this page is to grab an example config file (see the [links near the top of the reference][activemq_example_links]) and change settings as needed. -* See the [Server Config Reference][server_config] and [Client Config Reference][client_config] for general information about configuring connector plugins. -* See the [ActiveMQ Connector Plugin Reference][activemq_connector] for detailed info on the ActiveMQ connector's settings. - -RabbitMQ ------ - -[RabbitMQ][] is an open-source message broker written in Erlang; MCollective talks to RabbitMQ using the Stomp protocol. Although it works well with MCollective, it isn't as thoroughly tested with it as ActiveMQ is, so if your site has no preference, you should default to ActiveMQ. - -The RabbitMQ connector ships with MCollective's core and is available by default. - -* We do not provide information on deploying and configuring RabbitMQ itself. -* See the [Server Config Reference][server_config] and [Client Config Reference][client_config] for general information about configuring connector plugins. -* See the [RabbitMQ Connector Plugin Reference][rabbitmq_connector] for detailed info on the RabbitMQ connector's settings. - - -Generic Stomp Connector (Deprecated) ------ - -MCollective 2.2 and earlier include a generic Stomp connector, which was the predecessor of the ActiveMQ and RabbitMQ connectors. Its performance and capabilities are fairly outdated at this point, and it was deprecated during the 2.2 series and removed in the 2.3 series. Use the ActiveMQ or RabbitMQ connector instead. - -For older versions where the Stomp connector is still necessary, see the archived [Stomp Connector Plugin Reference][stomp_connector] for config details. - -Custom Connector Plugins ------ - -Creating custom connector plugins is not currently documented, but it's very possible. We suggest reading the code of both the ActiveMQ connector and the Redis connector, to get decent parallax on how to accomplish similar tasks with very different systems. - diff --git a/website/deploy/plugins.md b/website/deploy/plugins.md deleted file mode 100644 index 5ba81832..00000000 --- a/website/deploy/plugins.md +++ /dev/null @@ -1,369 +0,0 @@ ---- -layout: default -title: "MCollective » Deploy » Installing Plugins" -Subtitle: "Installing and Packaging MCollective Plugins" ---- - -[puppet_agent]: https://github.com/puppetlabs/mcollective-puppet-agent -[server_config]: /mcollective/configure/server.html -[client_config]: /mcollective/configure/client.html -[servers]: /mcollective/overview_components.html#servers -[clients]: /mcollective/overview_components.html#clients -[puppetlabs_repos]: /guides/puppetlabs_package_repositories.html -[libdir_onpage]: #copying-plugins-into-the-libdir -[makepackages_onpage]: #packaging-custom-plugins -[verify_onpage]: #verifying-installed-agent-plugins -[types_onpage]: #about-plugins--available-plugin-types -[choria]: http://choria.io/ - -Summary ------ - -MCollective has several kinds of plugins (most notably **agents** and **applications**), all of which go in the directory specified by the `libdir` setting from the [server][server_config] and [client][client_config] config files. - -Pre-built packages are no longer provided in [the Puppet Labs apt and yum repos][puppetlabs_repos]. Installation via Puppet modules is preferred, and can be facilitated by [the Choria project][choria]. Puppet Enterprise comes with a predefined set of plugins. - -Not all plugins can be installed with those methods, so alternatively you can: - -* [Put files directly into the libdir][libdir_onpage] - -You may also want to: - -* [Create packages from your own plugins][makepackages_onpage] - - -### About Plugins / Available Plugin Types - -Most of what MCollective actually does is handled via a plugin. Each kind of plugin may need to be installed on [servers][], [clients][], or both. - -Type | Which systems | Function ----------------|-----------------------------|------------------------------------------------------------------------- -agent | servers, clients (DDL only) | Executing actions -aggregate | clients | Formatting results -application | clients | CLI subcommands (for sending requests, etc.) -audit | servers | Logging actions -connector | servers, clients | Interfacing with middleware -data | servers, clients (DDL only) | Enabling new kinds of metadata for filtering -discovery | clients | Acquiring a list of nodes that match a filter -facts | servers | Enabling fact metadata for filtering -pluginpackager | clients | Building OS-appropriate packages to install other plugins -registration | servers | Sending heartbeat and metadata to some form of inventory database -security | servers, clients | Serializing and validating messages -util | servers, clients | Various, usually supporting other plugins; includes authorization plugins -validator | servers, clients | Validating input formats before sending a request - -This may seem like a lot to manage, but the general experience is: - -* Most users write custom agent plugins. - * Validator and aggregate plugins can be useful when writing advanced agents. -* Many users write _some_ application plugins, but most agents don't need anything beyond the built-in `rpc` application. -* Custom discovery and data plugins can useful if you need faster or more versatile discovery, but aren't necessary if the existing discovery features suit you. -* It's rare to write or install custom plugins of the other types. - - -Copying Plugins Into the Libdir ------ - -For older agent plugins, certain non-agent plugins (such as authorization), and most custom plugins, you will need to install by copying files directly into MCollective's `libdir`. - -### About the Libdir - -All MCollective plugins belong in the `libdir`, which is specified by the setting of the same name in the [client][client_config] and [server][server_config] config files. - -The default libdir created by the MCollective install process varies per platform: - -Platform | Default libdir -------------------|--------------------------------- -Windows | `C:\ProgramData\Puppetlabs\mcollective\plugins` -Other | `/opt/puppetlabs/mcollective/plugins` - -The libdir is arranged like a standard Ruby lib directory: It always contains a single directory called `mcollective`, which contains a directory for each [type of plugin (see above)][types_onpage]; each of these directories contains any number of `.rb` and `.ddl` (and sometimes `.erb`) files. - -In the general case, this means plugin files should be named `/mcollective//.(rb|ddl|erb)`. The `util` directory may include some extra directory levels. - -As an example, here are the platform-appropriate destinations for two of the files installed by the `package` agent: - -* On Windows systems: - * `C:\ProgramData\Puppetlabs\mcollective\plugins\mcollective\agent\package.rb` - * `C:\ProgramData\Puppetlabs\mcollective\plugins\mcollective\agent\package.ddl` -* On other systems: - * `/opt/puppetlabs/mcollective/plugins/mcollective/agent/package.rb` - * `/opt/puppetlabs/mcollective/plugins/mcollective/agent/package.ddl` - -> **Note:** The `mcollective` directory goes _inside_ the libdir, whatever the libdir's path is. In the Red Hat case, this means the complete path contains the string `mcollective/mcollective`; be careful not to accidentally skip the second `mcollective`. - -### Copying From Source - -Most public MCollective plugins are developed and published in a source repository (e.g. on GitHub) that mimics the structure of the `/mcollective` directory --- that is, the repo will have an `agent` directory, an `application` directory, etc. You can ignore any Rakefiles, READMEs, or `spec` directories. - -If you come across a repository that has `lib` at the top level you -will need to treat lib/mcollective as the repository root and then -continue with the following instructions. - -> **Note:** Servers and clients will not need every file for a given _plugin set_ --- you can consult [the plugin types table above][types_onpage] to see which parts should go where. -> -> Alternately, you can just copy everything in the plugin's repo to every client and server --- nothing bad will happen from installing unused components. - -1. **Copy** every file in the plugin source (or just the server-appropriate files) to the corresponding location in the libdir of each [server][servers] node that should have it. E.g. `agent/package.rb` should be copied to `/mcollective/agent/package.rb`, etc. - * Since this must happen across a large number of servers, you should generally use Puppet or other configuration management to copy the files. -2. **Restart** the `mcollective` server daemon on every server. -3. **Copy** every file in the plugin source (or just the client-appropriate files) to the corresponding location in each [client][clients] system's libdir. - * Since client systems are often admin or developer workstations, this may or may not be automatable; at the least, you should maintain a list of which plugins are in use at your site, so that your admins can keep their clients up to date. -4. Optionally, [**verify** the installation][verify_onpage] on some proportion of your clients and servers. - - -### Example - -To demonstrate, this is what you would do to install the [puppet agent plugin][puppet_agent] on a collection of Red Hat-like servers and clients. - -The [repository][puppet_agent] for the puppet plugin set is laid out as follows: - - ├── CHANGELOG.md - ├── README.md - ├── Rakefile - ├── agent - │   ├── puppet.ddl - │   └── puppet.rb - ├── aggregate - │   ├── boolean_summary.ddl - │   └── boolean_summary.rb - ├── application - │   └── puppet.rb - ├── data - │   ├── puppet_data.ddl - │   ├── puppet_data.rb - │   ├── resource_data.ddl - │   └── resource_data.rb - ├── spec - │   ├── agent - │   │   └── puppet_agent_spec.rb - │   ├── aggregate - │   │   └── boolean_summary_spec.rb - │   ├── application - │   │   └── puppet_spec.rb - │   ├── data - │   │   ├── puppet_data_spec.rb - │   │   └── resource_data_spec.rb - │   ├── fixtures - │   │   └── last_run_summary.yaml - │   ├── spec.opts - │   ├── spec_helper.rb - │   ├── util - │   │   ├── puppet_agent_mgr - │   │   │   └── common_spec.rb - │   │   ├── puppet_agent_mgr_spec.rb - │   │   ├── puppetrunner_spec.rb - │   │   ├── v2 - │   │   │   ├── manager_spec.rb - │   │   │   └── unix_spec.rb - │   │   └── v3 - │   │   ├── manager_spec.rb - │   │   └── unix_spec.rb - │   └── validator - │   ├── puppet_resource_validator_spec.rb - │   ├── puppet_server_address_validator_spec.rb - │   ├── puppet_tags_validator_spec.rb - │   └── puppet_variable_validator_spec.rb - ├── util - │   ├── puppet_agent_mgr - │   │   ├── common.rb - │   │   ├── v2 - │   │   │   ├── manager.rb - │   │   │   ├── unix.rb - │   │   │   └── windows.rb - │   │   └── v3 - │   │   ├── manager.rb - │   │   ├── unix.rb - │   │   └── windows.rb - │   ├── puppet_agent_mgr.rb - │   └── puppetrunner.rb - └── validator - ├── puppet_resource_validator.ddl - ├── puppet_resource_validator.rb - ├── puppet_server_address_validator.ddl - ├── puppet_server_address_validator.rb - ├── puppet_tags_validator.ddl - ├── puppet_tags_validator.rb - ├── puppet_variable_validator.ddl - └── puppet_variable_validator.rb - -On Red Hat-like OSes, you would install these files in the following locations: - -#### Servers - - /opt/puppetlabs/mcollective/plugins/mcollective/agent/puppet.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/agent/puppet.rb - /opt/puppetlabs/mcollective/plugins/mcollective/data/puppet_data.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/data/puppet_data.rb - /opt/puppetlabs/mcollective/plugins/mcollective/data/resource_data.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/data/resource_data.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/common.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v2/manager.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v2/unix.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v2/windows.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v3/manager.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v3/unix.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v3/windows.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppetrunner.rb - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_resource_validator.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_resource_validator.rb - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_server_address_validator.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_server_address_validator.rb - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_tags_validator.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_tags_validator.rb - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_variable_validator.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_variable_validator.rb - - -#### Clients - - /opt/puppetlabs/mcollective/plugins/mcollective/agent/puppet.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/aggregate/boolean_summary.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/aggregate/boolean_summary.rb - /opt/puppetlabs/mcollective/plugins/mcollective/application/puppet.rb - /opt/puppetlabs/mcollective/plugins/mcollective/data/puppet_data.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/data/resource_data.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/common.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v2/manager.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v2/unix.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v2/windows.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v3/manager.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v3/unix.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr/v3/windows.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppet_agent_mgr.rb - /opt/puppetlabs/mcollective/plugins/mcollective/util/puppetrunner.rb - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_resource_validator.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_resource_validator.rb - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_server_address_validator.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_server_address_validator.rb - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_tags_validator.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_tags_validator.rb - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_variable_validator.ddl - /opt/puppetlabs/mcollective/plugins/mcollective/validator/puppet_variable_validator.rb - - -Packaging Custom Plugins ------ - -You can use the `mco plugin package` command to generate OS-appropriate packages (limited to Red Hat and Debian-like systems) for your own agent plugins. - -[The Choria project][choria] can also be used with `mco plugin package` to generate Puppet modules for plugins. - -Note that the packaging tool uses each system's native tools to build packages --- it doesn't necessarily support building, for example, .deb and .rpm packages on a Mac. - - -Verifying Installed Agent Plugins ------ - -To verify that an **agent plugin** is correctly installed you can do the following. (Verifying other types of plugins can be harder; we suggest looking in the debug logs) - -### Check List of Agents - -The `mco inventory ` command includes a list of the agents installed on the node: - -{% highlight console %} -$ mco inventory some.node -Inventory for some.node: - - Server Statistics: - Version: 0.4.10 - Start Time: Mon Nov 29 16:38:28 +0000 2010 - Config File: /etc/mcollective/server.cfg - Process ID: 5387 - Total Messages: 10196 - Messages Passed Filters: 7108 - Messages Filtered: 3088 - Replies Sent: 7107 - Total Processor Time: 25.95 seconds - System Time: 7.0 seconds - - Agents: - cassandrabackup discovery echo - filemgr nrpe package - process puppetd rpchelper - rpctest rpcutil service -{% endhighlight %} - -### Check Agent Version Info - -You can also use the `agent_inventory` action of the `rpcutil` agent to see more complete info for each agent: - - -{% highlight console %} -$ mco rpc rpcutil agent_inventory -I some.node -Determining the amount of hosts matching filter for 2 seconds .... 1 - - * [ ============================================================> ] 1 / 1 - - -some.node: - Agents: - [{:url=>"https://docs.puppetlabs.com/mcollective/", - :version=>"1.0", - :name=>"Utilities and Helpers for SimpleRPC Agents", - :agent=>"rpcutil", - :description=> "General helpful actions that expose stats and internals to SimpleRPC clients", - :author=>"R.I.Pienaar ", - :timeout=>3, - :license=>"Apache License, Version 2.0"}] - -Finished processing 1 / 1 hosts in 100.70 ms -{% endhighlight %} - -### Check Agent Docs on Client - -On the client, if you installed a DDL file, you can look up the help for an agent: - -{% highlight console %} -$ mco plugin doc rpcutil -Utilities and Helpers for SimpleRPC Agents -========================================== - -General helpful actions that expose stats and internals to SimpleRPC clients - - Author: R.I.Pienaar - Version: 1.0 - License: Apache License, Version 2.0 - Timeout: 3 - Home Page: https://docs.puppetlabs.com/mcollective/ - - - -ACTIONS: -======== - agent_inventory, daemon_stats, get_config_item, get_fact, inventory - - agent_inventory action: - ----------------------- - Inventory of all agents on the server - - INPUT: - - OUTPUT: - agents: - Description: List of agents on the server - Display As: Agents -{% endhighlight %} - -### Examine Debug Output in Log File - -[log_settings]: /mcollective/configure/server.html#logging - -If you start the server daemon with a `loglevel` setting of `debug`, the log (usually the file specified by [the `logfile` setting][log_settings]) should contain a bunch of lines like: - - D, [2010-11-30T21:33:33.144290 #5753] DEBUG -- : 5753 pluginmanager.rb:83:in `loadclass': Loading MCollective::Agent::Service from mcollective/agent/service.rb - D, [2010-11-30T21:33:33.144786 #5753] DEBUG -- : 5753 pluginmanager.rb:36:in `<<': Registering plugin service_agent with class MCollective::Agent::Service - D, [2010-11-30T21:33:33.144928 #5753] DEBUG -- : 5753 stomp.rb:150:in `subscribe': Subscribing to /topic/mcollective.service.command - - -### Check Presence of Agent Across All Nodes - -Finally, since agents are a common filter criteria, you can find all nodes that are running your agent: - -{% highlight console %} -$ mco find -A someagent -host1.example.com -host2.example.com -host3.example.com -{% endhighlight %} diff --git a/website/deploy/standard.md b/website/deploy/standard.md deleted file mode 100644 index ff7bf4c0..00000000 --- a/website/deploy/standard.md +++ /dev/null @@ -1,544 +0,0 @@ ---- -title: "MCollective » Deploy » Standard Deployment" -subtitle: "Getting Started: Standard MCollective Deployment" -layout: default ---- - -[inpage_step_one]: #step-1-create-and-collect-credentials -[vagrant_demo]: ./demo.html -[overview_roles]: /mcollective/overview_components.html -[security_overview]: /mcollective/security.html -[activemq_clustering]: /mcollective/reference/integration/activemq_clusters.html -[subcollectives]: /mcollective/reference/basic/subcollectives.html -[activemq_filters]: ./middleware/activemq.html#destination-filtering -[puppet]: /puppet/ -[overview_use_puppet]: ./index.html#best-practices -[puppetlabs_repos]: /guides/puppetlabs_package_repositories.html -[activemq_install]: http://activemq.apache.org/getting-started.html -[activemq_config]: ./middleware/activemq.html -[activemq_example]: https://raw.github.com/puppetlabs/marionette-collective/master/ext/activemq/examples/single-broker/activemq.xml -[certdir]: /references/latest/configuration.html#certdir -[privatekeydir]: /references/latest/configuration.html#privatekeydir -[ssldir]: /references/latest/configuration.html#ssldir -[activemq_authorization]: ./middleware/activemq.html#authorization-group-permissions -[install_mcollective]: ./install.html -[puppet_template]: /guides/templating.html -[puppet_lang_notification]: /puppet/latest/reference/lang_relationships.html#ordering-and-notification -[file]: /references/latest/type.html#file -[actionpolicy_docs]: /mcollective/plugin_directory/authorization_action_policy.html -[actionpolicy]: https://github.com/puppetlabs/mcollective-actionpolicy-auth -[server_config]: /mcollective/configure/server.html -[client_config]: /mcollective/configure/client.html -[javaks]: http://forge.puppetlabs.com/puppetlabs/java_ks -[choria]: http://choria.io/ - - -Summary ------ - -This getting started guide will help you deploy a standard MCollective environment. [Jump to the first step][inpage_step_one], or keep reading for some context. - -> **Note:** If you've never used MCollective before, [start with the Vagrant-based demo toolkit instead][vagrant_demo]. This guide is meant for production-grade deployments, and requires a bit of building before you can do a simple `mco ping` command. - -> ### Puppet Enterprise -> -> Puppet Enterprise includes MCollective, and automates the entire deployment process. See [the PE MCollective documentation][pe_orchestration] for more details; see [the PE installation instructions][pe_install] to install PE. - -> ### Choria -> -> [The Choria project][choria] provides an alternative standard deployment with simplified setup tools. - -[pe_orchestration]: /pe/latest/mco_overview.html -[pe_install]: /pe/latest/install_basic.html - -### What is an MCollective Deployment? - -[See the MCollective components overview][overview_roles] for an introduction to the parts that make up an MCollective deployment. - -### Why "Standard?" - -MCollective is very pluggable, but the developers and community have settled on some common conventions for the most important configuration options. These defaults work very well for most new users, and create a good foundation for future expansion and reconfiguration. - -The Standard MCollective Deployment ------ - -### Architecture and Configuration - -In summary, these are the architecture and configuration conventions that make up the standard MCollective deployment: - -* Middleware and connector: **ActiveMQ** - * One ActiveMQ server (expandable later) - * CA-verified TLS - * No extra subcollectives - * One ActiveMQ user account/password for all MCollective traffic -* Security plugin: **SSL** (authentication only) -* Credentials: - * Certificate/key pair for each client user (for both connector and security plugins) - * One shared certificate/key pair for all servers (for security plugin) - * Pre-existing Puppet certificate/key pair on each server (for connector plugin) - * Alternately, you can use the shared server certificate/key for both the connector and security plugins; unique certificates aren't strictly necessary. -* Authorization: **ActionPolicy plugin** - -### Security Model - -* [See here for a deeper explanation of MCollective's various layers of security.][security_overview] - -In brief, this is what MCollective's security model looks like with these conventions in place: - -#### Transport Level - -* The TLS between MCollective servers/clients and the ActiveMQ server **encrypts traffic,** preventing passive sniffing of sensitive data in requests and replies. -* Since the TLS is CA-verified, it also **prevents man-in-the-middle attacks** targeting the middleware. -* Requiring both a password and a signed certificate to connect to the middleware helps **prevent unauthorized access to decrypted text.** The password is easier to change, the certificate is harder to steal, and together they offer reasonable security. (For these credentials to be secure, we expect that you've made it reasonably difficult for attackers to gain root on your systems.) -* The middleware connection does not identify clients; this happens at the application level. - -#### Application Level - -With the SSL security plugin, each client user has a unique key pair and all servers share a single key pair. Each server node holds a collection of all authorized client public keys. - -* When clients issue requests, they sign the payload and TTL with their private key; servers do the same when they send replies. This **strongly identifies individual clients** and **identifies servers as a group.** (An authorized server could theoretically impersonate another authorized server; in the common use cases for MCollective, this isn't a significant concern.) -* Servers will reject requests signed by any key that isn't in their collection of authorized clients. This acts as **coarse-grained client authorization.** (Note especially that the shared server key pair cannot be used to send requests, which is an advantage over the weaker PSK security plugin.) -* The ActionPolicy plugin allows **fine-grained client authorization** at the per-action level. (This relies on the client authentication provided by the SSL security plugin.) -* Servers also check the signature of the request payload and TTL, which **protects against message tampering and replay attacks.** - -#### Summary - -These measures focus mainly on strict control over who can command your infrastructure and protection of sensitive information in transit. They assume that authorized servers and clients are both sufficiently trusted to view all sensitive information passing through the middleware. - -This is suitable for most use cases. If some authorized servers are untrustworthy, there are opportunities for them to send misleading replies and bogus traffic, but they can't command other nodes. - - -### Future Expansion - -Later, you may need to [expand to a cluster of ActiveMQ servers][activemq_clustering]; at that point, you might also: - -* [Divide your nodes into subcollectives][subcollectives] -* [Filter traffic between datacenters][activemq_filters] -* [Do per-user ActiveMQ authorization][activemq_authorization] - -If you have already used modular Puppet code to set up a standard deployment, these changes can be incremental instead of a complete overhaul. - - -([↑ Back to top](#content)) - - -Steps to Deploy ------ - -You need to do the following to deploy MCollective: - -1. Create and collect credentials -2. Deploy and configure middleware -3. Install MCollective (on both servers and admin workstations) -4. Configure servers -5. Configure clients -6. Deploy plugins - -This process isn't 100% linear, but that's the general order in which these tasks should be approached. - -### Best Practices - -**Use [Puppet][] or some other form of configuration management to deploy MCollective.** See [the deployment overview][overview_use_puppet] for why this is important. - -We don't currently have drop-in Puppet code for a standard MCollective deployment, so you'll have to do some building. - - -([↑ Back to top](#content)) - - -[step1]: #step-1-create-and-collect-credentials - -## Step 1: Create and Collect Credentials - -Credentials are the biggest area of shared global configuration in MCollective. Get them sorted before doing much else. - -A standard deployment uses the following credentials: - -Credential | Used By: --------------------------------------------------------|------------------------------------------------- -ActiveMQ username/password | Middleware, servers, clients -CA certificate | Middleware, servers, clients -Signed certificate and private key for **ActiveMQ** | Middleware -Signed certificate and private key for each **server** | Servers -Signed certificate and private key for each **user** | Clients (both parts), servers (certificate only) -Shared server public and private key | Servers (both parts), clients (public key only) - - -### What Are These? - -* ActiveMQ ↔ MCollective traffic uses CA-signed X.509 certificates for encryption and verification. These are just like the certs Puppet uses. - * Unlike Puppet, we aren't using the certificate DN/CN for authentication --- the CA verification is solely to make man-in-the-middle attacks more difficult. -* The SSL security plugin (on servers and clients) uses RSA public/private key pairs (or anything else readable by openssl) to do authentication, coarse-grained authorization, and message signing. The public portion is flexible: it can be either a raw RSA key, or a signed SSL certificate. The plugin identifies keys by filename. -* If you're using Puppet, you can re-use its certificate authority, and use it to sign certificates. -* On MCollective nodes, all credentials should be in .pem format; on the middleware, some extra conversion is needed. - -### Walkthrough / Checklist - -Make sure you've covered each of the following credentials, and keep track of the credentials for use in future steps. This guide assumes you're using Puppet as your certificate authority. If you aren't, you'll need to generate each credential some other way. - -> **Directories:** Below, we refer to directories called [`$certdir`][certdir] and [`$privatekeydir`][privatekeydir] --- these are defined by Puppet settings of the same names. Their locations may vary by platform, so you can locate them with `sudo puppet agent --configprint certdir,privatekeydir` (on an agent node) or `sudo puppet master --configprint certdir,privatekeydir` (on the CA master). - -* **PASSWORD:** _Do:_ Decide on a username for connecting to ActiveMQ; we suggest `mcollective`. Create a strong arbitrary password for this user. -* **CA:** _Already done:_ Every node already has a local copy of the Puppet CA; you can use it directly. It's always located at `$certdir/ca.pem`. -* **ACTIVEMQ CERT:** _Decide:_ You can either re-use the ActiveMQ server's existing puppet agent certificate, or generate a new certificate on the CA puppet master with `sudo puppet cert generate activemq.example.com` (this name cannot conflict with an existing certificate name). In either case, find the certificate and private key at `$certdir/.pem` and `$privatekeydir/.pem`. -* **SHARED SERVER KEYS:** _Do:_ On the CA puppet master, generate a new certificate with `sudo puppet cert generate mcollective-servers`. (If you use a different name, substitute it for "mcollective-servers" everywhere we mention it below. Note that the name can only use letters, numbers, periods, and hyphens.) Retrieve the certificate and private key from `$certdir/mcollective-servers.pem` and `$privatekeydir/mcollective-servers.pem`. -* **SERVER CERTS:** _Already done:_ Every server node already has its own puppet agent certificate. You can re-use it. The certificate and key are located at `$certdir/.pem` and `$privatekeydir/.pem`. -* **CLIENT CERTS:** _Do:_ You will need to continually create client credentials as you add new admin users. - * For the first admin user --- yourself --- you can generate a certificate on the CA puppet master with `sudo puppet cert generate ` (letters, numbers, periods, and hyphens only) and retrieve the cert and key from `$certdir/.pem` and `$privatekeydir/.pem`; delete the CA's copy of the private key once you've retrieved it. - * For future admin users, you need to build a process for issuing and distributing credentials. [See "Managing Client Credentials" below][client_creds] for notes about this. - -> **Deployment status:** Nothing has happened yet. - - -([↑ Back to top](#content)) - - -## Step 2: Deploy and Configure Middleware - -As ever, note that you'll have an easier time later if you perform these steps with Puppet or something like it. We suggest using a template for the activemq.xml file and using the [`java_ks`][javaks] resource type for the keystores. - -1. Install ActiveMQ 5.5 or higher on your ActiveMQ server. If you are using Fedora or a relative of Red Hat Enterprise Linux, enable [the Puppet Labs package repos][puppetlabs_repos] and install the `activemq` package. The most recent versions of Debian and Ubuntu have ActiveMQ packages, and you may be able to install `activemq` without enabling any extra repos. For other systems, [adapt the instructions from the ActiveMQ documentation][activemq_install], or roll your own packages. -2. Locate the activemq.xml file. Replace it with the [example config file][activemq_example] from the MCollective source. You will be editing this example activemq.xml file to suit your deployment in the next four steps. -3. [Change the passwords][activemq_passwords] for the admin user and the `mcollective` user. For the MCollective user, **use the password from the list of credentials above.** -4. [Change the port and protocol][activemq_transports] on the stomp transport connector to `stomp+nio+ssl://0.0.0.0:61614?needClientAuth=true&transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2`, since we'll be using CA-verified TLS. (If you are running ActiveMQ before 5.9.x, set it to `stomp+ssl://0.0.0.0:61614?needClientAuth=true&transport.enabledProtocols=TLSv1,TLSv1.1,TLSv1.2` instead; the stomp+nio+ssl protocol have had several bugs in earlier releases.) -5. Follow the [ActiveMQ keystores guide][activemq_keystores], using **the CA certificate and the ActiveMQ certificate/key.** (See list of credentials above.) -6. [Write an `sslContext` element][activemq_sslcontext] in the activemq.xml file to use the keystores you created. (If you are using ActiveMQ 5.5, make sure you are arranging elements alphabetically to work around the XML validation bug.) -7. Start or restart the ActiveMQ service. -8. Ensure that the server's firewall allows inbound traffic on port 61614. - -[activemq_keystores]: ./middleware/activemq_keystores.html -[activemq_sslcontext]: ./middleware/activemq.html#tls-credentials -[activemq_transports]: ./middleware/activemq.html#transport-connectors -[activemq_passwords]: ./middleware/activemq.html#authentication-users-and-groups - -For more details about configuring ActiveMQ, see the [ActiveMQ config reference for MCollective users][activemq_config]. It's fairly exhaustive, and is mostly for users doing things like networks of brokers and traffic filtering; for a standard deployment, you just need to change the passwords and configure TLS. - -> **Deployment status:** The middleware is fully ready, but nothing is using it yet. - - -([↑ Back to top](#content)) - - -## Step 3: Install MCollective - -[See the "Install MCollective" page for complete instructions.][install_mcollective] In summary: - -* Install the `mcollective` package on your server nodes. -* Install the `mcollective-client` package on your admin workstations. -* If you don't have official packages for your OS, you may need to [run from source][from_source]. -* Make sure you install the same version everywhere. - -[from_source]: ./install.html#running-from-source - -> **Deployment status:** MCollective is installed, but isn't ready to do anything at this point. The `mcollective` service will probably refuse to start since it lacks a connector and security plugin. - - -([↑ Back to top](#content)) - - -## Step 4: Configure Servers - -To configure servers, you'll need to: - -* Locate and place the credentials -* Populate the fact file -* Write the server config file, with appropriate settings - -### Locate and Place Credentials - -[As mentioned above in Step 1][step1], servers need the CA, an individual certificate and key, the shared server keypair, and every authorized client certificate. - -* If you're using Puppet, the CA, individual cert, and individual key are already present. -* Put a copy of the shared public key at `/etc/puppetlabs/mcollective/server_public.pem`. -* Put a copy of the shared private key at `/etc/puppetlabs/mcollective/server_private.pem`. -* Create a `/etc/puppetlabs/mcollective/clients` directory and put a copy of every client certificate in it. You will need to maintain this directory centrally, and keep it up to date on every server as you add and delete admin users. (E.g. as a [file resource][file] with `ensure => directory, recurse => true`.) - -### Populate the Fact File - -Every MCollective server will need to populate the `/etc/puppetlabs/mcollective/facts.yaml` file with a cache of its facts. (You can get by without this file, but doing so will limit your ability to filter requests.) - -Make sure you include a resource like the following in the Puppet code you're using to deploy MCollective: - -{% highlight ruby %} - file{"/etc/puppetlabs/mcollective/facts.yaml": - owner => root, - group => root, - mode => 400, - loglevel => debug, # reduce noise in Puppet reports - content => inline_template("<%= scope.to_hash.reject { |k,v| k.to_s =~ /(uptime_seconds|timestamp|free)/ }.to_yaml %>"), # exclude rapidly changing facts - } -{% endhighlight %} - -### Write the Server Config File - -The server config file is located at `/etc/puppetlabs/mcollective/server.cfg`. - -[as_resources]: /mcollective/configure/server.html#best-practices - -> See the [server configuration reference][server_config] for complete details about the server's config file, including its format and available settings. - -This config file has many settings that should be identical across the deployment, and several settings that must be unique per server, which is why we suggest managing it with Puppet. If your site uses only a few agent plugins and they don't require a lot of configuration, you can use a [template][puppet_template]; otherwise, we recommend [managing each setting as a resource.][as_resources] - -**Be sure to always restart the `mcollective` service after editing the config file.** In your Puppet code, you can do this with a [notification relationship][puppet_lang_notification]. - -#### Server Settings for a Standard Deployment - -This example template snippet shows the settings you need to use in a standard deployment. Converting it to [settings-as-resources][as_resources] would be fairly straightforward. - -(Note that it assumes an [`$ssldir`][ssldir] of `/etc/puppetlabs/puppet/ssl`, which might differ in your Puppet setup. This template also requires variables named `$activemq_server` and `$activemq_mcollective_password`.) - - {% highlight erb %} - <% ssldir = '/etc/puppetlabs/puppet/ssl' %> - # /etc/puppetlabs/mcollective/server.cfg - - # ActiveMQ connector settings: - connector = activemq - direct_addressing = 1 - plugin.activemq.pool.size = 1 - plugin.activemq.pool.1.host = <%= @activemq_server %> - plugin.activemq.pool.1.port = 61614 - plugin.activemq.pool.1.user = mcollective - plugin.activemq.pool.1.password = <%= @activemq_mcollective_password %> - plugin.activemq.pool.1.ssl = 1 - plugin.activemq.pool.1.ssl.ca = <%= ssldir %>/certs/ca.pem - plugin.activemq.pool.1.ssl.cert = <%= ssldir %>/certs/<%= scope.lookupvar('::clientcert') %>.pem - plugin.activemq.pool.1.ssl.key = <%= ssldir %>/private_keys/<%= scope.lookupvar('::clientcert') %>.pem - plugin.activemq.pool.1.ssl.fallback = 0 - - # SSL security plugin settings: - securityprovider = ssl - plugin.ssl_client_cert_dir = /etc/puppetlabs/mcollective/ssl/clients - plugin.ssl_server_private = /etc/puppetlabs/mcollective/ssl/server_private.pem - plugin.ssl_server_public = /etc/puppetlabs/mcollective/ssl/server_public.pem - - # Facts, identity, and classes: - identity = <%= scope.lookupvar('::fqdn') %> - factsource = yaml - plugin.yaml = /etc/puppetlabs/mcollective/facts.yaml - classesfile = /opt/puppetlabs/puppet/cache/state/classes.txt - - # No additional subcollectives: - collectives = mcollective - main_collective = mcollective - - # Registration: - # We don't configure a listener, and only send these messages to keep the - # Stomp connection alive. This will use the default "agentlist" registration - # plugin. - registerinterval = 600 - - # Auditing (optional): - # If you turn this on, you must arrange to rotate the log file it creates. - rpcaudit = 1 - rpcauditprovider = logfile - plugin.rpcaudit.logfile = /var/log/puppetlabs/mcollective-audit.log - - # Authorization: - # If you turn this on now, you won't be able to issue most MCollective - # commands, although `mco ping` will work. You should deploy the - # ActionPolicy plugin before uncommenting this; see "Deploy Plugins" below. - - # rpcauthorization = 1 - # rpcauthprovider = action_policy - # plugin.actionpolicy.allow_unconfigured = 1 - - # Logging: - logger_type = file - loglevel = info - logfile = /var/log/puppetlabs/mcollective/mcollective.log - keeplogs = 5 - max_log_size = 2097152 - logfacility = user - - # Platform defaults: - # These settings differ based on platform; the default config file created by - # the package should include correct values. If you are managing settings as - # resources, you can ignore them, but with a template you'll have to account - # for the differences. - <% if scope.lookupvar('::osfamily') == 'RedHat' -%> - libdir = /usr/libexec/mcollective - daemonize = 1 - <% elsif scope.lookupvar('::osfamily') == 'Debian' -%> - libdir = /usr/share/mcollective/plugins - daemonize = 1 - <% else -%> - # INSERT PLATFORM-APPROPRIATE VALUES FOR LIBDIR AND DAEMONIZE - <% end %> -{% endhighlight %} - -> **Deployment status:** The servers are ready, connected to the middleware, and will accept and process requests from authorized clients. The authorized clients don't exist yet. - - -([↑ Back to top](#content)) - - -## Step 5: Configure Clients - -Unlike servers, clients will probably run with per-user configs on admin workstations, and will have to be configured partially by hand. (If you are running any automated clients, you'll want to deploy those with config management; most of the principles covered below will still apply.) - -To configure clients, each new admin user will need to: - -* Request, retrieve, and place their credentials -* Write the client config file, with appropriate sitewide and per-user settings - -Unless the client will be run by root or a system user, we recommend putting the client config file at `~/.mcollective` and supporting files like credentials in `~/.mcollective.d`. - -### Managing Client Credentials - -[client_creds]: #managing-client-credentials - -For your first admin user, you can manually generate a certificate (as suggested in Step 1) and add it to the authorized clients directory that you're syncing to servers with Puppet. However, this does not scale beyond one or two users. - -When a new admin user joins your team, you need a documented process that does ALL of the following: - -- Issues the user a signed SSL certificate, while assuring the user that no one else has _ever_ had custody of their private key. -- Adds a copy of the user's certificate to every MCollective server. -- Gives the user a copy of the shared server public key, the CA cert, and the ActiveMQ username/password. - -> **Note:** The filename of the **public key** must be identical on both the client and the servers. The client uses the filename to set the caller ID in its requests, and the servers use the request's caller ID to choose which public key file to validate it with. - -This will have to be at least partially manual, but if you've used the Puppet CA to issue certificates, you can pretty easily patch together and document a process using the existing Puppet tools. - -Below, we outline a suggested process. It assumes a flat hierarchy of admins where everyone can command all servers, with any additional restrictions being handled by the ActionPolicy plugin (see "Step 6: Deploy Plugins" below) rather than the certificate distribution process. - -#### Example Client Onboarding Process - -1. The new user should have Puppet installed on their workstation (or the server from which they will be issuing mco commands). It does not need to be managing their workstation, it just needs to be present. -2. The new user should run the following commands on their workstation --- note that the name can only use letters, numbers, periods, and hyphens: - - $ mkdir -p ~/.mcollective.d/credentials - $ puppet certificate generate --ssldir ~/.mcollective.d/credentials --ca-location remote --ca_server - - (Note the use of the `puppet certificate` command, which isn't the same thing as the `puppet cert` command. This specific invocation will send a certificate signing request to the CA while safeguarding the private key.) -3. The new user should tell the MCollective admins which name they used, and optionally the fingerprint of the CSR they submitted. -4. The MCollective admins should run `sudo puppet cert sign ` on the CA puppet master, then copy the certificate from `$certdir/.pem` into the directory of authorized client keys that is being synced to the MCollective servers; each server will recognize the new user after its next Puppet run. -5. The MCollective admins should tell the new user that they have signed the certificate request, and give them the ActiveMQ password and a partially filled-out client config file containing the relevant hostnames and ports. (See "Write the Client Config File" below.) -6. The new user should run the following commands on their workstation: - - $ puppet certificate find --ssldir ~/.mcollective.d/credentials --ca-location remote --ca_server - $ puppet certificate find mcollective-servers --ssldir ~/.mcollective.d/credentials --ca-location remote --ca_server - $ puppet certificate find ca --ssldir ~/.mcollective.d/credentials --ca-location remote --ca_server - -7. The new user should copy the partial client config file they were provided to `~/.mcollective` on their workstation, and finish filling it out as described below. - -After all these steps, and following a Puppet run on each MCollective server, the new user should be able to issue valid mco commands. - - -### Write the Client Config File - -For admin users running commands on a workstation, the client config file is located at `~/.mcollective`. For system users (e.g. for use in automated scripts), it is located at `/etc/puppetlabs/mcollective/client.cfg`. - -> See the [client configuration reference][client_config] for complete details about the client config file, including its format and available settings. - -This config file has many settings that should be identical across the deployment, and several settings that must be unique per user. To save your new users time, we recommend giving them a partial config file with settings like the ActiveMQ hostname/port/password already entered; this way, they only have to fill in the paths to their unique credentials. The settings that must be modified by each user are: - -* `plugin.activemq.pool.1.ssl.ca` -* `plugin.activemq.pool.1.ssl.cert` -* `plugin.activemq.pool.1.ssl.key` -* `plugin.ssl_server_public` -* `plugin.ssl_client_private` -* `plugin.ssl_client_public` - -#### Client Settings for a Standard Deployment - -After receiving this partial config file, a new user should fill out the credential paths, substituting `` for the fully qualified path to their home directory and `` for the name of the certificate they requested. (Note that MCollective cannot expand shorthand paths to the home directory --- `~/.mcollective.d/credentials...` --- so you must use fully qualified paths.) - - # ~/.mcollective - # or - # /etc/puppetlabs/mcollective/client.cfg - - # ActiveMQ connector settings: - connector = activemq - direct_addressing = 1 - plugin.activemq.pool.size = 1 - plugin.activemq.pool.1.host = - plugin.activemq.pool.1.port = 61614 - plugin.activemq.pool.1.user = mcollective - plugin.activemq.pool.1.password = - plugin.activemq.pool.1.ssl = 1 - plugin.activemq.pool.1.ssl.ca = /.mcollective.d/credentials/certs/ca.pem - plugin.activemq.pool.1.ssl.cert = /.mcollective.d/credentials/certs/.pem - plugin.activemq.pool.1.ssl.key = /.mcollective.d/credentials/private_keys/.pem - plugin.activemq.pool.1.ssl.fallback = 0 - - # SSL security plugin settings: - securityprovider = ssl - plugin.ssl_server_public = /.mcollective.d/credentials/certs/mcollective-servers.pem - plugin.ssl_client_private = /.mcollective.d/credentials/private_keys/.pem - plugin.ssl_client_public = /.mcollective.d/credentials/certs/.pem - - # Interface settings: - default_discovery_method = mc - direct_addressing_threshold = 10 - ttl = 60 - color = 1 - rpclimitmethod = first - - # No additional subcollectives: - collectives = mcollective - main_collective = mcollective - - # Platform defaults: - # These settings differ based on platform; the default config file created - # by the package should include correct values or omit the setting if the - # default value is fine. - libdir = /opt/puppetlabs/mcollective/plugins - helptemplatedir = /etc/puppetlabs/mcollective - - # Logging: - logger_type = console - loglevel = warn - - -> **Deployment status:** MCollective is **fully functional.** Any configured admin user can run `mco ping` to discover nodes, use the `mco inventory` command to search for more detailed information, and use the `mco rpc` command to trigger actions from installed agents (currently only the `rpcutil` agent). See the [mco command-line interface documentation][cli_docs] for more detailed information on filtering and addressing commands. -> -> However, it can't yet do much other than collect inventory info. To perform more useful functions, you must install agent plugins on each server and admin workstation. Additionally, if you want to do per-action authorization for certain dangerous commands, you will need to install and configure the ActionPolicy plugin. - - -([↑ Back to top](#content)) - - -## Step 6: Deploy plugins - -To let MCollective do anything beyond retrieving inventory data, you must deploy various plugins to all of your server and client nodes. You will usually also want to write custom agent plugins to serve business purposes in your infrastructure. - -### Install ActionPolicy - -For a long-lived standard deployment, we recommend that you deploy the ActionPolicy authorization plugin to all servers. - -By default, the standard deployment allows **all** authorized clients to execute **all** actions on **all** servers. This is reasonable as long as MCollective's capabilities are limited, but as you hire more admin staff and deploy agent plugins that can cause significant changes to production servers, you may wish to begin limiting who can execute what. ActionPolicy allows you to distribute policy files for specific agents, which will restrict the set of users able to run a given action. - -1. [Download the ActionPolicy plugin at its GitHub repo.][actionpolicy] -2. Install it on all **servers** using the [libdir copy install method][plugin_libdir]. -3. Uncomment the `rpcauthorization`, `rpcauthprovider`, and `plugin.actionpolicy.allow_unconfigured` settings in the server config file. -4. As needed, write per-agent policy files and distribute them to servers. [See the ActionPolicy documentation for details on how to write policies.][actionpolicy_docs] - -#### Notes - -* The SSL security plugin sets the message caller ID to `cert=`, where `` is the filename of the client's public key file without the `.pem` extension. This string (including the `cert=`) can be used as the second field of a policy line. (The ActionPolicy documentation uses `uid=` for its examples, which is a caller ID set by the PSK security plugin.) -* With the configuration [shown above in the server config file](#server-settings-for-a-standard-deployment), ActionPolicy is opt-in **per agent.** If you don't distribute any policy files, MCollective will continue to work as before, with no additional authorization. -* Since policy files define which servers their rules apply to (based on facts and other metadata), they can safely be distributed to all servers. - -### Install Agent Plugins - -Agent plugins do all of MCollective's heavy lifting. All parts of an agent need to be installed on servers, and the DDL file needs to be installed on clients. - -You can: - -* Install any number of pre-existing agents. See [the mcollective-plugins wiki][mcollective_plugins_wiki] for a list of the most common ones. You will probably want to install at least the `puppet`, `package`, and `service` agents. -* [Develop your own agent plugins][agent_writing] to serve site-specific purposes. These can help with application deployments, automate routine tasks like retrying mail queues, collect complex inventory information in real time, and more. [See the documentation on writing agent plugins for more info.][agent_writing] - -For more information on how to install these agents, [see "Installing and Packaging MCollective Plugins."](./plugins.html). - -[agent_writing]: /mcollective/simplerpc/agents.html -[mcollective_plugins_wiki]: https://docs.puppetlabs.com/mcollective/plugin_directory/ - -### Learn MCollective's Command Line Interface - -* [See here for general info on using MCollective's `mco` CLI client.][cli_docs] -* [See here for additional info about filtering requests.][filter_docs] - -[cli_docs]: /mcollective/reference/basic/basic_cli_usage.html -[filter_docs]: /mcollective/reference/ui/filters.html - -> **Deployment status:** MCollective can do anything you've written or downloaded plugins for, on any number of servers, filtered and grouped by arbitrary metadata. diff --git a/website/ec2demo.md b/website/ec2demo.md deleted file mode 100644 index 23aa6a3a..00000000 --- a/website/ec2demo.md +++ /dev/null @@ -1,11 +0,0 @@ ---- -layout: default -title: EC2 Demo -toc: false ---- - - -The EC2 MCollective demo has been deprecated in favor of a better Vagrant-based demo. - -* [About the Vagrant demo](/mcollective/deploy/demo.html) -* [Download the Vagrant demo](https://github.com/ripienaar/mcollective-vagrant) diff --git a/website/images/activemq-multi-locations.png b/website/images/activemq-multi-locations.png deleted file mode 100644 index 56f700492313de4cf0fc12bcaa79177152426578..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 77356 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 diff --git a/website/images/client.gif b/website/images/client.gif deleted file mode 100644 index a914fd7b974e5d83cb048a2545475a8393adb659..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 92800 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 z ze+@e5T!0EKr{H$UT_@3n55lD(hmA$pp@a`s2%=y4P2|oyxPSsvd6X$)*(1fE<=H#$ ziDuM%PBcXhV*}O4)E*117Rnxb9CC|W*g&%+G!rQk${)s%!{m}_RYN5?1`S!{kx5c% zQ8Q2)Lz@u<(qJHh)@?`~iB-Fkn$T*y zEmej}XYIGlKI`nb(;;LcNBX$Kj46q<7!xL##1si3%v~xUTu==)6-d>DacWalq(;!M z|xt0?d@a1r>boP@}+AkFia)AceAO_Q)>Fp$ay0lB46qOl{Gr zK)(tS6)E!U(bN_O-OROwGc+gXchmbbDPx4jIzxh>b*DM zfeTKv;J<}Mcw2}IcW!%wHunyr@8ZOaJo@NWW73!|4c8|Vd$9*B12Z&8RA?OXhZj-t zn>E^98xxi28l6ppG6Rv`lRT57PPJ@k}VR6jFo9c{0-%wS%)+g1xZaig@z!K z1S62XXh1)KGy`k{iC6_uC1w!jpMR6QL;Rx&WGKiRvP8)RL-qhs{Y9RlkPQ7`D9WaHdBp~G?_2m^=G27;J|DUuk76PW_VtVr>JRm@U%-jR!A5v2u? z*dX>kxI&VNFpd$zVYl!&80^f^dNS-|;vn?Lya7^ql`~&Q=0T$%!lx)iToXAYp|B*< zpm+mWpT-Ijil&T;ibHY26%W{yAdqBzqQj&?Hp!Gu%Hvm{#E>eEg~2y6Cyt0LWS_iK zDC@wmgU{0=F8QX*V3tgf!&DCRT*gOY%A;}0QKUu~c_WBSeGgEfao<+5hG@xoV8-k6jGNo+q@R%1etJ%$F zHVh-ZMMKv2l}T1IE12ahYCSto&|=3TbC~}rIOzJZJDVjothHQ-~Ta*W7&-AQcv34Pg=4kAK$Kg+RhMlzI|G6?S;C9R?_3hM1KGN#JNb zNup$W8pO#^mNI%r{0@G^z*g|NLOuWH!$BMS#;9yQA&zYrXT9Eb&Tqc+VF@)@=q03k z&hfESA3LA*Iju=afdy&L6b^yB&ORhD1{=Z5l<_Kr7!#Y~qoWdvM@Razgi=X6Iwm|f zC^oMH#(at8ROI2OX>RBJALp1BoSOD$V=f0yFi9YKV!tm>15oF6WaKarV z(X=XHZOU>Pb`{cMY-B4tD$Mru;WT0mQD0CGrM~4+=?G&0&6n1Pm389Oyk~Q>dxYR# z+;<~Cn=!%uRH4Mcq6YEI*ZJg#b`-}K`!o8E51;`FIw?>IQS)8j@`;CcsI<;THl z)!G0Xxu{}xKvEUX8zO}&%F!%k2iTBrK52ozBNScn*rt!p$BciA>1**j)%E(*bqBui z|0O(tKMpi<5eh-r;Sj1YM(j}53{@OL5UQ9Ux$nFA&VRl_e?v&AB90`z z>)G(L-TL3l?fP10%eb_kJ?@p45N+U?xQVJBf31@8$3LArA6#Gy2-IHC&*n{h*N*et zBKtu8C9k=^y+Lq>H~s(i6xD9N{!f9Lb(B?fD3C?`Us{h_#>0Lib}&`)hq8gMv=;nz zEv)f@U;CT|lV-}LzWo7L0N$7$1=Tyafe@Kr@D;{|JYOD31V6Mv#Hj#0+|2;;l`09%aY@Cg8oH-?>E;L9_=ygaLsB#Zg#b2)>$xJPT5NV2t5fL;T+i zE?p1WR{brMNg$x(wchJJRW9uYKOg}O#Em1R*|`0n42sJF%Gdbi4GvBVZG{L5O5wR> zp%-G`ibW3*-pKg@lLO+F5?YXGz7D3_ud7=zn9h*QRNG!%pF@)Ln&~Ap&4viJ<^O6(-_RN+93`0a-E~ z;s#km{1wOs6r927U^KoNFmm7F#UU-GqcHwbrQlE+^&VgmBC3_+BBG;E6+vwgpDg8bO}p2_9tDwZ|F}NDZif(bZsr zfZsj}-bDW*6fSy)M*3Dr3RwCzpHHnLj@lH~cBB&@xIDzM>Du%z^53l9cfaake7 zSz|@ISRH+0H}(;H&+I*ACTb-hPE-%Z zq#ueUPJSdvwi!ctrT3X*SoYP2q{Jp1OAWMu2QX1QP9onRwYd`rX$K& zp}Zs?ejHd1B4AcrJ;;>Dc);p$;o{jPeO=>Bu8{L#oN4MMT=t|X_7dSB;#%G#|VG;viR8?yFR%?a}1}fEXs$p?%++%7_eH0-<9ao;r#~_#q4RC-p1gG}s zB3}PuVFo4`8dh9#{@o^0W+&d5C@yEAJm+6VXAIRKXfQ=#dO&O$-13-b=m8FosHX?* z$#|0I7T(#KaUgqgrhB@Z`5=TDsHA&o=XN$HF}Y=1ZU=t;Cm%IsI=)aJ(g*sKB!~cH zg1!S6_~wB)X!ud0W_Bm7nGEY$=mzrSe?TY=8B$voXl}j`TdJBkzQYtuVBPp9pbX%E zMvII#h;UWte>$epO`~b@r?zmYsTk;o=4j$g)H|HmjilHN1?js@-E=ml4l*HVHlZ0Jg?s=+qb$XqX#t}b2oP4&x6x1oCLKKQBXShUV z|LsOHs^}GBDy)hk8f@zIOsaC=1Gg3C6%=KjL8hec5|G}>RnAc~mfKl4YODt1tVYEn zNu_dx8ez5o7g%Py;isJ;>57>Jh6WC{RwOkNSTFKfvc}=EK1>l!rgDTsuC~A!@G63V zDR;Q!ZXs(w0xBL-sLB+n;?yL)jw?-;Ylod{K^aeX08hl3fxCh$Oe*OL*=DCcAdK49 zt(7UC@uj~Oo`_oKBR%Xrz7;}jTg0tF!a^iRav-*nY`8Ymr|}bV7ETt5=f(fpUrKOP zzz!?1ZjVKPtjCZV94g~t+EI(K-#f?~$L8g!23HE&Ys@Zf_Pqnm4#mbQt!m;dLQDe( zM4TLqtbs}6dG@Ohnw!O1;+Aqtck0qRXhUb_Bdj{Db#4yDuIg+ogCd4%*7htO2510A zDpg*NK`Cr-O>83BEVqtG(IV8$7De1-ij@wmnkB>B4n<+sts_QX;ijUVg00lD&>jtk z5GL(nf}+1VZGkX_+sdWaZslwc!{at#3*3R$hMRZpSLMcujqM}D!r5XHZRXD8JFFd{ zCMC6=E^*GrEv#-~YLMM>>+Wh`$ue!{p6s7;?&iKN%Jx%m4(ZwsNL2rnu3s_6JJ7=F z(!d7jfh$&Oe_pDvHg80&nci+=6yB%s0@t=uWuDlk;QC_kA};9~>M+f23U0(vn#l$T zf}wt=@}jS~$l7C4s*ye^%yjD|ep?6;A5ZzA+O`^Io+ce>Z{o%v-#RNo)Pq?v=pYoZ z`R+}}<=}e?Dl(z&;Dzm>s3DnZT_MKaKblJe4_4{J;-1 zVFw_H9+z4@Ofn@S1V2>rJEU0|Z!#xua)=6NdJPwg@xvZ4WD9J-rolzy?88mwE8c46 z`<8Kx-sk@!F#Im5AB&&=;!>}{aRxwc3%r*_z=Ji=!XfN|9kc-#NP!VFa}uZkG)FTK z7(oCl9qHFTn;7YGJCtFH|6YXde}GDMTA-RKMCjO|?(8?}z%?!(w&B z_8(w%pABR2MCvmRI3WydfCrERF&ja9ku)$TZd8zTNOwhhb%kAn^hbj!0{=202jc+JD5Rc+co4iVGRT~TywNr zpNU;NV@tvHnM}-H<8^_w00x|iUz0QkR5vuoKuiBG!4$-_8LWXG2!bI9!cogjd%3f2 zr&&F)^7hQH13z_p2kJs^WXdKXpAvM0#$=z;arIVWe0w57C_@f(Kv$?h4CI176CH9d zH&^JjUbi7bCXr(?_#zf#)dY77#7$x9^>l-=v9vWuZ$Jk?Hw|0>iIaE*V1NpQK^z>3 zFstTh(|0~+sQsySW?HXDiggUVtn(V*GF*WQbbu7B9z5VcU_-csgE58Y^^r^XIXX92 z$Y<5WA6+*Rg9D6RV*mz_cm|NT257(qB;N)=^AdD6R_5s>ZgFL{vH{;Mje}`TVj)+@ zsBCYgMbra0fCCgF#QQz?TJ<@UE4NTsr_29oxS)$PeE2yHWO<1%`Ur^h2226{?RX)> zW}EEiFVP4z%{dA;DSWl|aQe_B4+JZN!x*Rl2k^B?r@E>e3tvyST-UX$zq+eS)s!!} zU$-HmGewDKzzAf3uU`NS_<-ENz+u|Aq*ve3k}c9Dm8A=4b9(w)9=k|D6KI#`n6(2j zOoBM)f-+&u?Kl}<=c3$BItYribUqVCVK4e zh5me}F@}CVjx#>FgmSpgvp)a3GdSeJB)q{SWCQPyC-&Mh6t8(|&xY1pedDjm;@4On z+NXbcqt@Yb1A?;6w8zN%6Iz7*KOuk1*e}-tggk)*2^KVX5Me@r?+h|j_z+^jcMcuO zs~8buMvWUecJ%lWWJr++D^464apJ{~3|XFpn9^lTmnBuoTxb(#&Wsd0^0Zh}XHcOt z7Xlr66lqeDMvtassuXHesZ)Ex+&R!H%BL7j65L7^?8>lX$x0-NHLKUJW!au=*|Kfi zqFu*2ZK{&4T)lhw?o8WPVqU<5Zx#+Jm@rg^^DH)Y{F5%t#FM#VeN6f8Ub~71GQNx% zbZDW7wSt{UZmnp}YIFaVRGoC@}!tz(D0JbQTY2{*5HTz$M)!!m24PY)CEcBAgoN2mYZJJsOp zWB!E;zPTPur-=5w0f}1vR))$&}SLQ#lM3S1)PR4o_4QRLIw>FilmvP2I{bEnE4D zRoU<|O|)6!qJ=isYLAU}R~M1u?x|*jl~m7DyS0{FKc5vZ+p5x)%Bg9~t#sYe%CySI zINy}f-MN;0w_GJ(<@CiR?(QDk3GVJ5+(~eUOUQT5`QPqC4@URy zu3EeHT&w1ss|b~s6^Dbyh6Vuvfs+7=D1v~1PJw`c(LsKB{{(?n?HU9GL&Z#3SYAR{ zm{8u{#@Ni#2n56xbY9Iw32n6Yd^xs$nA6&R0*ho&MmFv<8|MM8s5FV7Ju*0|pu9Z1 zpgg#UyaG5HLoOjPArMjCuLtMa_1x>E^X~VHTiwF7=T~JdEoSA}b>&YWqfCq}EQo?| z0`j(o&56Ej8ynq7(Oz&2KKN7J#T?TBVx`fZ9`NVt!6qmWJf`e$g5yV>{#TPPk;SS3 z5Fjz1$DHpX{y_R&=ztK`^9uC>Kn1LOucuRn9ZvOvGruYBRJdobkcnU=93F|dkyo&X99oV7{60Q4&7aChRS6!{HkSPKEwd(Y@Q8Ee!Xm6b^c=hH& zNO%_RYdC->oh!0aBA|L1cn!5*MxZm%&YEZcH$Lv zgaKO-L4qAGtJBw{b27h-f_VDbA+71St zeIaOYZYEQ@#Cv-UUZ;;Bc=VowWdjB|cJb6PwvJu4bqro(Ip*$0u}j5DMN0|3TM9wt z7|LH#SS4Uf;uXUFp%6o*Mid{;O^{cofJ>kn`}*ba7ocMKAUZPm(cju#JUO7Rbm_5Y zt=Gimt@m?dSHa6`%NqiW&*qT*;3WYJNXT_qVAm%QMlq3464rHogdFSEY<2DTu~qbX_xfaZ#1}# z1fZ@0CrD`cFB$~LfBe1}`anm?qyL0*^+kNnGV%eF|C}a(fC=iL`w7|$hUR~4(e|BB$F<;V}FP*-;kbX6Y@Ol2< z2cFP}9xDOypJV7Lh$QnW>!kve}7=bA+4M;-N z0Q!)KGZDAMZUadR;Ae1;LaxPv0P(R{L&Sz~mt3_loB^Iu8Dew*7kNxnJm_fT;GH={ zC)7P(XkpU)T6v=qcO`CfkQ-c9e|6!rd^=O;dJ6T=G6JjlUQ>*Rvxmfvc&%_=1iVpM z!ryy!86q$RBZvnOcYH1eNpx!&Fc`*Dw8t=w(dz>H_0?+fzOhztCWS=yWep_hr`Ndr zn5=PFvO=~;rbEU;_Cgkq(~UzR5hYQTa0h0^j>VtGD#l;Nm&K&TyT=X0?#E~opAmaW za*4N!iwTnfi^PnD_vMl#RfL=~Px+QLYT@kd?eXo6?4|7aFL!oHcWHMKhD?TBC`>6u zC>$v=DDx;L6f|=W3tdHj`1Wpr+oo}dYm%JfR)M-8+`%`=NQhGkO5|J@NaxZNTNZnY zwFrqy_RAHD?g^pEDT~ZY-Xxf(rX{HRs0XJ3Go`g;y~5q&+{1&S#L@;b$U?}%_o>E( z(>|-yRDx9ESCXGetf;Se)YmxzxwN_PxDZ-dTBRKCj`j~3ul=sU-M>FdKU!P^t}S5e zVeMfXVSm8x!Mb1;VZ}y(MSu?o4B!r=N1#NsN#jbZOKVFXNiU{mF?yL88!Q;!>QCre z8{?a77CI<@D*qtCVeYq&5!r~>bCDA3-BoPv(M^;8CN175{lu(t3m95Jv%EHRO zlxQn!aS*%qoeUKM>dbk}MHTjdQaIy9kc;^G!S$E zM-GJxjv(|&ZyY#HF>LxXrDtiSdF`x)Q5Ptwt@FlYuA&8p4SZilX@9yD&|?&z*`?xk*fZsQ*1ZU=7k_azUzPc)C_ z_vjCFPe=EO50y_ikE##f5f~7fk!=x8F_v&ekay7?sZ-cE2we#Sm`T_SX_Q#59SdPx)x8hS$1u4c60dbA?n_L2a|=7rEIREUAyhM0(f(vE(-!|WmJx<%$S4R^cqX&@@Cnb`wRVR7uCw)3e!sNZS!pd{ASdVZ1BVn4Y8)jHl?Do;Vl2Q3zsH$VUOe|&qw1Yt*6bkx~@C@pZ2=Dofj_<{*V55 zLG?lBeDWT)Pq`z#8qo>(PO#LlP5epU*KS2#C00XvfKOlhi!<{_^0f@a2i&vFcuqX3 zo;=@^y#G)0^8HfZbZBz?MEop0uMS;1v}eOB*W=Ab`i{-C4RcXRaoEO>u5vf}=T0|u zy~l}&^RXR-1%iz+_c1!Ji2Hkk^Ih__;ykyihoH}?fve$<;rZE_e3P_+bZ4~LuR>Ra z`l6rNkBOMcWoXat(eTb_D@4$_geMUN@#0aw7IRHZxKiZnU(nl#+C)(OmjsV>N_;+^D2-P z_UwMNeNjEJEq%OPL(6r43$jrC%^t-*uJbfKHkaqxd$ZrNkjrM-<|ZzBpq^~lG;CGP z9m;!fu6YuG%B#z$4sA#l?)sUowNVvo?>LH%&4w8P3AD6VD|t%T4@Vwtt%5 zmUD#y5c6QnBAPZ7B3&C{$otb)3p2-Eh;=l&q!J`_E8*eL;qW z39)lYjtvp1V^OC@oYXB=4^*Fp1C{&rNo5K@?`fhLC7R9Gi5o_>D@uLEP|cn$nTDO6 znKqu&p23=`oC{xUVwPtyWLyKZvmdi}>c?4OTkRWL>*rchn)Vw;Hf;P*9a=aNuf%;;t5G9KN6?kc3*%?7@qOEj*u z)MTuzUGPYE3i;aTsZHBnxKqlcO1H>}o(7JLbeG+jS7C>*d`)hpx9Psnn~!m9sJbwl zy>&V_-bpIKQD!G-@V3~oU_XKUP18BQ&gzBZBwiD2yV!MSxw-ec;ZgmCZ!?=91JC{K z*G=z*IG|TJ+9TUBYR%Q%>3U}A#zc71MB7sC7uf}Up>}B(o4dnxpKjvrl?m&yye~CKfT= z^-+IBD~O`=Ipk?gEntzaFxWyw{0!5tiPT8lkORplG8CgF1J$#{vQz?PgEum|vtoyK zCvhRg1wn9P*OP>!zea?IS$yUn(&|PTDerj-qK_l!)gdONnxZwOx|gL8!Gt^r`x8#+f*8J9IsgC8SeQRH(|e0*3UIR@MS~zbUA8Cc~zVXldG1qisOb81NX{_b6awU z>$h|8x9zO$tuwQ2(w*^buV2gu+_00x{h$)Q_<{kt%ZOF-nuoGWgMVT}Zig;Qb`K30 z%YMNAptg(Nu_3J|+W?wNSj!x7dGk9x_X(}MQtr=Qnp8{%6`Gi64xbLYC-Hh%vmR)i zpFec8XFgZ@-SlMlBa`;cJlDQnHo%-F1}Sf0P?rUpcWQ69T7FgaG%E8wD6`pVD5_pW zOGD9cx5?9pd>ye$Km2ui-9A6nV6Sm3YbtlOPVYthvTRx^r?4vRpXEGL9JS_1wqOIGfA1SB_)DG z?d9C%{exX0Y=}r-@MT#LNgjR<{NVc)B#jWFwgnhci>)zxU>{*&EZvahuGX$<0>cQN za%h^6L*Du{FQ)aF+8Ecu=cfP>|AI)RjC%Zu$m8Mu;WkQXiXyw%go1=bB}FB#xum(@ zW?*KGW^&UCvx(DJvrSBg3=zzajG#q|#%}uWIZ;}^wX|N) ztx=qx@I?K|dV|*i@8WpN6Y$sdi3yl0v#kTxl%yQ`eXDE^2e0d69=4?PV^MLJ-;bz> z#m!#}dQK4v*N;-HsSORQp;C9+2PUg`y88q*zRRMtg&gN%*>G$B4bqqJFK9tV0s8qu zJD%~HvzB$RSIp?eG+!&nDmW__Ex$x-*UK~lR&cn6xS_f1xax2Xob{ag+n|sO*OYnu zc&a@rAA$E@?&lx7VEevEK&%Fd1n&oH!4QPFqP5^wBFAGr5orT**`Sg$;x2hvFZ=G| zC5jnM>}S=O&1(~Ciyf#Y$@hv;*-(EcJScgV<#je|)H#|pS~q$h9iOv~7;oV`R`OZJ z;^j6Gx5=$hcCBtUaT4-k-szgA+=SlEKK@9=ph8ij=kFkrZjinDUXr$xfuLroo~ABX z@xu#k1@(!neY|e;+25St%Hma^2MxmgCtSfVY=2fw2uYBGEo>orSEx=Q)EL5oPpaT? zxzHsz?w?*nY>6CyxsgFkhu-@Xh?Eie$IcH)pt6pDCx*KF6$x|a+86{d8hxWWRYQiA z;L4y(jt`Bwi6J0;lU4AY9kf!^&b2QzF3!p;ErcsTDfW>0o+KEnm-Qnh^yjsIOJIL^ zVy08Z8BZG)1m_@N0+tSi2kn*9g)$nan&CH@4*eS^D25;<5ppip)+%v-9+Lk4wmz~U zGEAJmqSH*Vk*87QUcnwG>JXYUT5>{B!l2T5xidjask3s2SiVr*H)^>yfnue*JZ=A1 z9or;kp%fAgXfM#Q!Hb_+;hjb;dvJhr4sd4L(Qz&u+ArP&uEufq z2>+ZNpdN3VKgcKW@A4Lq{*{7#L_j~58it%yplOI;T+V0?n94O3`{w?yj{X(+8@>|1nH4{!t`sg40JmV+bMWgDw z;$N>1ID9$?XEj6kxQ!7{B>2AQBY7xCxr%lutp{`|1Yr5(H2P^N5@h zlWvubi|%@$vX4MGp}ZAeGNwFtgNZ}1QF0CiL0Fm3^vufGC|^hUgq>8 zwoL8TyT4GqD*if>y>RPTg+AgxTu)wkL z@Fss$@myS7^>yLfggewai^lRfKY+F4Gj%KAem<6%`RY4eJgKi;6TO*Q@6mG4wLQMC z^r!8}EIt*duAV@6KWN_!y#j-o--W*8Th_y+_uiIyuli~0%Hzc1D2@tFdAdnsb0c=w zu;;f8}@Q^Sa;Uzq`2Q`AfL2zXvo` zjBOo*5<*)wKi{AK(iH{)vBU@A-@0Ea=j6DDTBMKt!ymKpbMuWFK9KPb_E!bBkS|WM z`26j!PZv7fT2Vt+C<9OLMmv-(P|X1Z1P0~r3lt zOz~CZ78X$~?)O9HZc959`|$Xs2aBEfPkq?u9;n2J~) zWYORycf@#2apW;m8mA+VIK<4(&MsCILuFO>~Mpt_j?cAnUXtjzvW1K89XJn}1B8!(>6 z3PTdc64t1Q592y%S*KG;wdGI?q#=vqlLWWIoOMGjuZ5A0$ifFWYc zkdwvbN@Q<({>f6QJx**?0RO^s6N}5z>|mSbu}OKk;I)&#VdOk+uov()(mG$>OH91F z(lfH=NGb$b^^SIg=8)V}zC{2xMN{%A5&VkakL1q@CDH|lw!_X%1y|=q4IZu` z_og~ynPQ*Rp?Pa@Xxvo`fSX6Lx!A959}4!Q{M0*iRF)wydo%3X@-IgRATdLQ^u-EN z)E*v`lPZ3NWe-K2jka?~HVaSSeoy^&ct-E7E$jVf``*r}+C=4qO)n*dXG$!~cp$p@ zOfyrU3GX(m?gIs8;XZKb*r91GAuMV0xt7XSYdtpKFdQbRP3a#V(AYh8n^?FaKU3H3 zvc^|LWg7IEJrq*ADISKgmMamw{kIlV^+*iFfkP3}SI#&TqA>eXDi;eTo9G)ZX*`ZP zs-LYCuxvklCoB&b;CHHO_daQ}1EpPWm#}|2MIG(Eto`{c+k_IT3D!`&Q}kbD zO@tH~rZzcv#ja}g%GukMFb~8ju*9nbUJTqjP=p`OmABkAN2?q2$q;OmD%3RxAu8`4C`+f~99p$AYRa;i9ptjlj1=!ZE zurdOQAH@$IfT!(8E`=X_R1sl-Zh#STPAtU?IX2k%VkM2+dh2NIjvQ1!wL)!WB$W!K zv%h4fg&c>>rA9sEQLOdV{x~%-PbFxjU|n7>Qve6rY;y1;c37dGz=SPzmKpM0D7!T4 z<=R5+kabaXuLDC$^F={P$B?GhLQ$ozZ-o+r4pQ&OeQ4t`iu>Ilv_(;4vGa1yA-4QBV0!j z5HN$cLJ5DdyqL^PWeW&A0s9mCr=iOUEu-9xrMM3JmR;TB*9jRKHgjb}S!|&Z8u^(3 z4wwW1jV5b67PCAec^0==dLzO!M@JorVF!z1&R>K%svkw;NkT&@XR0q$fq1VskF&HYTOJ6=n#w% zxar^t5heDI)Lp0+`(-wfAuFnB9Az55K2w+&$Y5R?5h|S&aJM-4Eg;4_Fg|fKQu(1A zs>C$=|Ca)&pd|9BeL=MjW<>1Iw!!z9tquL6Y<*I$J%T}EQEW=#W{Tv_SL8A*7a0PA zQ;GlmYJdpDS1<)pS1Hvo1j?7G%j_<&mWdtT7kOeR>5qbycdAg9?iR9Xy=e3LKc=Be z4K5Ac7w}7dX&RA0k$eVFVCIk=L(yjeokvQy#VE7FG8=*t??{c(SQsx`gks9Y3r8RR zuXuzB7%A3mB9azhkjiQp1`upXi%ZOrr~+iK@*h(LvJ$@DHHz^e+1l;>E1JI`m3(~^ zzL12jlgM}*G;QAJ63Kl<%s(nAFmnc|!(^WqN#z{92DE#AKyMBO3rbkAAEL29u#AaeAd*Iljy=VV6 zR#5PTN)3#mt2ik-i8P*A?PBRv54;bwnD#){%$$|e8y zzH4K$T&gLE`PS-KDxJZ>SlS#(8HZwO;GU+9@qvsoK_8-pt4V%-U?%J)S-wieH=><^ z$nqLd(o{!mVO;X5fj_7n@E;+N`23vozGXg|%Jt9iIB$Up*x&-{>E*_ofTL%BiuBo1 z`L~@?gURtd6hz>0v(aFuv6v|;&TH12DlGj@RoWH99EPe#;!~=Q`&f`fB!Nqw{S7*t0Fj2MX|UP%wRJpL6S<)g6 zB6?G)bT%I^>=~1#1u|s;DrvP+^5hI#ZC@W8UPC;+wOHCSTkYWZh?(>Ai3X?d0((zZ z=RZ7$2q-ATR;ag>2@OA99|6YG*%7QOHJZ?Gz4DHwYRjb)#8RAD(_#--L}3a|ADEU4 z_ROYB>*`O}MAOV32&F>sWw2y|g%%B3XuZhgg?nT^ZYm8CjMx3Tgm7HphSg#rKu;qi zWJ^#6Nl6>k>Xkqqa>m!Ur`o(Z>Jf5aZq33uX|5;2bi3X`a z5zr3p8~?0lhGm9rS8E&U;J!$wK}N9oNUg?Z*Pd`_GtOfzYhg^?&Bv?L>3L4M{BU|z zkc~`fwzs#ZjOk5g?-lz_``5&GqW)!tH8^1{sT4XykD$Hq!I38gNq~Mx@|NZh`8>D? z{yP(nVcl)9KgY%L=oi&1L8q)EQ_d?7kTUd_{(dc2Yrc@obYHyK2TXWWApcrg_36l3 zV%^?u_FoywnFfHE85t4%phViOg)CNLol#f8Y^0^6RPm6N47Md=14F+&PWmbIT!AAhEcVVGZT-CA(?n`3L;eNP%=Vs{*xy zi5YW;(Ql&7_kQ0v;~K`iU$K_k zbAewnxT#`F#y3}{SlrSzYTZP3IDN&YZQDLDBLfZ^VmCVzSxaT{HD>HSQUo&8`S->< z_=M~cWy^pNl!#Vwjl(SUvpBpGcWpjeN$qsOHm${r+y@oepY+HHKh``X8B0UAr|nZo#7k9O7#Zh zTTkn*m)ouD+(N13FFn3%@?KB3GNJFR6e|?Vjo8cks@o$?Y972}HlJX4>V+$~R_XpL zheFos8wurVDfWc|l*?ak&h-LW)34B|%96M%5k==KX6sV^Z&^Nrsw|zio>cjAtTtGX zUmZ*$c<`6LjU-YVZS@3#hAG9$I^Hjk$dmLH5_FOm?iu1CJY}>~8Dl$Kl_h}G&Dji- zqxc1k2Kz|lifvz9N(@wn#gWD*4F0E`hfu*=9gmR1M%oEI$hlAah)LMe>HAQ)XEdl1 zVlfQAl=c@)Rd-3!JmgVcHhD!Q7w+Xca62cKv9&T_sB@S!S}wV~$+O?KtxPdG<PE0gCP z$VLKmG)bo{#7X5x;;8n()2K*z@jeD+f&>k+U2U?_u?kg_Pk`=W z1PkPdoRS8sYy>$@O;>eZ$KJZU9_r+wjCsE8-8)R)j9Wy&(IyQ=x1k#i_Z&)QVu;9) zd>>Gfg_ri6fnj;)l6H^LILxf<$So;@z20RjCgBaw^Ur z)6adZeVl~4mi7fJBc`1lkGo~uR6Z43NbWFcm2}bst2x1FI z(aoU44R6ZIjQNDojow*s&ws)JD^yHqVfYwao&27s_5&oTp&-<{<=8!}JqD~*v}}^1 zw6t0wu-sfWO}>itx}An-gMb0+nUj#M+82f1y_)oJ!}wh(O9ks`jLY%e`$DS!M?p+I zM@`(Md05G84k&8$@wEQ~A*8PoqKr{L2Vh{iz{ zszxl1OqUX=>Cv)yE!8>;3S2x@n^aDz(9G-y)(HI(b-$rQp)l3uYNX@zA-Dv&#V#C| zMKD-Vq--XMM`aCb)UFZ^XI+LGmKv%n$Ryhh!o}I)xy1Tqwn=p2uWGP7IZ$Tw+ghWA z)e`(CJbvWeQYlIdEkm&orSQajgc^weiZjB(tlq1HXu`rrd1EP1}1k@iOnexSIRt;!<>Mt<8ht;Q$PSw z-C#WDkn!)>!@5GQF}T=9eX1Ck5h+BBEDr)%h{Z=Wl|>F1YS)?Zg*OmyanH)sJsGBJ zMC+G5sO58|{fYBlPf{0rPf{9*0vZ&)c|>vmv(aSxZ)(Si1mP#(=D_CvbZQM67%;aq z1tAi-P9h5!`Yt*mHOMZUW0l3DVvO9ytXGe|7V7rT5XOqZBo)pa?YHkME>#9s;4#Rn zABIX+Ci1kXefcM6h%Dmy&I|~Z-@O=YFDh%75A7KX0BNC7t0e*IiJT`*yi>v0uz<%{g;}8KZgp5!YvAn3}JHxlMo5Xz{8-rk-yZ6+6O%Q zIhO5)d=JU@jv?~yE9Q$Kk1I|^ER)E&5zYE_0KO7aIdodr?hpm_|u z;a9aDLkg#t$D1B1%^rt&EWML5La|FfNFp}K*$|{{7Qa9qBJq)*p%P@dar}Cg@bi$U z$c6Yt50M01Tmcqta}D_Dn^1ExwjUe1FPRTWz-LAwU7%eI=KIz)?`vx-E~tr!d2V%G zfMlO8c`b$(j=;DAM$)5K9I^l9f3y+gXNhhC@+nlG zLx~R)@7;WIJf5Qjx+CHsn|e{kI}@>?K4AH72XprgPF2#<)e>p6YhP18F35LhhpV9VG`i$()K=iYO=?D=8ED-UmQ+|l& z|1{H$L{`Ek6w4#(`uPXhL=nl6E?ccI*`b@(n`OHR198h`AmXUI!r2x+7I6ny)0*)8KMq%@jBZ%NhI!86-k^c?J!Y1~al+eSwa}BBs)1 zn-JugaO7fIrk-fqqI?qbxN@Z82L+_lo5Bo%6BsqQ|0=VP?$y4ayQ!el!D~Sy6C#nK zk&Xh6mkg(2JO9@FkBS~8Kn7s2-yO0wDS{hbD5A^ux=lT8Imo*F`3Yf;Og$g|1bn50 zpjvg}BN|#rLi+wy)fQ5bo5r$zsq*M~SFd+(=Xc#r8s}dlJmv?a2!we4t!W^pO5;nO z!d+19yDSqXI(zuf)>aDji6tpo&Y!7miS>ysLMnN&KAa8lBrr{kp5^^V{tUqvhIRn4 zQ&ll?;lwu-kKclf;S%jEh^?W%@ppKXz_y<4fD0|qg&-MUFQ7F}EIAoF)0@uqMKv;{ z^pR^PI9jO2tpqzm)!(ML!T!f-eEEm4n!byZY4O*~8ckSKP3Mbf7SD|kiw~o4?*>B= zt;ob+^kKmjZ)M(0VmK{3~iK z)NQypYW(NqIkB8bWODG(Ack0~@USpHZKfB8hsnsrkg`QkMH0SDcWKEIpEugLY_u z)rcNj1v=;JkGtJ9tl*K07n|g*!o^W%B!Mu#?<(#D?opb5ey%bd45?i1hCYq{gvZ%E znX%ZtvOq}Q`A%)Wc=CIA!XY~;|7I;vt$6+km@OS(hiA%?IEUxCC&{JN^l(D4Y z%k2_g;?lvU6=t^WK9*1vFwHj0R8P;7YQyEe3&p%HWs=DBskCKgqk-A0z0rfLxXejD zxyMXLbHeTR_V%SCNiyFwEcRS3jz_9BE}sL1_XN$chWutCNo739;;}bJO^@BZIb6>q zdFv6$!%-i}^L>QnVG%)PcnbYVD%zwop;%U@*dZr**hckD>hhd`#N+LNO6_g?2svOB z?e}O7eDDa`Y|%)>Yg=ghOT)9VDb`-6ooJHgyosN5Ykl+@TZw&Hu6f4(}}-`7MRR6lpDu8@PJrgVliS##xuDpx6Csn4t42RXQ-LLa#tRlh0tAu z*kCaoLN7T!9XiZQjUyz#s@S&~iX5XP3#H39HdZL?_DfG-=EdWHe^f8i&qOJax+sO(qr&SuqFL zc#U{Z1|!PstWOUnhF3wvShnXS0(lGQ73(ogE%UU+wM(5L6lfIg*zfg49suU3Jw1VP zDf}qF93U?KIKC@qxc%vS!lEpfhcb@5Le9`b_r2!2+ zatbNE2yu7I90qK!z8F4pZw4a^DYatTK>nfjHza8#4qZ$#zX{Y7w%bk<#-a57o_*g& z+b9r?gTC#RF}2(xI&8m_Xi<~3#S%%uT8C(b;PsDcY8#)9x=k7Y;d)ai%G+I5t#nqjin-X-iF1?VB`&0jR4IjT{uZ>! zBI)i#AM$ybWsk`1c}_#WD?f?mC1>UL^g)44F#Ohj>i&3o@RQt`FtzeoPq1x%N!wow zS~1EemK;1Rhy$yt5GC`XX`1P%F}+aapc47L**BCITZlqF6rf=ZE!^4|7GFl>rVEY`Xl4q#vvSgqlyq#XcTm_~kCC^1mP|FFn? z!yu>hQXBO&08`a(SH7OkcX()kxoy?bxV@gcw@UGu%Gej{PFh6r=~z0;y+XKy1!6S> zb-DM}uAuR4H_E<1*K64dF2wUGgi&}r8+~jjyAJWrU_><=PgaC6>O?9n&h0vXgsiPs zIvn0C8HE&8fqb%ng(oWh-!lZqem+haY?e~RE$-9Ash*?sCvlc)nt0IKrB_9D+TKLK zh6To1iS$0<*Q!Q5uwOcA8X5v%wAxLLX+39v)jMi}#_=4Xc>?NJxWxZa7nBTX0ukn9DUW~Th0nMQ@av-N(n~p zyXeExMgF(-X~md00>zSO>aPB`NP&W$)JKSX@;&Z}{aoFlbknXnNHFrF`Ze2;KEzor zkm=+>XJoa@Jy+=QHsA1yOf9Fkr6cND#WK1CZeAhCO0?kf=g)rU>uoMmydJj+=&gst zLk`!6(<*^tHrSyv=n=qPaoKm<O z1*B5&MV)#R)YaLpFWY5bJ=&`BUTR4j!WLOrLa@Vo7f~I6ct%4ccU?QlA3t+U;)|bI z_?6SmdU;%tkUQ&=8jFX<8%fep^kBx$ptpzQvWAHJvMGw&d+gCks|W&|MzdLdjpPIDmNwQ4MP59 zZU2PjY8`&Jn|al}L{;tBhpU6p_lHfMFBWMY!&LBbyNEQM@u)-3719h0=rJ=V2oU4Qh-kdW1GL6|Wn�yDB(-cw4U4JsU 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

V8cf(3X3R2`YRb044Z1TScNL@Yw(#T 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

Ac0bmM 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 diff --git a/website/images/mcollective/li.png b/website/images/mcollective/li.png deleted file mode 100644 index 73d6829a2a3b45fcdc4752d4075ab2af55a8b7e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 149 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 G1pf~|{>a?` diff --git a/website/images/middleware-magic.gif b/website/images/middleware-magic.gif deleted file mode 100644 index d3af7ab95e8598f7098a6251e8d8637643b4328e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76782 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 z%0D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 V1?cI}V;=I4ZQf+GM0Am4$%1q?$X;eTHc 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 D$8*PW diff --git a/website/images/server.gif b/website/images/server.gif deleted file mode 100644 index 5d718a45fefd9924eba38059d21d41a2f8612c22..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 83946 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�?jChD6Nc*2O}Feb8<0pRaL;Jpd{m!U1GRTPIdiczA@5 zF@$_p*#OEfmEKPF%W0kLWcwN0m=@%j_T-v24s*civxa7`Of4YuX_u6~wpa&NFzVZI zfn2~B{1RzItYsOF9FhrVJ&G8osp^k@hO8!Qgm{OpRuw~dgk=cp!+Gl`1dM|AVY;rh zmr?6e`{hS)>x7=&>XU0@7HuJ#Y(UB}yrv5w+c--V2w9}Xqh+DXXb)xOL zu!%V3ARXD%qCZSO={Q$Lx9sQK%G4;R(1S zYv7)%R$j-~zEk8XUAF#iuOLf#81ERh)@#L!iVKbO?%f@3V}md~F0_&ssDpQ=Z)xNy z3y=G$``k5l7(tpjas3LsdAw46E}hz@6gGn zhgblR${8Fo?^N<;$qKpMXx_I;hCUJ2Xg48R6cmufh0Y^|b(}5N4a*;J=--?282R%zYcuUD8 zev^6_hE%r{8PoM$|IJsILqM1HMMtDfFZ2MyOlc>zeCu_g*hWVO$6;TB)cb-P-_lE8 z_Hf@@Lr)P)r*?6;_X~&`7r26QkVqnh6K>ZkRmn8;MKXUEcOfG8L@@VRf0G}@?ocCF z39k5gr;D7#RhmMGYTvUH|Fa|18ZYyB$p5jR6`y#D7x>Z>2P5dhgZ~1B&rnQP)Jan= zA4ddBfCpg?Z+EYFj#*#R#`sugK8EL*T6YtUKT>UvdZZsdk)I2aU+H1qm1-aL6EAgK z=6CwdDXhQS8TEHd|6yX!`2$S4Y%iA#d<3C?ggQj~(**dW_n)O#6^w6$bB8Pc!$@2& zkq0*|SI+z3z6-4f0ItXFiq^S^ajn!d0^WGiI{D(6rT7sxdW>@iF9&l1i-FZ+ZgZJ5 z4g45qOoz^IW!LBXkgSIqHVeQPd|R%d^HB7uFA~I;GySf~w{_`32l=L+^2rPh$j@}2 zdIMvMtrf@;Ir{uP7xu}IE@oBlBLBBS*exDzfbT1zbeRt;Wn{*(a0F#Q=_ZEZz4s^A zA1|7g-=%0PjLx@!~i9(CBy=v|oU*CvYIaf(8#FOek+*!iEkXLKG;^-b9D8 z0AS2GAR@z|qYeMS=?}c8n==CQXknYtl3)5hu@{JYUkBD9>Ft z94bzpj8SJDOprdGPIT9<5+p>44jqC7j2_IV9G!X%D|Redh+xa2r3h4BJ!Os#A&NUF z60Uj!&$g7<)!<&fVEYQpC|GCO!iLr2J*?QP(3XJh@e+(tz|6!h6FZf+t`JF+C~+WT z*%jzwy_QR(j`%S(Yln|L-v85#%qY?eN|}Cq-8yQ!O;r)Wh183y?zF{QBTs&}sdBO} zg|>Yg7t!3h%U#?2YFRQy!p*;f_Zwb&!pPh!uRe$lbJKOCNFq&{Yh7N`ikp(I-~9C9 z^y5+Ur;uj3(1uhDd*G(me^J5HN+i=E0*Nj3u(Vi!|6yn$IvM`7R&;R5MVDO)-ep{V z1aSv|cO33_AzC20nA~xYrC6CvFgE2*F7Ta{5-aqL7GqEN;RxPy&mlJ>i$VD`R3`-{ zg-L>HW$4a3wp0ZoB(1#Tj*>&3sNa%fiY3p6WX6OfTgpHJB6W1pb6tsuWp~p-L>`o4 zIA5X}7Ls$aCS;)Nk^g8=F*T+Mqgj`khD;0?c&5S(ovafZqx}VWsd9hrwo{*D@mbJ9 z?i7;4QAo}33R80amkvF^M1mDuk&FV5JNMidW~iU?TIjB1s@c|WA>tBeq2eL*<&1j% z+ADg_ZfXugmK}QDS9FTGoUXaa!_A`*m?SA3l|ox?O^s!1E>6WY6`N*s%y8;SGvtyP zb4Cga*Q}dpQxC4WHmk0{178OnHN(DHEUU*VWFJq=5)5j!*|X`o#;rIr zKx&c)E{yvx$E9%^@lSb8Ty9_N>h*6q@XC9^49{d{=XXOnBOP3ZSVE4!C@-usw0jBK z=d={L;|#*p8UIV=UFJyZ>79tps2jvabKSK)(IAR4x8>vjZjUXrK_8%8C!Ms{>iTET z%q@6O+YH=Hi*#8+QH?HNYPqA0bXEaP&Cq*SykyIDzHPB-LNz^XB8b3QbfRI(v~}Z~ z7q&P;U|VY;kz|{VQVV9NZ8h7TbI!Kv<+MS!-QrmK7wZC=wNN*YKsd-F%g|$T>%$Y@ z5suSFKHcP%Gkl}l25AoW?Zp$c-t-B9uCeI&$xTG*D0NYe&Z@&rz4^(;4SNfoos>g4 zwP)(w-vZwA4(|{`GECIx-?ujZyKxJ0PE#J`Y7w>PktKTOLg4=lV!i7f1RG()6r;+~ z1P`!J1^-hJpZ_4pE6g?UgXt^7`fB4ohc(YnE=wXRL#AQ|nq~Q5-7D50%j%fpo zVF3@=te4?LfiCml4a*|I(9y6U<(PsB*yn*Ja8PRYz!edd#=s)V$UpKZhZdTM6bPIU zTPXw(LFge07y<_ohKR-fUQ|UEnW<_g!=f8=$g^R|(1+kj(TeEkN3M;KqCn^o3rWBbD}xY48&xR6Nybf*CbDDl zco>Npj!`vU;iD@5ccj9EV}hMaOgYR@!jph;krip=99sv%S}ur^O|axiFeye&nsR)i z8vh7AY5_{%z{L`!WK1L}c12`vDu6n~V-GE7yjgOmkJ|(1=FFkXT^>YzB|wS^!l=n_ z4$*(?98ZxbVnJp?fCwBs5k5Qi5TMWl7m)}hTpqE_r#vu~{3OjWWw}ops;QM}e5D?P za<7`uGBqh%-H>9LL&Po4o!06mG#4_n7}RVA;&6<%DtfJpDvm@fJy+u7=7V|0=LiEW z==}6K&4K(&9{ZrjFd)HDL_C5U!V8NWiE6%`Zti#jL>T4RNInRDiH}&tDf)Jn(y(C2QBaeg&L*)i;h*H8$L$ z4{CD(zYg?8CY2*WsMx|~JOYhyal|#ew8~;ia+r^WEGsJ;m&+PUZ%`2|LOLti*Z#_; zGD^{MDJH5d(#)+Guo*I*BiF{ZmY*U_oIJ!)+b^zQgVb%3f*w;cnAq) z7+1IHg$ZFmOE7WX5khLjtaJlQ6Yi!rL-sN-XSvf|Zhp5Me25on>1AF(O*O&-LoYl% z)!rP`1_Bx^hjw{-;g`8J@i?l6vg2M)S#fRscWH*vHpC%?-idWp?pb+Y&Sd%e1D)As}3Wq}SGbeM;6yYB` zcfmuJ=DEVmk#`Q5i22#ESejho<fPW{8gyfxx(6OwDk1+WnTIDxGr&r zpv7w&DXn&Ri!QZLZmnb+-lH5dsLiCqXJwpN>e+wev{cM&mEg3(Rs!Z+mWN$DICIqy ztC(}D&siTOA&S<{@;0y@6ChefHh0c*$vRj;gPI)(H=FjXv5gln(*O32NiX58gzuH? zWlLHG&hAhZ9a#@}Y$8^&%ETtLo#=~4^T!8k^|)VcA(!CUXnaFuxrrAlJ<&8x^S(>H zp>%;3EQQ@(yCsPW+V5#rM4cEi_{W%?f`umvK=*J5JVqh$SyiGPBf|JD)qT**7+B-s z?s#U2c9)Q|P3JDon|eieCKsVx+D+QaItWxV7u12pZ?@sOtcn?xSH#UlHqM;x(Q`@v zJc?u*`a2vXD?~`b5=v*flb!C6zOj^hbIRz(u@YpeH#jxuz`E-uiN%F%XwL~r5jIMW z;k&pa7%p$Y8`Lg#!V73u?8+|7nAAT{-Gdtl?@`{t)J?wo-T(1{7aHp6c%(??Z8WTG zJcz_i?$eV+rGn&kU5f#RDr5mNztGM{p~oF;T$r0q2|W&0P#e;l{+@=HFrAYGR^)&8 z^?8Z?4BYBg6{NrndUxKvafIWZ#05HMJSm2ReU-m&oez?PIcpp-LpW*?QAK{Y#uAF9RU|Av8%D~<;v|oJ0 z-~7>^t=+||5Fm9x!ZIwK|NYs2NXt%%Aj_%ic~`i+8#`FVE26=8-$og8@i}0oVf> zWCSgcM?-MoBYG5Hgy9FCL<^81w`fZsvJjgki>MHsja(5(phGxpPX~ewQh31W$lyf5 zNWNUfn_!kI?guJHj0Jv#e*K2y8Q;$AL^UvCR&AOmLR}>C$;AkS9AJb2fWaC7fF*{5 z1YkrKfY8ZtM>H^;qwSk%tix=mKm?pb1hl{zsNEoj%18ysLhQp%1dwmxj!xau)%>0r zE*V7N3ZgxeITjlLLS3dIL?itkqy6J!G}6D+4*y}Xg*fCw3@|_hXe0!zfc%-*lgXSz zPQ*Q=!!e0RR+$K$r&%;N(E02syT0?mU@0JcCiN;{~|m7sR8z%piIQMJy^_ zVRZ#DIfU>WdhyvbJgiKCm(7EBc1)uFar01<%wnXBAog(!O z)|znDPbMT8wM52fM4la`#54vBU_>$41OFocz(!ES6e@s`%|ulK%$+;~52yeJWPnMu z09Xn|h^eJ)HDo_FL`1HhUM@;aS?3f@XB)0sP0<;i5NB^;=b@daNN$>Ru4jzBgrWVT zm$-vFtOI@e<$e0)VCpAfii0?C!+&zaGXw)G6apR4ff-Ce6Zk+5%)kt^Kng4-W8$J{ zW}#_%S8OyAH=LxKAOdma*gc>FJ|Ko#!ekcyNoQlJK)+wF#XP)Zm ze?CK>A_Fo6LoNtvF1$ja7Ah;S0wyRbCKN)W7Q!AtY8^J-AR@BmyABMI#J{mxSnua_3yJ zmg^k^q7jMb^ix6h9s}vhUC8Kkz~*Y&s6AYSWISa~s!T;#<2J5=Zw`PwsKNmNDdRju ziZBCck%m?(CrYS5V_tzcr~`KjBqtH%DpDUzcIQCcW11?7mg3`>sp*>H=X}oT!1Cvw z0_eeZ1E30OCopU$80f>cLH`?=K^b&`7EHm#HbD~{fe&zO4tT7`HfYF-?8t^}shaG` zqO8fbKn%1%%ew5#zHAD#YzojU&8h$fAyPYLz^X>5t4_&_0&Q6k(?Yz%CKv(?DFSea zT8@1kX?9FrTGX(LsmXvTi;CuL`W|aKRz523nKNeFAJb2<%`2Cc!c;ot8tL z-s$7&X@3T&pCZGc7Ah%d?xH^Gq*AJ-vcaWhs-|AS6-2=jaB8P|>ZgKgsJgDGHmD28 zuI$FF&7!K^wv`F!uK#V-1_t=9?`8n+3h(d^ukZ5i?h@~KMS$*_fbTK@&n~8`icb(; zO>5lWFgOCz;%XyIh?j`yv1nJCM(s`_;Y(WR#R&;P<;EVqrq^Z?8Tn= zDx^q8$U&cR2i!8CxtRtr5d~L*;1X~Hd}b++x}|zP<3i-8ei|${_$PoqgTf*z!}7ty zLafA^K^Igk#%e4PaBRnZ?8k~M$j$(RmTalMEbXo=3&U&+xIhfI>{x9k&3qURe`}(9PyhDvDS^QGr{HEs9Z6Zz4!UIai z0m#Bwq=^5PZ_o|xdJ2SA8inz`oB`+9P{8TpHf|*oY@S-OZNMx>2m52 ztnR12t_nA3sfH}f+VBs1z`E83^7bw;F>w;RUoGQuF6;6x^D^<8jYiUw34jZ$zMT9$ zCe~u*Lb!@ESOO$OLMA*yD{#dV`r8y8N8h4lBbh^psqxcp%+^FYA4^#&(`@AmgbW6xfDv#5;u2_a&q0}prrq@2VG3_&+=LoU?86?7`7s_+ccaAGSq%-U`b6LATobP^M>Opn)0 z)01V}mMot{WM6=0r`VG|b_ooz5KkHpe>AG*Fb>DCYOl5pFZOG{?8(Zo3y^FM_y7?! z!T%H>sP}24PXjfq>LS+E13kb)mnhenq9;SzoccoTz9#Y>Pt?qn$ogLOj860W_6L=G zwO|ZqU3+&lI$Fxj+*%_9Iurm#FhX40vskMfQYBS7xC1wI-*}lo38cUgEJG7GsLOUh z2kh_)2=UFjvPcs+Xcw^&L%@L}coKJIZ8Uf<|E?n+uSrCBW!JJWTQ;jcI1<9wdjT-^#E00jIll>YV=%Nr{7g}V@L0AU(#j?|dC#(9RLR0rcxS#_KIo&Q#U zZHM%0^-csPO2z_=VEV}+vL?%8b`4eq!8dLlf5>%4!1Z@yhg*&cH(Wss)XhnFfDr7k zNw8ymPqwOJw(ugj5esi`tEz<)uV>rVqk~I;3%E#Ux=0InYQrD=)hvj+>AVwWoI z&Tj0|cF20{ZR7S7Xn_ZtK^q)29q>WJb^@XbYB2mMGCV^!0QSKWJAmeCu_Jr2PwszC zF2VLCe5NRTW5NtDxfDD3^a3OL)ZAnWk8SNac|I?SrFM$-TdgzCtrI1aEli@!LI=XmK}L67_Rk5}p& zcrK)3g66)0p#CYdV{+pPrks`oem=X<^X1^`=inlJ(j#!1B0W@B%up1<4D77$UOV9W z+odUTd+L+ZyX2QfbuwQu!6#S0uS>ia8~e)2v?#gF3kB?_nph zp7CoOq`pR!`1WxbXJsM808;oiwzoOidWDD{THVElI{@ZzS3f;q|2t^^_H%#tWB>Pq zfB0Ab_^;{s1E$iW|C*LNS+OB|7o0#4O5ULTAzUVPkW$-UrFb_)+)G9fxIaMH6F87y zL4yYoCKM=7VMB)xAx2avkD)}11Q7txxRE1(0T&ex=m>yU#fl&eQcQS{rAm7g%ayCg zjn#}55imr!uoJ^hpFe>H6*_dM3Zq6-v{2e2#tf%UaYW@Yb%>EASFU_H!{$xbI(smY zdX)+I*_D;k4{)&1(|2gI!l}>-SW#Wz*s7iqnynSB(Fh;T7#_{XIk0Bl1ChYMA1kjiNugWAdOVg zNhysa5=Ajp^pQ(y6Z{R%eNHHm%m&oGZoHobv1ljkma;8`Y;`|Q*2oypR)>_Uf53a}$4`qUD&Y%xSE zobl8ob4-geM7KdEV1QwV9fAlVi6od{qKYioqmUz7PV@8F{^Bd=j2t{5F@+g;Fye}> z-uMcWu*4dRtg@cTSjs8Wip%4*;8Ke(zls%eHUD?Zb4ea^ltmEbi3Voqp3dm3^8ycI z_+lM2*Lw_QcUAt6rFqnGN1II^fyB{ACC$(2r=gA-YD%e&)Y7Z5&RXkCxpu^BM>u`N z65ob14wvMHS#BBUqN@(Ox3}Aln_cPowVq*1rp(xrOE!>iK4W$VT54)>iZXk4&m_NF{!iV8Fz=Gt%t5!u-;DqcI`n`k5~mCtT*#q|PSE1`t-8ef zi`X_3NYKLyyq1y#9m_ZG0kb@n>-~=h?ow$84ZrreyGkOsVOGM%l zhqKQg5Vtsi?QTIQVS?mfrmg4+FJ?Ss4>tG}E*c)B03(qF>U{VTyKn;;;<$@C)X|r| z8Kzn2YRwQUmqay%ON$Y~V#_ucr{i6q1QsBp2bf2Z#h58eJN(@A%0oq!WXT=y*n}a{ z2MDJn;(dJFV;|f1$MsRmKIu~*)C}pzs44Pkj9j0j3=zplN`jJFyBel4kw30&5)w(% zRM;vz^mIid
taiOjfCqPH#W(27fC5C2h_>eSG2W{el;iHYzz)XlB)Jeq0KJ8a>IOO&sD4wc&Y zx@SI&(yxAhC8R`kn%KS;Hld7FZ2TS@+5A*ZvXzx=`aCK>7*gvJt2icDc~_AIj06lh zw47SadRo*5%w?Su$PT8ukpsYKh+@L(MzqQpwARob{5U5%&^fNXsRn5NbZu%W6ISFJ z_qaS{YYZ~r);pyj3Z}t_a2-c3Y}J)#>8Xb{MscV{aI%->EpI0|nO?40lD&*#sZ1ed z)21O}5cJKdV&&((sChK8nQf$H8_7|`0+_&k?I}nB>r+Ak0usactk1p*x{4%VBx`u% zYL^?c-TDl}y4#&RDF3v8A{w9yK{x;a77z`6&Xu+ox~)c30k{tPQ#Jcw2tO+Gye?s> zySYP_?`FKz4R?`XK^7YH+))lT#K4|KNr4u`QRJWb?TT?Ej~X-M4sHa)RS9GuIUZ=M zD2Wh+6YR!2w9!CkI75L5bh7~s$SPLZg3h)`RVxBG3Q~!Rf1cu0rmiO6`YL#^f3}vF! ztKLg0t-Vb_W7cg+`cx9+a!x$VQeUw%Eic*~6TR z5=LsNSAO~214^lxlLtT6@d_TKz)m*c0W!)F4&RsZS8me`_XnB`OCi+^|$~1@n8Sq>_0cNoTV*w zDFE%wygaFs#3mMoff}ac8eD}M9FJ$P1b8Z9bTr}@vgl7t!ne$!`2c0Lst@9<4Yec= z3b5gZ27nBh0k!~u5mav^Qm*=PFT#Rr`YtZ#vaRLJ>iKd=2PNXVEjFJAB21;4lv5a1P6?4khS<-tcZvX6E6&S@Bts>K^zL<3YOptSfLyqk4&V{ z6Fo;Ef=lVnYWilcw(!{3IUY!5;h}9;`taN`VrTaT!aY7|x-$ zbgcO}@CK(bxd2DFB*L6(ad-mb2qVL*=BN)Pk0BuBcc5Uuj zG(rKu$pt$H3;zV5n6DZyf)fh_)zT3I^(l$k<#9AlbJ~R>aO}G50xs&I9MWO=aO`$i z;{smbijS=16FU6IGIu@7bDJ+O-w$q6AZ zXQ-I3hG@`bAWW=yjGr3EXAVOiN-`NzvJ{eW8J969C37+-lMo02Cj}uHhW{}Ool5F{ zhH|VCG?T81kjXLjVU3oKwc_$J1OgkL(jo)$B3F|tgGa8?(jI<64`hJ{rAjQP@+~pM z32AOwfD$N=t>y-XHEVC?Zci|sr?rq!`rregkg!?)vKn#l2LCeVuJHLNk*p}PA304u z79$?U0UMg38HB+XZUGibffJPRCMf|vDYHK9Q!*{nGLA{;WOH#x!-#!`#e5%z|X zowh}-(26bp0~@HvMcooGkx-vr4?gy3IJdJREp$M4G&5SxWWWfEg#Wb0R>m5vfiY9k zJt4Cu?~^h$AxbsjNpsRNC*d-0k`SWwGARKTm_;9h6F3ReGdq$S8z;xE#~#$7yW{~i zXA@>+`cWW-CkYH-2jrj{jzvT_f(0pp7uiKjGjw_g)hyp>FFmXv2vtV! z6e^c99XsS85mZYIl}2&2=#0l=Un(v={pbp?6VVrleO;mKu@30X~)W7Vl#=P0BWvL@s6NfTC9agtysL26~yN+lByZuKVffD&?5GAZFM ze~)85wozpkLn4FZI)Wa{b|XJlSx<)+sz3r(RwE3c^2}ppuaywjvMSfMZ&OBW1(9ZJ zZe=*KOveQ+KeAF0^CC)8J@vIoTNP?0w>||nYU`6=ssC1CvD7m0ASZJb7McMSa`Ire zVIg{q)F?vp{MOr!2ZUHrK%L8v07jSDccDbczEjDF=mo<71 zQ(Mt9fwVHJsA3)BL6Ni>lED{ZawaF&Ri*T4VKrd2_j)Hm5GLUhZt@evw?45{GU1aF z1c4H6VH|`}7$Wy3g@O6VRXjnqEkV|IGpr(9wIrmv@R`4T3;KL+w0x zH(tdx6YH0_xV1m-S9q2+g7oAw8o3MvF0sS6~k|b7`^}<3VTz zLP4-0ewDH4*4RC)v6C^bB1U#2CP#{kcY}wlAVX&jC=dV)00zjwLfC_Mh4*(~HjRI5 zif1-9#nY5!PRIh&Ov^YW#sOa&^D+N8nI%(_`B;z%*^m#p84`Ijg)t$5cpPpae(jTK zL03aw!bzo;5`NfNRTO}AxgnC^sLpwvcmK>Dz<{cfKn|F}wT4XMI6_dLlb!W>pATfl zMss%k5-FeOCwnqGMH8A(VrYvtC1p~X9okh}HEEZY6CAUUp}826p&6_JMmKXcj4_*= zc1m@aeFITLBBK<*_i1;PoV{pq`I#UE!l*jJnO+)rUCuGkHuQpk3{>HG&BLApwL5Ei zsBOAUeEO4ld4R`LwVsD(iqb%(xc5$CRO{GrNw|dhID4Pg6EuODUpS&6vlL9BkcAs0Dc3Q>G1E?2~!&us|E&p2`30K*~lX!M$8r>up*%cwwdX9^^7zeE_ymxU)` zuX(he{Q9yl)*dLVBSbEsrJE`Z_a%5i4z8(M37ae>Lb3rM)P^~`(VHOi8M+HKyNZib ze~*`yQnZC}98mSDvASUGGp?_9KI@YbK4G>EIX$t#eI0Z$fUzv2uZ<>g)l6nGY)rZ1 zbGiF?O5>BR-B+y*(?6O)o4wRID?7c3YanPjDnnevG42<#z@Ec9BrSrx7g*&`ylg?a zos!j4|CX$3St88A8c-5xbN@2Gnfsx=mbIIdtcyXT4Lo?uN}!K0TNu1Bsk1Or!WTYa zeZg01C0P$jp&9lNP3F}aD6vr>s0em@#GHF@=0Gz1n;GRT(K77JQ+sZM)2Q78?OQdDmpzB=*E5 zq8VU;UjvphVIdmITr=#dxwIMHF%%;sLJ1B2KhWVR#dwN|*H@qFPa07p3V=^RbV1~C zvI+DFwbh*n=Bz$m!96r(*fli_1Grv>&uf#OIk+I^m9NwC!EJpYupNly!6bd*s)gG= znYrS37;?p$7s@)3^<727)68WtX!yj&rlAxdS{ZMlq}?1_XMUXfdFpe%i=u%-?VZ;$ z@5W4C6Dy~T8UFzRm>|YfEg_P=ylY8X3g`wj;e%U_VXA=X=c{?6}JA7YwYk6TDdVXvR!tW>Z z)Pc0k6Mt~+Ax=ADP8(l>J#meT0Sxv9)_mOxBVzN-n_@e2U5Gs{9sDzeCNoZxA8iP9&A5F;|wBoSglh6xomECuV9uw>TE z#j|H`pS_VJQ>K*1lBLR*Fk{M`NwcQSn=R$tbE%S_JYktaPt7E2|I=WnHG!(Ef|a=I{?)X$K8 zYv0bj>l(gd6;$m`zP!rph((V#%WDANvUUMvxe6WTUhsYaOE%9xI`+=$Ek#m&U2S&K zErL8mTPY^(lO0b4ng?8fFHr>{g##LPkA;iH6OT8|6od+Ltwlu9i6sst)NQ8lB1|;i z?Eh96PH#onpIZ~kA#i6UC5%KxNp&82H%2KXTJiBU!g^C? z855L9wZ)}>Hfn{90pJ;64;f?8g49YGhIt=fej&-&ms*BKCwF0L^~k&?=P$cmM=} zWwiZ47@c;>cq6wmQX3O2y?1DBbl`DjE2JX+=NCNtJ9PA z4t6q+@#xb`DGf11h^H1MbjvvYpcLFa2P3?*Sym}4kuL*MB^); za}t}E_Z@hWBncf6(N*mvFx0eXn-mk&R`GQ0yekcZSjE^A)&K*#C*I_4oBvI%9FYPY zAZRg#Q|M?_54<-`)oV%imM|4Z9Y)qQUtXRBn>9E=x8R!i`WV^9xW?n8+IOtjTORMa zwTA=!^E$1(>PzGBMZPGaD<+kq8ROW8NwQ@RYfy(0vJhYICMY3${0=-^$Q}hZ_$=II zO?Utxo;^$l%UrZNgSb~lkvT$BJ>10Y^p&$ZI;uCq8L@7+iD%nY?Q3#WqQan_e z5Z$5_I!s)rK()&Ps)!S)@Q5{Hq{Qag@kvG8lKl?(o7p+%GZqrgKKya3A1%a}`f5lX zgrvph5UYR5;|d<_fsOC+k(ub6l|3K{9z9Gho$&NcIM~S-_kb%LX`y7BuBS}W5Xgo6 z{LNo#C=YX_OkSHAiTAFF5kcHyVT)QBe@OO8l_+p<$=f;<0fwQ5YKC zXr^MLtBxWrNLLi5L1ow$%`Me8DRm}X_7RVlNsLLVG>S4E#hsp##2i5Vt7MyL52s=S zb(77kR%kI7@Ejn5n4I5LqDjZ9WiCzzFvZ>?3i1w?o?2~Omp870||D;XFe zO2HMEwu#~t6~~&}q#|R@ASU`{Sb1G`1wOYWoH%qjTLl&wa2VOuaoTP=VRsX+)p{dSEzmX}7dfw|90?Fbc z1f1C?1ffNe5tv5f@z>y*cnc`TY`EFfE!6rjSwU8kl1%KaV6N)LSm19y-mGLO?c_~;8rxp@hLdH{){|K-Aicw|s!P0|i z*xD(xjwB1cCe5OflBvA2JWMPGFJwW}d||kX8o5p5h(uW<1*UD~6YX|RyyGRyaFb@_ z$v(^>3^SMLX5H1~dg}~euG}qePaZNl{fk7{U1!1Bz?!GZ#Y z(|)X%j`EV_UCk^Z%~0ZV`nI!TjR%b>mPwGC=>Lmb9yU~(vTLYTV~Sf8Be{>M+$Eny z=^GJx)vp2+T!bhOUra1WrK6z4J^^Y)4=l<_q7c`>g85hTJH0vI(&TmaK{pMt>WW6z!5` z9JR=WKM~Aiz(__S(>Ie3^&jwpiO``1OP<EBC#AMX@T3ezeGe8;zJ-M~EpoUsH$)+AybTtU zdz6#*bGI(%TxdzckDQS_U$4RDMGiO;`Ts$UC>?!@YDy6e=8&C(5~IY>H5xk`QLO+0Edr*Q|vx3sfYofStc^ zMY4XIj9pxTNI53={EE`!^DgnPg?@Q~4N1pP?K33!&~IlIcd}v?RYx1Ur(_i5bj257 zNsv2oGkpKzf1W`*;KzMpu?FkqQvb;2a=4-puhCIW@@hIEd^5pB&5%Acm4Xzb3GqiF zwy_agXAP`m6FX%rt`bv7l!0LffNGT#F_aRw5+rU^TURF}m(^ut=Vi6yfJ_pB+W~$7 z=ui3)HRb{yl7xkw)_|=7N{Z25AP4{==xt+IT7LywVC835XMRQFf(&CT$#6@2!h}|* zI1Q0k%`itC({?3+EX?IUtVekx=yFhKN#u zQi1?sK%T#ZdN(%Pp-*hMXvx-9NM;VCw~!17REYl!6EA{AQKHyaD#d%d!+}{*STG}pv4I?va0?*D zS}kaY`?iT!$beZ0icx|MSQUX*mK0IX4nGiZM|o{Wuog)@G-e5`qLC1le~}Rgjs5 zAqS=+bEFUFQxK+r5TJ8wzGPRr22vS;8$1{f_#hzu)RO;YJM^G&tVXKh4R zoT4k5!M5U>YNwX8?Fg-&BFc*>% z#Ik37)+e4o2x^B4#jqQ1W}7x-Hx_Y7Ncn#x1Rez-UPXAI_DEvI_8kB~NGj1D000GF zSvlHro!yt6beD^~S62V~U=0lyDx5f64$%>gb6@!fDKL?V6J?MphL5ONBeOFSXLk_* z=TRNSc12f>P?a*NaE?dkpj9{r)bfuW`fXLnk(`t#kW>I^l@h-*0O;{N(8Zx4cw})S zXWD5#1bSu_nGv9a8`BU!&h$Ub29`(&qhHEo;)5g$ltL=uxdfTj0s55<#A( zG7fSnP<51J7eaboI$iGBr-K@lx+!JyIVlT?Hw;C7+ytC66kUEAX(Zu(f*K_^*{d<> zLBN-fM<-cg6d3>bRC+Ig4QcWc(GZW6T6!h;5|&z&*vOGynW-s(4aT7fGRBbJsb<0e zETf4`{9~hP(_ecttfty=6(n6CNmxrLheUA;KlCc9i9Ph9iS_`j1uCq?H(=p7LGO5h z;86vcv#5CHum`1@hH)PS5Cn4Y3wjU)@dyA@>J`~qh7G8ZW7D4S%BtAN3*%Q;oFbkK zp$B?U3cQdEmz9wIqN?FmcS)t82&-fKlyMnya3n!k(@?9t1S+6&3xCpq=tw(Z6|8va zEcH>Jnq{eA08UHmt>p-rBr$V==QL>Xv0AG=B1=7LQ?{7)mmA3?kC7F{@eng3W8NwuXGHQ61cA2z?Uk(~_NN_bwxWBsqsyLb3O7e7M*M)X<2f@Dp(}VW3BO`{_J9uu zTQaStG%Uf2ZPJqjdwVw^kjuoqlc0RzHT#3bN7#XH_7Y^UkJ%e|N_rL} z9LPUBS(vsvYfKZ&;W>Qh6c0xTZr}tJ-~b`80lFLlA^-w;kU&5@wx0Zd85t@2Fb@BR ztV81pOhn=@_!G%Eu^F$tL6dx%FqCl*Odec27UI###oTvSmJ){9lZjcWwivd?w#_(_ z!o-Xf3*;fMd6Jwm20!2c4zK|mKmioM0{k2T6;J_tj9I2>JJQTv?3~8CJ3UB~6#hVR zx#Uq07e`uzG6h#M>-obwp+C%Y%{v>Es+=@;#}|25xjP)vol(yHc+SoWRh@;M-#f1& z!>1zmi2}CD5)`1HMviHko*A7I$~+I}Xh-3wYoKIuHW+%mPeJ0UDqI{fyKVfCoKDXKwh(U4_zCMIHzPgqHVuc;nS? zR59Q9CpN3DYopD~#CR|nqdp9*=0;NRUMcbGTAAC4>Jr6bASXWumK?u0{+a; zslCtr%+ISG0#2X_L$VV7vKiGt+a;^4wC%_(;S>H+64-#Uxspxf2bcfg7}H8jR6=Fk z(>t#k4B5Sh$$eysmCW3B($;4YrOsQW8+a_=J)!=aZAx*yZvo!|xTljhj_Dob0s7gLgbImF@<@vzkiu}u=KE4mUSykHJpDTNb$NNQc;420r>>6cl{6QwK` zYI1lg-reZ*s4Ptqk_rIT$~N9TNbG4^knUV5(HzjMo`9kskIW(blTt7>Bcw~WMr1TO z457|=zfe9n#4rn#fCqDc23zn1K=1=PfC9j-<^S#9|6S}NQ0)IKknGA1=Ks9Q4&dcp zege|20}z)Ad8)q=o^#tA=*Y_L$|N#&&JZW68n+-1%^@|5ZIs1Lqy}x@+JoqcP7)XF z=uEz7Rt>F;F0IsB$T4x_6q@bMQxyiql?%hX36rRLMnk-TQ3o~$aDWC_-~>QW11JCj z79Y?eAne7y@f_dr#jel0JnY22%N0-qFVFx1P5}T-Gv0U$Ib%?Nd~?+e<628{v;nF_ zQ53c(Y=-`&gD%12lh1J;O6hIF)h>w=fH6j|ys!3b26ozCaAnFb)4T96w@T_jZ5xcyITEQ#cjI z33vbp503@BPVr3b@rIx58!zpM@A3T{0xG}w91rYnKn!D00gfN$D!&0Vv*(nMYLXXA z=jH7;-M=b9fG-RYg=-JZanP~tH+we=?9SGY)vP64+oX79RuLASRgZ7I+ytu@-Rx9e ztC(zTso@>KL2nPm&;uyI0TxdQe_kuikOycW@fFVj9ANpt4)VpW{Qw=%mcQ}AKK{U7 z1LlAJJpcsh-vd2x{uB@FAW-oZkH_49_yEoViT~xv{^io%{~HewELem%5yId>g8~(v zpfGR)3KSK3%wZSL->eE7C~RcmV1uSfAwyE41k3;4NqhJ1$&-iD*J3wLT?z(MK`rn}&6PS3%;TejTi zmV)^gFygq+f3}S-z3<=ZFNYQlNeJX5D;_K$I!K@paLh5aflljDHHCgOEjQwR z6!OL(afFeD4Nib9#~yXmt+s?>+i`;mP}l;Mar}wuKYO4tje-yyAY>9jJW=kGlJ;wF zzYNy`5k0l$j1ww2;d99wqPR;Ar206aq?Be}vhz;8)Kl-xldKU&7+-|3#y`#`a|);V z!VBp>mIAcuKQNXaFN|u*T}- z>(rbo9H#;noGk4HEWC_|3eghUAW8p*oZK_RWR^gKZRB5G3 z8SQZ5<=gD_)bOlwa1)W^1zrRK`7zHoJ%id)uvlQs&5jHJJo6&UrK=`830Mu zGrNy#eR#mA6j;ne1AR`5;h>v#TF#e#tt(iYhUTihw&D>DN70Ztsgw!~!i`&olDziY zY{}&|$alS6XzjPrM!W4H%|#8O(rm;sY!qIo0SF+FK*9+r3diDbFVILMk6Xq8E}r*V z>W@Vb(#>s~X-Xl9l23&BPagkIl?EAMtMhU=zNc*(wbQ-!u?80IkWx<2@tR?(X3uz$-bxDO}&2n z>zQszYPzVN-of?Z!g{Y7re!iTrjRHtS!?CL615}K_A$z~g-b|gvzu!$0|JQrO$uIc z0~`tl3u0^oaxTFKKD<=HlXwg&t9w&EDtDt0pg@B1XvQssfeqY!4=~}wkO~i^s70y7 zC9f02Bt9V$kKkS(in~Xw< zVtmeeG?7O*Tqwk1<%R!1Cs_;tuwfwFa04DRfeJ}k#2@*J2XJB%Iv9dZVnGDmdhCG?8Ri28qZ~*Dwt)|= zuqBPyyN?=el}nktjwH%CQ^v+KDOJ^E9};t>b%enk<;d(i8>56N%+t%q{N;%u38(ZV z;~&^fLQNwng%k(5urGFVK>5^9G|HIIS%pYe`Xs1J<^lj1*aHD&9LxZq1Cu|h?8k*s-StCGHu`}f`B%dgS zHwbAwxrPLKq_W1wG@-+g)H9G=EY)7g*Ahl7<^XGLCPpp$*_j+Pp$lawT^zcVsYGMWs)=43q?3JpwYEP(0W>;GowFtlw5WpNOQdN;c^(B7wnIrc zTFW$Ut`{qBlE=m}c07@k0vli|&^qHgiNyxe9Hr>a%uLcVqDfD`cu^sB4muda#OhG# z)g6b|I2LrAE>A9D1PovR276>72uVl+6PQqiDr6xGU;qOdT&P;H01Z#tiH6jK4tJW}qUjo|swiU=ijkueYsFxyOGX2R&Nb2lb zedvB$Vsp^nLGoY^d^BSYX`qBX zmsUPPG8v5lcZsSAVh|BbdBjzD8g}F%Zg|srJ&a=6m+VoD-vRhcXA%@8Zh_6ps#~pY zv9(KB7LWO43?#RZhd<1-uc^%WDA)kqOUA*&uasgO2y@ebL@aXlR?29E`4G^JZtDM@ zs3Hk4kiY`mr~m~dAOXKHi@P74m3X5C4^VQ~98O}~Sqt&h*{tXoDI{=v7~EP3*RrW~ z{7my5-6bWr2S4Jmk9Y_p7-^@SX2T5kxB7Joh)r+$drG z668vevZIj18lPibN~)H1xxer!+#`%ELJxG&F%2h4C@o#QR~D-SMVVZgG@SAfRwjA9 zRNmF^*nN+7(w#c(HF%9d6fXNMpZ>C1fn1l-j1)bi85Z8~l7L!*HB!M^`E&2_J2|6m zHGc;m;?sj=KJun8Eb3jGzNVoDhr-00cmzi@_bc zKA5luHc69tswU;g15?OB|B#1d$e3bqiJSA2LfoOLng?8xw6{w_voQY$hiM5t(5(7X z#W55-0jxyQ24X4*EWkj737u2CC<;`@_DY>Pyb%px0X^)) zcuPfQ`nRw%x_Ia^%sYyi`Gm&t5*RGCe;YMM1Ug6Lq5>HQW&1Lc$i(LJM1DzyxSAOs z6cwvTJrGf8ZG4pX2DJy6Jl*N`XxE>+NfxJq; z0F!ta2NZ(A{qqTyQ9-q6hEy;LUW-07qXc2_1}##-&{Mt17zbj=2YAVcGLp2P68pwwx z;zvY7HNfCYgW!N9<3IK+i+D&jG-HHMbgl1txL#7pE{p%2?;r$D;5alBh87gNFM`5x z*a8Hq0p|F$w8Flhvzo!}6nD<9*G!c;BxK=lsfzyn`!E&lR67o9jJ>a*g3H`_d;9xIE(1Wtr0BZ`Vts3}!) zOTI+8kTobk0Pxh6JPA<+B&od7Q%xkU3n5x%PgwN^S(w;c_!wA?Rgt_5>$+4IYRyT= zgE8gRW^9RVEedLyrz=t!Pos?JNzd`gOX1@L5!jY2!58gm#*s@AX%fn6<5Pl33UT_5 zB6Jxlf*20wfFs*xLV#+opL^3d_#0$O5_`SlMfiNMlG}^gh8m%q-{j~?Tby*wTi7*fw zhd=>hU{dK22EILkBKTU`Q%@+w-{WMF;@bcqcrz=57#?J&kbngv6rY;22@>?i`tUf) zTw;cSUX?fpO(3aNorT|`r3l71=VC6%Nn}M{WJYe}M*gjBn1vJ81XnuYaGhjVf`)Cd zhGy_xRG{QJfCD!O11vy-XP@pv}fo&)=02aCJs*ajCNWuWiu3h5vm|n9G z3<=<@r-)*5w2-+yi665x6u1Q~ehxHOHOMh2c1T_}6txHt zmzV?geNRl2VlmQz*l_;?s6K%Qgb8y<10)Cm9l!!>fCH715tQHsDS&DnkOKRd3M?ob zUJ!;c_<jNUZ6nVZolu2UO4q3UB}iXn+PFlT1=&(KZddNe%tkfN0c+Pud99W)c|5 z2u@lJbGc~LkR%Wih?A~>8dzpPv`PorQNoae3zTV*Eo$_5#RB94vxwp3q z4&Vgd)(cp;jbiYE*!Tghzy>GiVj#eR*cbzpfP>GyjWO7XsfL58z6~j82`T8}699|6 zwuf8b>w};I_@)1B=~Wny4T-U;O3yUf&e#W%IhhO%r=lnYzXhTt&W{e_gdgB+3jgee z$d=n+foNO^xgi@57YGdqaS@M35eIQREC|^UAl5F9E^dfv(I5XA?QvOUDWRkbZ-@{& zrk0h@3_OWRC5-&!??XGs@j>p^i|H1P3FhWrzi5tRy*=2VtvsmiywGkhwg*(W4ZKDP zE8p((CU5)B@UgxPG(d^=_F^XxDU)ymDVPl;kb(y@RV?3Og9wBDCT^-&$_Kv3GHZ)` z?F^S=V3An70UZfpfI+sPJCp1sWV4?#sc_G}@M+0#4v!=c?{ERi^be2ng(!{{j}4Ac z2r{wl*P#FN;_H!V*(AfIjT32P@YB zikj*Wc(7Zz4LCrFYfjFS-~>~5gF9C;TDJ!a9SI-AE3{}WW2@u;DvAyrnYv@!K1XV? z3=n?s2g?Y7&5m@>Zh#=^Vj=+-O~3d|r}VK&78a)f4Pfot2q1BJmsPHfbouDJxt}Ok zmMM>(bW*;zkOiy7+FZBTdw1%gX3J`IZhUkJ23)lengeJri&D6aL$?RwI+dQ}p z?8N_uYPW}IxQ+akda6H(sV+t{j}0$y_t>cV`rbMa3IU;y+NqUap63ZXMYEa24yk;d zjN!GU*qB);?3@I~(hWMe`$^#-2p#zBh^K(hP8YO6b=N4M)nN5@ahDZ8X;sCMg6NhY z(EwAgB#+7xhzOh~z=AMH12>q1IY>=d;Du-yhjZ|Ud_aRNad;eogX%NKo9|wiRSV`M z3Jd1&%jZb?A3zCCxQ#e(W~G0L?>+(YUia8I9P6Lz zGPj5Ic8Tck`jdF~`c~?(#{+L3@&xsLlz^{(D;4}mN(x=wqnIn_LBv-x>Qhu0%4Yuw zf4GHc5q!W0hz0^Tgg_C3V2FeSIdCwT@FB#A4@F3<2+^TLiU&7vyojPgLKFlsDnx*@2SzcnHwqKVg2o;{>K>bmo*c{aFP7*swvYk}YfYEZVeAxneEbR;^X0Z`aPH+7<56x)Q>MC6l-AQhDgO z;{8cj-mQl$9>S@5H!U_6Cr%V`V|mQv6Igs=ne4=A=M!V>QOa!E^k{oJCQth5WU?2Z zl24Q>T-HrO$qHL1we7pNS$X$_%e{B+H(kJMoyMay={a;tMw1SAYR(s@N!kA=dC~-Z z5+zEdqEQ_luBmdl>es7B?*4b%aWz0B6xe`*{S7EUfY@)c27o;X2n2yh80f?k2|ghO z6%A5>#TF25F`*Y=SZHAw8ICaq8XbE0VTWm?K?WHjnrNbBbEt?%9^%2mOEcq4_nuFA z(6?9!i+PccRLWfyn2$6H30x3-fmH=?Ln^r>S#Lc?7n4xQV(;IXz)^!VgZpCYn8^OvZv`dFEN0YOJ{in{1Quxg=6!D3)Ug zs6?68lyM0++;Csw)u>%}jq_b}c~xf*Kk*2&-6Vu)q6s12g;z{Z@$mmM)jjAOS1Eh3 z0>)ODi~44zA0TiLfyNX|N;|EsjxkAzK{&Fo z0Twetd6rgfy;|*%OetU_0BJ}{F1s{R+GV@#^;qRyLE?oaUs}E^rM=17tCTer#b#Gw zbE;_?Cq2!PnKeFnvl^YnWQNUD5cAg4oCDi4+n`D!g~dULCL~O|j;<>gKkr7`Z!jAym+?+J^1xRCV&K z+8fd2TG`CY;7tc`D$d3J9W2)TyiZ(7%FXESX%pv^KQ<8~azYi%;g3(e<6Zh$t-FWR zEC!vBTL?(-$SAW8G$iOk9;@3p{0;~hS}5! zn-ULbYyd|OnS(uCg_Us`Cwj{Xo)MQrjBG7tF+ku^+^~qAEOw6<=n?>7$hfHB#jQ!| zb6vgC20sAE?`@L$o&D^gzZ@ZfNky2<59Tt7%;bS`e>6=I=9C(F7|b**fW-yr9IKJf;vGawD5JQO_f+{-BVT3T^VL~8X1&Qbr(i_!C zom~pY9`R^KL^hzjp4f6dPmvTlIytvkQ3+P?`IG!9OyI4&@u$C%UGsL*sgt4 zavIf~;#5{sjg-gcvW;mX>Y~cLr4_}QmaiCT2TB#|Ps0f`d6;7>Yg^7)(aMx?fFTGH zSO8uXpw|Q>;Wz7iUdfOm4}AP!TM*F!8L@K6#{9$_pMu5s;1Z5#M1!VEiibp40EDR} zQKLSB=9%na4IuDmKY56UKXR#-lf|*6&+-2bDq49vO2A?qP>IxW9#zF-0!OeuwM`q* z+F6n+Wf~wso*Ovpn^xTvs#Yz|bE8YNl2+;|}1Y zD%>*a*`EBuY~cfo1pJCFFf6r*i*iB}4^_XnGD#R48Up}(QcppVcR9d}X9%dk6QEvg z3mia*eu#0D)KPb6tV0SmxbxcEK?i3rYld>+^0T$&wzFNaYJpcfrD^Oj1ak;+ir4Fu zd}s|(DA*o0OP9`S2`2&QB7Z2Y7G%LFfY`J-N!gU zBALZh01P<3bh*|NjXH;l5Z1o7sW?%JQl!EZuz>0QNv zYNw4XT0}oFOl+}lyrVZ4NNY_K>S?XitrMFfglLNU_e@))G>J2(EWc)UOkH&XLO0O-?t z)PeV#t(X_&J#S&)5HjrD0!l$HnK`0Sj4#JK4_ku>W5I4Ke{hik zqCK%B=RA**!)|)17A@yWK$P5maOB`&%!rCv>R=yKe9QrP3BdzK$rgKm+~`gf*zA?p zYf4P*-0{$XyyRbov+(~=!nSbPZP=%-6wf!^-cMI}2uBax(w}}?lUDuuXnD0PpldAr zn5FRJ1#Js+dS1KVQZ@J(49I~hn1U%V0wd^w9x#HF@kOi&URrR1?=40lg-P(`U(ggE zf^h;yG1&hB4L)envavt~^Z@g;+XuQ0MCly%WuFUf*d%;H9>~Z~aF#!eLo|3FzzLuD z^~5;P$DzC!(j8scq>tFNUqQ?qcM%!-F-egaiL2Ef1|r!}@LgIA$5yON(%ni=(BG0F zKm&%w1C|adR0JM?$pucKM)ic;ATAxB9ok$6~-77EE!mUp>K@9d_7MS3W+aNL}Q#-^vNQP z_yZc$SVGiYkny34+0@#EAV)|BLDT>=Km##kV>3X5HOL4}AWO1D4RA$8JT#*1I0yHE zM|jkXhauu277iS^!G?GN7GQxCH~|v;fDVKJ1wdFwKtz6p1QN_dO@Ws8XyFbv$#IMW z2e5!cJ`tlCBP$vkpiv!3Y>QXHpIdO=0H6y1!QwCKSUEKlp~PW<9RMx@zybUQFFqMy zrJz|r14;iRM41qyElK2(7z(1`h=es``Vj|K=tMpc14wMZefWw4h=hJrKtq^>Q=$NX zq=Xb;fftN{8?=N=P=dC>!Ytf^Fcd>GSVK3CgE=Tms+eU@91Afu){WB zqc6+?D&RppjsZQ^qdndu5&*#u=%YgrL`Q4@upB00azsDo#zQ8W9ZH7bZ2$)#-aarF zR`r&rHARUP#zHn^b|nPpB_!+xSH(?9`)xoCD4{0GAxsJlHiVBkVAb7$*(&BuNg_um zR!cO91tajnFBAhaL_;-TLoKF7OFAH6SjDKT3q}J%mYckqNxXo5>HvTC zXMer`f?0%`JcMEPBVx`Sd>$y%jG05i+@0V^1xSZGIU^UM1ri}eXD(a4y;XFUj$0Yw z)0y7ibrl$iCfh_^#2HBetO{4$q%PUdhl*rcQ7BZPLr3c3k{qWPCa2KQSU@HO6&S^< zjHHrq!kr9BX!a)m`22Gj3Ajl z=m0^)(rhV?G-;SNrnWSuVl?OpG?9KFL9tm^c4S zCv~dPhxSWFim020KnLIwVI_fCyLvPn4KXo+_)DpR1O~pEdDMnf~sV2sm%-40=EgDH*b&|PS8aEwmwa|hA0Klf@XfK9n z+Mt>70YR=R#bz#vkuJ@U$O2!EO+FyOt%5*_>Z;mM!i}(iee_BL6f3Y=>oos0D7J#B z%yuHo8rDIuzzggom&R-t-Rw^CEP`1p%$7u)9u+4{O*jn?ktM|n<>^ygXm2d1yKZP0 zrG-SDfYXhQ=fD+3Zr;mbkHxYbzR)_dU2H51;6GY3~S5s<7ylg zwW_H_xG9b}lANmP&q7$pJmfSssC#Na%__!&YU%5??qbB}Mj!-#B*fClfZ-{Rzw#5* z;%5EiR=^>}IatrT7HaSeSY{M~+H~#tF)B1s?L#g`*9_Nco@l-?MW+9Sg&xGKT%n>T z{j1c4|+s70yYHj_`pH{bB5*cf`38K55U3V7DhgFkmHu@+G-^JV1p*{BYu>y z1Dr5OfW%CKDeYow(T1t8{ap;to$<|Vv!Z|wNTnBCB^=;^CZNJB5JR!#LmR?eLW}{9 zjS)5+tO&iGirGW)RxP1&Xz-xHon(f`MMVbp1pg{7R5)*FnH^GWgYo7@;y7`I5viHU zL-UTnIeg&Y4&1{O3DoGesN_%A#}0gerFVv&uy0og71)GAT$aGEuf1YcbeHM-})|-)LrjWB!Q~2+71Lk z5F|koFaZ;cmzTT+AMdCiUkyHl!L5dXZMCRx6tZa`)IR?)vN<^NPed|fMMI)RP$y^d z0B!PQBvK_0FpbdRCY5Yv=Gx@iLo>{Q8|>_|vF?q9BeX~jID(^GLPIm$f?aZgCgedL z;DIv30a)va8=%2|Wk?u!0a~YZgse4RqBVzHrHTY15EH{T7|T@iM4y??tO%Mts2o>? zfD-0wJ8HwHwS&qr^ZI0vEgmkI$sL;4UlU4vUBndW~1xqIsc|7)H z_!>;hv`^5qn`8zh3`QwbvQ8^$<_wUp-9lzea)tk<MRIAOT&F2j9Epryf?J~EWa2esP)7x_|0O7z}TMlcS#{{g{{?kGgUA)W!M8MWJY!R z1c3w6O)C&nSofW{^JJg_Pg8~y{Ior|ffU@dN&q#8{=_Wg?3zx_P1ehK{BEOR1zGXM z+KHazsJt%jdaL!M>$q7XFtWLLP zFw{fw-}UChK1M`$!?=KR#p?8gKL7zPduw-Z2~K@oLEl|YJ+EGWjg%cm+w@Y#63kgN z0}wh<4=62`hn0UX2Y^rU)s}@hh;!r+lu!Re!)!RgaC`P>$ipEy0mYaDf$IQGK}-l# z_jLyfZM=ud1?09kU5RhrY~~+v>;Sr?t&8W8+Nu%A`4o*Z0~t_s5`aM#*RfO7xZkSH z9AJc`DawSs8A~(oQa}vg2>A&RxpI3$W?+(HV{%VuvXf)C1)&8xoH+PGT10$N`b8~M zz~WED0}v?N?ACzN!rMgJ+fz|hZb{XE{N-Ftc>tJ*DhUH5qZ5E z!Q?ii>_g1EM4lOQ!YlYzMQFqc&lk5(Try&9&Sc9%X7FV_LJWl;xwAhM#CO-N^Q$Jj zq?5?_uMq}6gwN3Rx8xp0Z9a6(#c{?9_*yK$k|_WlSqZJ*IARM)DV#Jr*A&@G1nUEfxR$_F0fISE1%jtTBDtvDzCS+Yi4~6g*|*iIM}(Jn#HZ zNVYsA>A?tkL)pX92Y%fBbqK&AQC;tjMcqLIggs^e2o?mu&t5`>3mG~lnyh(Fh~%LNR=sP&YV>tLapFhPXirbLNyzCb3y~q79!-diDjcLbo{m}Q^eMK4l%9rANcO~} zg-)J&>6la=Q>92ZF3cx_YtW!T;)(2ec;Zfb81pjb7!SyX4JZKa9}MZ9 z7$Xl=RyWhBPcM}au3Ux9?IvXL^m^@#ZYmEIOw91@g&F(ScJH3CLOC}97*PKnKgIR8 z?<1}l4!E8I%x|^=2$J9zg_yAkfC6e8fxeswoQ)WuC_5o2s}|#nHndK_Lm@b*3J|qPTx1Q22A?_x$C!gJj5)291I2!QB+46~ux1cWb zPbv2BLsB%)`U8nSq|C7hnxcflFa*MIOOni)GK$AF!BFt(%8r^NjyO&dnu9896!Hcx zvx=e0#?9o(Zm$2j6#DLj@Km$NA1qv(EQAWi@e@i8H7irRyb@yRCK#xsl+!8gIqtnS zJEf=^f(`&xqyz|xhBgRql5kXyq7iDypwPf)L-#xkK?*pqpsEOtN<7h?UsH7B#jp|* zX1E`UAkIb{-9j<0Ku_q7z#thV(#X_e4K1T3gCYtxTo1!*(nvw;G)u!iwTKumUkd?= z!uS#O+bONIhn+M9V>7g=o{|EOf(<5AiiCKfN{W-rG1j?H3DwY^Xr#DU7HWC4$4BqL z12?sNoZxLp6f)Zn-;T@)Ctr1u3W7c+?rE83@!YNXTX{?&CD>}$!Ef)iE+3|a2QH_#z7yxbP;Gs8VPG9guOB4c)%@g^Ew+J3j4 zAzBE6!J!=p%6K+c4T6>U+>Y*zS6p$Q4J{{bdGiNh78-UTSU?}x;-|Q_2No%G&;g55 zZVCIwu{E|9+x+4Itkw{m==SF59|{)o9I!)R*rjR8@jwWu^?5>&}ULZYEHU^Pc`@_18mq-GD&_2_KH8qUTR$e|!1ttevB z%K;f`UH}Mlp-rkf#`@IVb)X9;1IAp#R&>A2O5gN0wlPhd=X2CIp6>_CnRGbFXUJZ zg<(P(e&k5u5>juRFt{BC@-x9(!3j#gjc~LNc1Hu?1ITJGy2o7Ty1I6TFtRhs99Tw`%6S`$gVd5qq0UQ_IR#y>p z;87y@6do_zW*9dJ<~0=<;X5k@H`Zuyp!%FmQnYryPYHt)uxMjX!V!&VpsI!z*~a1o zfVp z#Tt-nNPH}8LVSuJY?zQZSiq>^#-UEZBz37S)#E$|$h02*(SoQQW@7wfj;ZnuDNKP3 z$%4WGJ27RlZWSv>%c@8MT@os3C`Bnlw^`f06&00nUE>JL$iKoZBT$H$Z=dVG+}`fG z6XAsfZnA*2()3k7LMc94*CZ5-F}IbqY#L~!11Ai$f}O1uQKaxu*aDAAqa{~q-Qx{e z{Pz~sMTtMYNUIQVAa?1(D_AcG4I$Oatev70oKB#F7Ut)AtK(1}gtDy-Xu^P26>XL* zvH<#Ufx49v@!_)j)f79oxs);oFZ4hQRD@W-VMQE!U~-e7rL@I=O>{y`vgn6EK>-R#0-18W zW82AXzXvv#qXYe(Z2+aiLBYb4ISos~_D8?G$+BuN>lLCA%1~}pZ9`^3y`l)N(|Oxh zybK%y6vzq6KQc!&ig9C41)C&kAenNTv6xctQh`bNWs-AU?Zdcs!S2D70w`Tm;&CK^ zkUemMtzAKDfVx(rzT?~a^27gGd5=Lx%p`$jI9S(CHIuM)2q^znZY;s=7PA!0pOKyG zM0=snD0ozS{Ec1NiKH0x89>7;sKAw4d_n^OzyL~JY3ufgWJCeMqu%xG)H1ahfKl5P z?3-;$$u7PatC5{n#kKUuX3$D=lQxrkxfT%9N;VgUz8Zt{~e zRTy8aRT%6msbZe`KBHY6%>@J(HyD2E3^7L<0?0mVz`+SlfTY6IS79m85DORM1zJJ( z12`N94hYAj7Xo~jjvzdLA^5Y)4Zbf_G+i(oD81c_N%jA*Lu9t*agx?)|829|W3L54 z-tEOMUb{IOZ>6RM6BJO80we$d2sps>*FYPOWB9E-*=pQ{0s=hewfFCth02>W2L5e* zp}MS{Q)e#kp1=a{Gov!@KAun2?`80WMeku@0{T1DQ>|tw{2~7X0cr!X&H;fik8Xscf}+_hP|>mq zO7Z~&G>{77;a)mWz?R6wUL_+6#{_vy&j1VC@&yG`P_bAry!>Y$;(-cKfC3OH1VCV| z_+jS;kxV#YZiu2q3UQ=FhO`FA2vy1)3{PUBP$8m$P@I5Ou%XV(LLRin3Dm+I^9YTn0u2IXTCBk(HXhuA^o%Hz!Xt^^-WA^6NoI&2$A&^@pLz$PRbH~|}9&K_7} zTZjS;PKFJEh$F7ZAQ*w+icHPa4+7=znAlJiMgRt2U8 z(g+&}0AoO$zr`#BrchR~tz0NYQYnUjE}w=mBz&O)^o}yrO|Zm{856=8(U9a~3_rk! zK%`~tGNMZasNW)Tu*5MTeo1xgj`cLGQ*5t^j_n55ksTZ2`uxWqs2~J%U=oP}1;Qhk z2udMgY07T0Q=D-~96mSbw3qJ?a(!k8lpeendaz%Tt0F0K$R&ZlGS z!6pBVc9cRBW|B7~!wrn^D#_9(DN^^~GNeW_3#kLB`cff`C6g9H|Kf+js6um8sW1Lv^zGR@?=)C1%2<2X<=F3ZFm2%@h5 zG>wcYw{mmM&_GD!t^z2*dY*_iO$}ru>=}6jz7$k7CeqDF@i;pIB)ii@jsvLZvUJQs z30a^zsv|tWvn7M^C96as{2>gu3I%RqJqHvAU+|x(2L;At#ngj7XCf0P;z@7E9{8+0 zyhIF%6uX284Cq4wQgp-sPrUl6iAYrcJfuhuwiE!?Ko9gl4%Wa;$$$*Nz=#;J?U2YG zN&y5m;42*f1>$f^$P_Wh3`=K%=-{)r66mi?v?5CppYD&sfNjD~z)lhZPoi*7(jp>o z)O0-NB!!Vj69ON?ErKesCoKX{^DM?PVhq%V1!^Elc&Z~NA@blL9N6mwieLwCq-1yB3Oyz2T-XlPsALkVp#2Ae7J@ZuEq-Chci`jYoO)?1`uTUm3k&M zXA(~-Vdf?jYh3v={hC%j-K|ZKX>VdB1lhG9RuIS9mE96z9KwKJYrqY_u{?E4j;OX% zh?XmeVn0Fgodhw&5IPZFXA| z-=e2=Cj)Re;YtoPh&Tcr)~+`20qzFYOC!RAZX<-AVxUThIJ8g|Rfxh^fZ%*~V+s>u z%4R&*;~eAaI|4E&%#RG0(LIsi1*$XzoDg?||DyboWD?2}hlROEOlI1xvf|N`t4##3} zO9xw8i7H+xmo{VnZYiNM0LRzhU_*EK?Zdv{*mQkXMaZUp4uc{|CP_oMkAA5d?t#h_83LcuciICuctLpQ^haiRTQLrYbLK;d z^EMF9hk*@N4LC8$#E9nwMR)gb6r!ON52Da0jz6NJF6wZVZXxCXf{KCzI3bNsxN1it z9x^b~>;jGr7%k38OHOemca>^BMUN8#biLt=!-r=vcrXc9HIM*4odlA5$d~AYh=DnF znF@QqO^rK3lh@2K285i&aVM~VdWb?S#dMgWG-FZudy$xx-vh10>AUDH*4D!$>{)jg z0=Mkp5z%1(6f2+tCAqqC5}Pv3D&`iM2Zpe)vw@-6b0PQ_FZBeNhpjM`JEsMHwK+7( zOE6IOo27JTZo)xucco5E(Xy|lag%Flm>9`!ht9R8M@2EViZ=kKo-L&~rLAT!VvrA5SzH80WQ0Zl*`$FxG^QurR@1oOBMInZ1WmcQT@YMM z4y5vH(WD!A&XGquxk%sJBd!cXBs;cpWwUdaK=H_XUE{oinA3)>*i6r$51O#_BJVf~ z1;QX?ncBq5j)FR?!^{H@_5l>BWLJ6xVuEF(IkGR2WjI=d-k<`rYNT`69IAO4b?s_+x5=7_jpR8wlqIO9PY{sjh$nH7Hpj zdVFnzq5|Z^Gzt-E7wHN71+~GnKTk8e0NqmjK?41y0_MQ|G_gIJ!9u`f14x<9$yzyp zmIYc!Gp#0bUpXPRtS)?;%jtKOz8uH*A|8%BY4#6NguD;0q|gjP4~_(O)qIj?$IDDL z+W6ca7Hp$0CrT2TNN zYzf()9f%rT_a42R#g)@i`^`}%9`Zmkz9c9(;WDk;-$LRRK+Wk0&f8ZOleWkIsjtBrm{KkxB#HI|JN%o-um9G=`q&i+)@W-sg!Pq0ccM@*y9_fCC&L zAoI5pn~fNW*u5(V-fIOqjjzS*{g%Tx9*&gXgA~vPF(hC?xoTw=Hgwlv8Dls+WI?)~2Q;JJVKx*!yQ3{ZYboTr-FeC+Pwo_hYy`^Z4G zv6N9&9c!2&FvA+4!Re6z1#l{V)368PA!Nt%bBg_e?7or)q<;{wU@a-YuZbATt9qgN5K%o1%+r@57AqYlz z0AN55w6*cQjm<+v=&v^-a0UHkU;Wb`AN+y+*B|~H;{E3z{^>tSFe4uFzy8%fH&8zx z00Nq(F+fm+r~m|&6A>F4vS1Mc1tom;n@eY2!P(b23%(UwF@|~;K3LF4hDIX zrOlA&^uQRf7eIj^rVfvYte4EuiaZbRl!wmhy^Q|w;fpt}*(_9?Cgs61C${W4aBADW zjT;W_*tKW-{tZ01>^#Ja8$XUbxp6$OeKY@^yS5uQX|Mn}_%>*W4M+?MeQ|J%Kdkqx zdOpvwQ-oL^j%5ymJz;c z%4x0|RpDX~32v$@U_1Kx-bcU+HY`ZdaFGCJ2{2+;k&zv#po)pvGtL$uggNFzg09J> zTi}jMZn@@;ODnB!5;H~*C_FJsJmbk@)qgr3OIWfS1^QNd2@ZDX35j~v=m{bCC#hc< zjicRAV~nb4uu0kzj~m;08v+QKn)hdp`U-{)e&~Jwe3h&Zsd+16*Gilu6E0SvIOU(iWL7{*|K4zayudbM{-FClu#?91w zbaRHU&0l$sD^@aQ1WTJJ;Q{gVq1T1a76qRFx_gzniOV&_VC9TAY>wlP#}X}Ggq5@$EDuwZcJk7AWA+Y z7BQg^h32xI@r*Y$<4FYs@q(U3%JaD!)lVsGThH5)G6^R*0S@6%)d`9)hHz|TdleC) z6JW6sEC`Vk8@YuPbP$3fPy!oH#0L4Cs1YnkK?=MO#u$EZf*-&Ed?VT$M<$^rh-`op z>5HHNL(;U`MF zR15@9J5vb2Bd*|ya+IVLlLJhI%AkP%;0d>=p!g`bLu;weBRzCmo|2LwDSqe&$+#b) zS|S!IY6OiZh>JZaVLT6^K`dT8rlT~ns$xb{EK;yfXX-ZxRLZfH+><0E?U4^4)Xrcb zXdc`c=S^hY2z&OJ#N@W531eOIn{;sz0Gj!^!q9V{E5ckXXGY1$SQ1uBipMRs)vz;} z1Wj(zQg+Puw=13KN)lyLo32!)PKpVm8YNeiDvFYif;3IbGl)T~1cC+B;0bf!L^IA| z8hoJRl{ZAv&1Tt=T3U}O8Df=(P-PFg)hw7BA!b~nF*&a2V47TMX2A}Y5vCTw3CTPG z6#FN>1X(OXwBi6XUdd0p(P>IsRvO9T|qe3w8V>{q_nu`Pl;xvx=*I(L)Ot6(B|mJFs2>+}CS* zES-S?tX>2wv(d?8Ng65tMr=wUuXDs?R1Ox=J~k4Mzd%a4ZS~wzoI5C6s5F6!^I^J|cjkiR7UEQXbjmid^l{V{eG%kQ#vnGc)p3 zBNuZcPTzmOigDTO}pRd|INUyU5xBWZBOMLySXFaa_#uSPy| z*6qj)u)#Q&*v${PRz(&?LV(O-%+PwCN zroruj@K?9-Z05;F4}=#KAV|E)e^Ot2b%sDq)i)o|aMQ=@F78LVB)KHV2g(J85)iDM z<$lg;wh3@{yx)1*Fi=SQ>No(}%VfYgYYVj-|JxpNWqRusiKA6)MNge zS%^#l5MTh2A$drX6ri^kdLVi(7kJrYdTv$~=!SEv7e`W7X(x~}_5cTBQ3|$KH<+do zJrx&HfLI7fe)XOauG2LMM#8qRb`T5fSHjL;l*f@!h(^qfcvK_iUa@> zFeDkl4Dcrz_p%gM2mpGZe_*&4LWp`U28R*|cdn-tJrx2{00;DTL@vQZ_TYzD;07YN zUiJ`hV(|l1Cvd4&aIB_+Og3>>wk2T@gA<1^IFmLSac+Ns52$b=C&52(m=qzRPxxRl zpaO-pCVpP{h3bSA0f-sUqB9Cqac@Y9p;HZdaR6)tKzeapHIp+4;3AB`We~Ve_VS8s zreepqe|Bgo@&#Qr5qnG}Fjr-7b#n_sV|52-5#~5_7jk(Z0=gCgT1Os@ zM~XR7FHDFA{Ag^4hHsX~Px4?-Y}6polWd9hdJM@=2?hW#;1N5Z7pic6$QTk985z(r zSPD2ln5SY>$d@BoJ8@!?(S?#3As;kG6WDMI_#_hB#88hY@)>sjbb~sy zKskg5xmH#Xp>^&>Xd9_+HWeTYhX&W@C8#iWe^wD>IadLK4!h_glc#rkNryNCn`#+W z=%5z^@C%($3}UE6bP+U|Q5nu?b2LMF<#v#n6OxVxPU4gu6quG;QJCbm6mT#GV~~^; zL2pICfGhZB2L)Fs@_eKTofyFnp^*`D00ItiCZV}u|7f2yWuN(}pZghp9pMl85C$mV z0QwUG>{oN5v1Wdu3NA7L#Nrpb*_fMol?})s0aO54U<$mz2vs142VfVn7#XV2Xm_=c zcjtil$eYp`P7{a%5R-@5Nr!YHWMp9$hlrvcBNz5C3!Zr*AZMb{W)-3Tff4>N2NeJw zU!ryMlA6Awj- zIL5{fWRnm7$qwu=4&$&5+h7f~HCvm?shetB)&Q!Yda0vIs+DT0r&_5@qpGXQs!Ri^ zuL`TNDyy;DB(O>h;shQ7Ljf;<3M57odqyv5<#HLp1k|V5-mceS&)zL zk*0b<3xO(+ky&%<>3G^2ZF3rm8^8fBdXn`Pqt_V{*kD9TiENVjm-hkx{J?)25k7ugO?>Us`kE zL{^{@u2>4D$P#SM`IKQ2A;NU1t|@pVXAk>eP(?ri4Un=8(61XbE5!psTJtSivbUu) zN+=*-2hpz%5Vt8yxP%K)V`3#WkO#gBkb>tDkQW(AAUzQOHnh*^g>?aQl&cu#zzcdn z3$C*%Q0pSR@GENOP4KufSG!hOtF?_eR-u?Ngp!G|iIz{nl^gggv&6bg>9%M90w^oD zhU+L_gD4_|ydw0Oxnn6A$h7&ITZ}P;H$3?Wi=;4IVj)+ z&O=i!r+HxDz5*P!=8Bs%3=;HfzYJEcUqO(2;XwTVyAxyUrZP;#F%i3P2Ld;1!|e6I zcvr(AcUN-|l^25&*eHX5H^3C5E!$zi;QPT}4907MI}b6!BCI^+VjxY@!eO})b{SaA zwO3p?F{ise$^{>LRjdHuvn$fWz}R_S*~FC_$hMm=fNEt-=N1srq6iYe(fW__YseG> z2T@sUA<#-)=7Wz5NIZcLQ~<9b@V)zrH3n13VT{J#a>81p0sG3YbgMirlvJ})Ld+{i zD@; zC~{UQ!#N8q^3Wu6zyWHq0b!625W{Gtd4$&g6lj|n4^Frxmkh=INyvGY2ZXu-C48jd zlDHrICJoikWTGVwr8V5*u!C!|3`H&!a3!38AWkWr_GFO0OnIR3V3~WxBH^rL>LMOG z%`Q@$*NkkJce(het@y$x@jwNMl~_nH4)L&sww9kvj8icU#hwCD@EpmIyvZ9@LGjQX zQ&Lh_QUf821W4cnQa}Y;;00a~24nED(*f0P@L0kDS#y8~%5fZ&H3^(R30$pNnN@fy%E9cX|CV-N;jumw~g1ti-801En=0*fc+hRb(UFXk|odDqDOl+*S9s?E__6B(`1kzoLKF_!-u*%A$gvBL>_-MDr1 z*>h1knKYWgN`-KPisC{mE!o=8Rvs?FG?d`7EhP-&KsKX75x-p;=*OxV@!RM}ip5Pa z#ceiurmFZ*&hcOk&_E2g01Kr+33<>`)^N*9GSbkg7|~!)N7vnKY|(`EXm>F^n4Q^r zvDtNDPn|e_U^#l)3EDuLG_0Kw@&X{-eT?ZWg?~IU|GmW48ms~G+F!E)5k1-K{7CM} zBuwKhz}_&h13>`e zNst6g00mW01zC^<)S?!4Ww{6cT;Cu*eL8Uu;l(E$#y1t=;rt1H5PY9GiWTREByqEq z2;L0mw8Qcgtn9?tP~Jx5SAI979qt@-LeB8H4ufHzA~8KCFI0zneGU zj4snHb{<=5=qiHf?W$HvDAK$RIvHo)`?=aE?&qvXF^5xNwDRdmNayx3=k!#c+}-S; zp6E6eIzr6k+JbpXN$l~6i5sz{3Si$cVOJy4>T$?3SN^_9 zqLKejbMBt(wI;~Q4(^iu+LVhw+u zC{g7w(dJxc75vbiUWV}*ukj&2(d>&Es{{4%d%r6mIzi46j9>r<;Fc;6sE%Em&A8xz z_fHZAxd^P`H_!IId}38uevQ6x4xBKn+g@p_D0hsXaqee(q8R=l1ws)+C?WA8A+F{y z>KPyAtvI1J&sQ4%p?T1H^)txC{?3ezj14X#0P%jKYFlD$Mi=6amahv-S+0<2|MYle z`44#ZnOq|wVHzIc4^9BM$>S*70O4fA5vVa*@eubV@5^N_;Hn^+nx@8Lv>6)j%Gm{H?K zju<0;1Q}A~$d4UOoGq(W)LVSIA{=BGzt(P zID`nXA%tcT6;!VVAq**3xV2Xrm6#hhR+md{|E9TH_i*9DeFOhJTtHQwv}hxTj`r-v z=NzZoHaoU#^-fmX+E5q-+C_U?gQAHCvQEOEF`5vZ*nfDw9cGyaNv@Cdo5zt1#h8lg#$8K?fo|&qOo2 zb8g844Jt0%thCNh5CKL-7iF|X7Djv&Qb!|g6jDl2yR_04PYCiw9dit=$Pic{^-)eo zK-f}y2_eC2&6>UfV zMpJzh1t%PG^;l>JCb-~RlQPB2Jq^}QV1~)OE#kANoj9XpnOm0O>s}>m$%CbGxLM** z^2!|Du$>5>JWecC)JI=ySx8bbZ8_3Z`OTDGM-^p}=1k?K*~Lz4CUxCNDg6vbd?_$m zQlClP00d6XQIg|_uf{rSX(y6{&4slFdtiqvCJP;tvC1f6<_^Hx>R3aT&tom!jFoQW z@L|NqA2%SfWut)v{O6uy9DHY>T_ig2bVK-iW_H(gS5(C9ZS;iDEYG)rb%~sm(x|rp zn76P;C%yEUP6op}woPZf$~xVCiQ=d-Vnp?<-IfV%?8wvIy7_k9vv-*Ac>;w0r~THT z@tGyfl-)~HR8&+MQEYVioqt|bMa>R((E-qM1TBOYpr^co8uHlhO5s~)zkOX#uEM(o z;&i`$lg7q>{jVviojC*^7LWc6mXtiv4_CrdOT)y&4}A2%M}QmEkLrcEca5rEHw)Ui zqGv(up=f$6+Kjkd6Rr?$=0iQOOovqHgeORW3RKv_7Q7G!G^8Oyg=r8T@Q{Z|XaWfp zr~tSY00J}&XfLQZVkwlk#9L79A^VV({iLWd_e}>1-r2zvwdgyT;BR&$3ywzg;GO?< zMQ+eRhgt+k#o)2UE4@qLtd_I|5$(-I(<2ejQd6Tnu4s=X)XWQBfEmmG009Y3aDo)D zz!*2cVK6()gI{Jb3ttce8rv90el!`6e00*2pUek7>>)~0j?$E+#0Wn8kq>ATZvi!U zf-LOekA0lvY&F5%7MZoiH}R5BNeO~?m;fzb>T)*sGYjq}My-={jX4PbpsS4OrZeWU zD<&IT8|7#b``AMm2>~Z*!c_x9Dw2kc#33XTLkqj4X`zKxhLrFG4+kAY&51VLfngiU}u&i0di7dc7RoLH| zHhNKHn#3Gwl&CdBR?v*B!8;1LT`>#R9y7u%j$QoSHh<@jcn|~s3*_8F8cNX&PI{7` zn%r7MPzsfthDRhe$(4JC15x#53!w--T}3O(Qj(4*OlO1@Sf{8P1b8P4Asy35#gr#C z!pDGN*^W%_7e?W^lsPc9j$FI?lr*Y!ZV~O6dcHU(i$27Y_M4JHWm4GF6-%$Tq3P8k zn>)7J<7`azt9mx(l+3nnIyqs^8X&sYfo|t3AXDtDu=&N-*|jS3BdzHIs5OP6btP-r zM=g~i*5t5Oiq9GkZ8a+=qg=L^SBmX$CE_JUyfwLb-A-j!GFs{M6<7i!gZ!S$QLp;; zZJl*0c(=Pd@v5YNj}?n?tJYZW_Q_eCMXzKZVh%4AW4ycnnyof>qaDGDNsao;E^{Ps zUjlbgf1OR}N6@jj2j78tT&}2aohUa*wo$@S5=#AY)djm?}QP? z)Zr}sN{nJ9#Z8()adA~fz{V%1m2A07sj%Eb6t?>0Jrtvr0<+a)HmBJovQ;s`k_=`l zGu2r%S@4|qnw0=orA=MlZB(Qyl>*v%CfbVYjTa`sEO-Y77zHt)q5Ne72QN(}IrCD_ za^`3StGv6Ev6X*GvQTsQIVb+>vlLCths4i+l7_PXMNX?H-B`tzRD`L^h(s;+-2Y&X_6I=FOl+~C+WrOj(;{Nt_CuVK1wH3qfwuc0~lLVqr*Tj0Rsk4{; z?7SMgBj(--y1R<+EpC|9Sz&idbw}xZcl^3T?l+gq%A6|HnWXY4bcf09>1XGXw-bT5 zPJ$rk`))Mjq(0rviG^f)uaDv$PwvlWm9+tQhYTi&5)D3E@`tMf_&W!MqNd0FF+JONDD}B25Z<%EqWs)?`}V_RMj5T(uYbVTL4Du>6E5Jd1=yI1 zoqfX*e@WXe`|;;J{-j$zlJB2WCcHHDrHzlvtG|n@Dc!3Llp6~Z)It2CCPRY{8bqefvO&wUDzV8wlJLR2gRv%L zk6pS40x&BQq_-&qLFkJ-OQS!S=)V&z9U8|JVtq)U!f47H;adr8$hQPS#taO@N<0{I7)12&J^%o|TCB#~d%qsF4=BRE3ZzAq6AOLRMSeUkxvI#WbFS=&CO?!9O`{Hr zl11WU$CHCEsuMt(*a7c=CW!>Pj+8#Mc*%X#$m5DMMnnpJd%tzml|d9o9-GIS6Um$e zvjp&tAjpcDL`c@T$&5rwdV9*hg15u#w>;A*a10J;t4caqzlr0yql6BcG8TR)I_7`^ zn$*5}oI#%Cu{G*IoHWM&Xk^Q>9Lh&*OV=|Kvb?A``bvm_2(9!>tQKUo2Ts3#2u=WGCdt9`@WQ%+F3TP}$H|$1oWIVVX+ATtG~0N$JS1lr%uvx~U0tzrVAV6}(N0Y>HQ`3R7rGe!R@F;s}AG zOX$kWk3mgkoWr|3&gTlUuL8|DLI-m!OLpT2-`ffCyw1UzlU007;c_dCkWR`(yzL|} z`pn6Q5X$w8O^GPT-ttV6iASFdy7UY=F7&AB?8}v_ONRSTk+RRJ49>q4wqlZpht!Fy zxVypn!uwp01C`JJTYJt2#V-OCQ42K50hP8js?D`oOvxhAX}U$^CKR(RPLsCxudDTOTcC4yJ6)_t8+bGf0k5 zO6EwuWwbx-ytpP6i6|}68(TB95mTxZ(gE@kkgCr53(@fmuJ^o%F0G0$)k8H~(-A}& zGsV#+M6o#)vMhI+2`B-<-%kl|y@oRMzt?iUS#alfh866Ss6g4f{qbAxT=bM<#J5=3oSBoCr`I zKPTn7hd6-$^JBk6v&8RIMOF2|9mUeabj0L%M_z4E+la?sh`l(3RK{-)0&^ot9bnH|}bVvLnTbl5axpN(1+?@@TtpjU9wT(EoRUq3^rVS+0 zxdlszAxCa(MQ_x%SanoA>6Ij9&^K(sx&;=-)rnuTOoF;xj=ioJq*|Vh!nobqD^$~r zon5-|iBZ)F#Wd8S^}>+dh_2lU)D^#z0^T2-S*9Sp<7GwgqTGqJiCZF2L}Xr(@rh>K z2{4#FZyg=s)rl>O+B)nQ$}J{a0|{@tHpd)UGZj8ux+Rg_(uasgIhaPQy4~9FUH}-d z@SVWwC0)3vz2|)&;}T%8ELY$;V4nRgh$6zOPzCFBQH{*s{ncBLWL@Q3-z|#33oh3G z{Jh^V^SKPJJG1RV-B8$_h=zBIKzeODMpe*)@eHIbNpcj3}ZIx+kZ_*3EW`kL{p0}T>!vP&U3j6OD23HREzKu zyn?+EuF*GkO%cY;L55)0^Iua$xisFjJ5(0jTTejNI4eFzp(y0B{4Fn1uvsW=x(9>)Snq3`IxnV!T8-VAf?Yrikv~HtehA6!S-)lZRK9 z=Djp8M+RA>G(?u=Wu*mWM#fFyygL%!A64*@U)#JLWmf(=HD=n_bDj-$R?}eIKIFyc zNvmdemR(|YD=wzNfkx=wZP!sg3(;F!KUU@0$hbp=uywYbT(-SJ4C#ls*pvLPmG0c> zi!@55O0aBBn(k$N&eC7aQ#D4|$6_h9KnE`wTCb}@5<_2Kj?$pcW0~FJrHJU9cCv=H z2ke#9GluCE!wUL3=_{_hdlt(z%W5eKhm7sn#e-^omTH%MM4vEEo!G|zSzPOxPUfn} zn6uW+(^N_DL$fm?yP3 zx|*-Ts1v4ul6$7eG=7u+=<>|<-YBZvw<2h#9N6_ zP39;7CqBlogy)3QEQzy8dyRkQ6@!0B$w;pisorqh6~ZT(bPIx`A#W{v@K^;EAAWQIR+lJ5Hat#iryYy1%F}xdt6<2wKff3aF(M8 z3x#Vuu3pNM@GDdcTJRDKwaivl@Hqu>oLku$4{?JbaR3nf&6QIicEm5faY%EYd&tI}xNgkt(?Euu{N}KWX|h9>Yb00ao8y?;K0_xD zT_`u_5S-g8*NKvoXP0~EkV0`XQ*w#$XN8gOKi$-0c0%zg?+({247WN5-RNQ^bc)vH z9N+O&9O(m#H=D9^P1RkUnCwdS(~q$e;y&ozO7zX0bIG%DHuQ?p&T)xj-$%Xl7fdF& zMMR3Iba^!MRaNTFOxm@^=_r@ui)|fvg)LntzmbNISYP%3wwN~MKAEIGj?4zE2z`|Y zXZDPy+xd83G~aWqOkkBWaNGrRY6?ZO@L^ydyLkIcu!zUI?n-eNGjE;fiU=nzipeWAl*>;bS9fkUU?yV@ z0r>Dw$7F@n_7F}AkN&-PCs@5KS7V`%o3P*7sx5E3hiJzN7=X=R4lnWi z&#wvr5dF>H{LIIE%QpbZmwd=i0LNbd36OvhfB_iT0mM&&Bq)3)fC4BG{N4xrEZ_n# z00S~0gEd$KIgkTA(1W};1zF&QUnmAtc2YkX0f)Q8%fIx7-zyJXN4kApb za3RBn4j)2{C~+diiWUI~7@*OBfddB+BuJqDV1a}YFm?n;VlbbSTlHMvKb(ne^wqr9Y2CjVg62)v8vnV$G^`E7z`GgFgK#c5KzK zL9;Y022?58wr=0LH47K(Qm{tlPEDHE-cvaZ*W#UPRWDz@gn>3a{8XN~YQIb|l0$|| zmMTz^?2w?~K!E`lKZ6b}dNk?MrbA~eP@p47k|$NJbeTn_9Gj+i73+!AuDpAO5d#h^ zcsTLm#*Z5hEFAf9yu2EcS<8Gn^`Xn}9Gg%(5Dg=LO!y^UxNHq}JKOfkI30+}9itT7X1m08wa5HKvDfItNp zw4aejBAKL;OETHe039VD!465dWS}Xzfj41@g$b1%Sc`obW>*t>`I}aX@uSy=V}jV` zZh*O26$}g+02DeaN@y3Caq@Yla(VW--I{=;e{MaK47feWi0Fg~nTB)U% zVw<Pa?3ulq_XoOG?D}-r8FQDEsgsUOft>1Mon_)p$8*3nSzrnve3ed&N~0{ zOEEtK-Sf^m(?ZM5vZ$g;%`?v&b8R@fJkv~IfU%>>D5K0?dr2e_L=Zc`@SX%gDi9I^ z0u3ZzXBu(bcE|sH8}7+4)x-!d(fDL0oLAXutDt)m3TC{kik0xeV`+J~XA8jqT*E9r z_3yHWA3QkWlz;bBGz%dAQ~)E(5f%;tZtu4m0t!eF#M}kiB+D;68NTP4l*i6jI4?%? zv(8F4jpNi{T#fR8Ux!V$Kpb_pG3m=UWV!+{s6H+jYv^%qU<@m(rega9w^le$aUY^t zzq(r1V(MZ(UE&ANsqunI5&TZH=5c@TB5bDjje10nU4Xl#i{2-Z#MpuMKr!guPgx$ zJklu@y=J!-^i2>h^GY8-z|asm(2rmXgCF_;l|vBKa4>OEA6S~QkA294dl0PG(BMD+RTQHdEJBQkWaX>9d`X6xL(z!v$12PH?TsIDm#pAO7YX5UO?ZS; z4NrxXa8#ohv@nGpTvoO9FyTi*icbLoP{Jm03S6p#0!+%qLfzP~dq?Dx8DB;>7rxSHo&l8894NUoFdgx^zkK#|m}<|86m zzzC)k8|l&i1a(U|ib2T9^e8h4;LPI2nJp}` zCO{TNnF?(}i!iE7m=P3KGc}3Kp^_<$t}NU^ks7ZZ!fJAnZR=QRg+8A-*)79%+C_bOJssvRL>Z?U+4i0 zRY-ya4xqf!M5kd>ENi>s$`zB+)=vDWmO>Pu)LH&1f_$Ce0R2VT;tV%)RLyNM^+(t4 zxQm+qnk6Y?s=*6Y6mmzG0n$t-rP$@+6hNqZ%|*u*69ci+buFANGnpF{Y(xXFg#=y) z+p`03iW5mNprw101;hSYXGOeCNEX&YKUI-$sIS5+E0MUzkIJ&Izx^eErHho2QrK8y ztSmICh?fc$Ll*O`jC~gnpTvGQ#Ro|ZYrZuyl$eaCv!NPIUM91d)$C?D!_Ci_5se>L zqZ-&4@{q$3jtU|<$x5!^kYO|?mc&7T4+&Ll=tC>Ez>|yr zbDLttXaoO54+>d8VIH@)k}mhoe6y`~1g4ZQ-$lW6e)KER9O?{f=!HZ*^}FKOz35Vr zF2|KeIJOCB`mQ!Jn~~3J5$)?=AIShN{3@yC`CvvbiwujCKs6HVDxp@0&0m4zry-iy9 zj7*LwgR-sxNk~|}H_MqcfCL0VWh~Q!T7p8qhyR;zb`o`U05+@I;aulzT=)|I*O>D+ z&rvYgc2jbX{uT55Sr23wYj(*NYr-|IHK-yS2X;EdeG>Fd|9t5Vs`lX2 z-k`ISy~C+IB3m#XcRz(iazk-;QUEDP$kb-)J9@W|-cz>YB~R-~>W=}Uq`FY)ffpBd z__5%A_i5w2jBhmS9g&zXw_o1ciL<<6*n2Q%haPdT9=(>vURMkLU4uEAdIx({_z{G# zZ1YZ=YSz~4B?&+^5=GsMQvw@M6^MFzz{yW}g2SRDI5Czjcj_B`a8zS{e`|iL_~iL{ z?N?vJ*eT zmLnzdMgRF?_S|FP^kaoUV_A)3+?|^{8YCx8({i<64p!nax|bZrpdljMLF(1q#o0Vk zWJZb~iXb5MaiX31+&^weETSM1YUD{eS1Jvpu~8vPx@6CVqAJ1QNXDb#tfWP{;ay;) zNLr&GrrrY{bwUVaQ9ab_WBTn4L zt)OKQltVV!r@iG<-lT@fC0(+h`dFTtiPa_=WI=`{ATAyG9HzqT;HXt2#R%$LqIOyi4f!ltDA1yuKsHLE4dgy{#XNgAV z50YYq8eM>vrN`yvgqCO+RvKuM+uFq^L8>Ek`ALN4C_P^1BfjMZ7NCNK1qjL}gT`N1 z;vi!Z>4i?`NRlXuDrdX+sDzH9-9fVHT$TISVSzaYf#?N4)|I1#opyeoP=z&h1T3?io=K!vv zwvAMvwqjk`>YO6g{nTBqs->E;T9u}tuL|T{I-ny0s}K@vcXX%;rYM(S1LF z_3EnDCJ9<9nKG%r=|yoxqytJvQ=kjAN-B$}$N_F>pe$>r;-vh@PdX~2`LwIKx~Wf= zj}9u!l+u=Exs|<2DqkR}etyZidf&t)47SSUQo^E_!YaUir8F^O+gV45GN4Gg9>8g; zOD-ZE{;0ynCclEGXl`t9t|pZ(q-Kij0T$FOp<4;&<1|iHy#}VQNUN`ctXa_}%v$7} z1*v>yAjG;X?YJtUM%z=BiX- z!lG&6W}{LkfOei`Ru0fsPE+9L&kE$sn&!?rt%!yxm?rBe+H9kCtNINg*fOe7YV8uW zE7!sxn!X}$M&RX0EkM5LrY0>O?qEpTXVbDvQw*$1Lgd_L=~yjpbLC^_LMVjksbz|1 zHN#BCCdbMm7wBp(!V#`0f@33&-yE?nuA;8&;%YSBZS1xu>?$qO z8b-#}?(*uaQGzYU)|SvI)6*8^($cQ8Qf_cu9G^08_fnIsnyp6iSsW#%RQ+nnzU?8N zBAnf>wV}g~iiJ=XqhNaPL4q&(MyzKxVi@}uuxb~KtMpS;=hFzfPg@Jfq;O=Awd97IItF7fPm2D&4q;I#D#Nhz0l5G#s+%gIj@MnL;`(-uavo*efdjr%P^D)G|ppEM53oy_E^%BPJ zG>Nu#Owmg#Uh|6B9c!@cN0UFR;JT8U8g+$3X$*s^cDsck+`^AgR4e zUo^^tOJBg9SICIK=?VdYE1)TyREV3V^0P0EcPFR`QgldgNk6u&9mCZm zMpmt$@k94MfP;@m5)2u^Ez3Eecgv0LM~t+yr)cC1c3=JJLH#2=GG zFRNT1q<7=vnqTz4VBVgw)81o!PdCioGq!Q}~XPK10= z7$DsEzp8bho`IP9LPcE=2@pJlZ5fz|P%x3h^ftTq??3$wTG*%1F`u?rZ$V^**-o=L zzb1ylCtjLmytf0lyHAXaqO>=w91(-xp>|Sh%`_u{g(vGiMf#&!0JOxwz{p>7xCA(k+m_f$`6l&ZJn<+_H5{E z*W(|*j$Zj_fIdOPpU}RVfP8*m5s$b7`RJIb{b}j0)+MQiyFGN1yiE&mk&wI{8y_3J zsQcP#*kC=2uI00OS|SQ@z1C%6sv|2M1LZG$qt~FgjaoP$&h_5WWzi>@TKx^gvf?Hh zDE2iQyvd^vM4BT&lmKfB3QQ2jZR(l>v*63d094ciwE~K6ix7&>M*`k20K)+u<*yV2 zTqnRw0b-m3qU4XA1H2AexlQN-mgPry4blNVzYY2flLQzRKmZAWNC+MRyXzZ3@bfpG zLLdtv=m<_-pcaAi2s)H+UXJ0+PYTdq5U2vlxmd?&3otwZS^WJo)W^6DKp#GAdMHvp znEG^T7)id$`nW4FO~LRzUDx^?a5BEreSx-|9=O&4_-@KiX7J`!T6Ib-sUC1^_Yr<@j8jUgIhhO;ncIt?gD#Y1Kg4dky3zkoAC z=>%2tv1d1qkB*&>N$+i75c@-8drI`B7^X0ErC`WV6@5zk)(7wPbo6-jRBF^{m{q{a z{aAIDdQqnnIi~~Kt_(3EwM1-6jBw^ zFUV?0zEI&j2Kn@3i3pM`(j|oU5XAvj3vdf_3uFuWQ{hvCQ_)kz(`ptJ7D1L@ED+|q zW>99<^DuKXa|$z1^QW_M=E!E@=IQ3c=E)}*$NR?>$9^YFCsngNx#`8jv(WR{Gw!p+ zGpaMrv(pSY7+@Ip7~GgL3`$J*3?6y|OkoUZ4B!kV44Zm5dRhi~rV2)OM!))$ChmRA z(=Z2VCjRu{3}g(h_izt&^!5$r_3uu`4JiyW_0^4y^_ESZPNq$1Ouza$`bQ>D`q0ME z24CQ!@m(h5C{JXCKvl9209EzK0!HUKJJ=O?^mB~pBkP%9zq>) z2BM0-OvKLI%;pT_jOUE~1Og8lO%}~3izmx9O|CG&U<&#HsuC(3lIv$S zQWrTleIJzp5fZrq^{r?EMH#7^T7#B@5)5?_6&i&K6&nd$ICbb-D0(=&L=Vy!(kD_O zQhywVc$`s)QR{vSihzU|l}4G$X(mg3L#spGJh z7i`^-aiqDljgcPGWZ4jYuT~ubnGmiw=_#6PNXrVF)7~^9Un4-8R)FZ6SpwwJc>Q z-6flfG>f=Ag72LXZlV*cz%WUJXe*z1HW4ZIOL$i~$Lw^^=w@xzV8|owMJMI+GozT6L z$y77S^DPt4ZoT|JUf%dxC|e8LB5@HoZTEZP{m&u%;dnWO?L$0{o4AHHMnBTt&{o5= zWON=GZYXZlZWr#juU=Lq*Qlp0do}ytGM+M`gQhvNU3Fo%w6=cm(RPD$i+J;V5q(_& z4*?qkm;0*ts)9QEW%!-?-}GMgP7>Y|`VwyBuIGvg2KSTnDfAb?m*Y7W9O z;Dnfk842^kz2yt%%ZPS{FGbKr>4g7{X!Q)Z{5=x;sdlr{u+qxW-$E@mE5;M;gty1# z^uE&WlKDLTQ_fFJ&@_+VKw>wTB){NvFJTxonolxGnm76VEMPt~38gBbAR#qgl-yFW zRIy)iShh5qiYt=$!rP5Nn(#vK-<3o4eHYAXgkMCZ)bvCbvz;l_^m)buCs#~ih9e_( zI&K_KQhuVMXVj(EdTXjy`1%m{X2*Sp?o04(HVOq=LS`{HzN1&$Oh?U$-*(_Rw5Euc z2z{7i_?aY!%`KXUx`q>GJG+ z8+P5SOL|K)u9^={8@Rb#q|TU^MeKr+wULrf@0Zf-tt_zY>1@tTCC%%d^qyZ9)Vh{8 zphXbsRtZ*=R$Lc;2q&GXC{F5NxH?&MeLWNtnVV?}Fb zm++WhDqgfdDZQ8*b}M{sm`Y4v!coApz?H$U!Nvw@1UN z2Myy4=|r_h%68woaeh%9FFu7HhfXew+c4P7k(-l47jYDiZH0AKW~*d*UCVrFf4R3m zRhx2~@=l9RSbP3{`TefOFwCsVOwYH~rq{x7Z~j#bpWV-ELs#UL`=)TKe1Dc{^Ka`l zIU||Z1OBV)j`)szZMwW0#oLiilU}wGtolz?QFU#la&>vtlQoCcgw@cWohGQ}!6r?w z^bfCkLk7sTg1nUv^d=%bm28e5qb@7V5E?7xoIN3#cB zr2=K^}6|=R|zBWU?#{_9|1fM|4M4C^^&2s1iC1RJSxTG?X-z?JGKu_jAVwp|>Ytfn&PLCFN3!yR2=? z>lTJpM;m4(*i~O%;Z*`VO&ggZ?W>WjPa0`>Y|f1i>)l&!PXeBr((WQ+C;jhJ_f>`) zz&uXT3GV9d%lNWuG`MmTALkxy{dyDk>y5_Qa z=^>FoP__w?gUVpOdGt)EqS_$eI-b!EX zNAmi1ZUY|c!;FIv9y-Yr!)XA!!3I4LQ&(_m+*4H0-10R#&_{51# z=G*AU)l6}~cAx}87DA%(2_%(>S_$omr}L|dZVlD+2#;5c37YUsIt6=%#fM4yy7S2* zT^V;deJ+EH3o7+-6G`Ne8C>qxBDKaf7rYi88PD2z+`ge`p%N!DB~UA;DQCxSmVf*U zUfiBZn6I0Gn;V_$EM+hF8bq)eFs89F&^y&TG{-aeJHXU?8t@>VuCzAqbn=)AcM?S@ zQ#<8F_gMA%*0>G>-+~vxnQ-M5zkV`%w7TcG+&*wrQXQXN!Po1}s7FylT0+*sK|;;K zN%LN#+2RByJ7BUOUV=Sa%7`ZhV_>`o7Hx-X7;Y+&BRxP?MbgQ)ne1^U>?JWV3R+rJ zd@q|`96KLvsx|82U~+oCTyb!|m~mjwXP4pPjkj zahq{nWf)c7t~r%Ox5nz%an`PXpv#ih9c88+=B{Tfb#9egiPraRs_QIsey!CjZpXc& zO=XWuQBA?izv|m}+YQ~~@Qfdlc&l9OYOl`cOT5}m6+n-DLwkdABX)cc(~rRO!Rh}D zaS!F|6m|S!Mg01A6uC+Aw!f=%x28Bhbycvrx8-&lNBb*9S(sL0lk-9M9oHexXEksd|$ z<<-r8-V>0)QTuE6X!pL!kH}RhKFTK|8iX`0+_EgNP&ZT`|2nqBu*2L-^+>f+XHgqm zNm*%HIpP%MSZg_Lp>_##g?_$$t_M^2ksmnH2Ts&MWSRG2V7!ZnG!*BdP_y87-!fsi ziq>j*8we#Hb}2z(TydKt(N)GA8hI~KskC94-2tv*C{Zs@Ex9HurmLzJt%862#gf8| z!`x=0rMGKEW%SER#mw5+xBj?}Ti@7V(&A&ZXp(JtYFcN*_n6_prT-+Qz7#rsTyy#K zBX{Jp+iSbGw?N44(&b6tE>tY%h?m}PIp9e-yT7ZfOVjJ(Qylo8FF_6Qu}h5(J15mU3-?g{OD&W!YQy2boY z?Zk`LpOZd4{fYuWOhqN4_a(Ye;zHl41xch45x&)-Puua@R(KQDl5dq0GZ zf{U}pqqAIZ9M><6@4&Xs-o;a!7p@FUm=6uR)jrkKn^zycvj5FpX89n313{AlEeQjiiv#g0L+IuI0{W5lZu;c|gtZ-;5s!Hd#F!Iely=yI`s`0k z06R)i?a#*Gx4_**;PxA4PV}}P@)#x#{*j-V0qm}~tp?o#YV=lg8#V!G3?97`!05d`ODuyM&eOMwrnr&IX@Hh6|cZ=naBS!VgezszVjV9YI`3g1YQD>U&4G0R{=yT&qyr%alDqUI zmy$XO5;0EkgriJjbfY}+-9<4)$HhivG?je?b@{J>EQM@&o6Xf?`I_~rmi+F5raI0d z_!xSzj&L>+?70l2DU4}%>J%Cp8i8s@s}29WYRG?4{A%ME_EZG63c8(!%08* zzCTvl!7_TEC3RIVrEX)Nbs%$ig>s7)D=|Tj*M_3$Rcg0erZic2R_8e1Ot;195?$6> z^}ydQOfDO}vv=#&mvekQW;WGwT)xAOzLn+8cRTB;{{j8ce8$&9zlu6{I_35%I)8Y) z^l=fkrO!p>eg@H?YM%pv^ZSPio*6LocF+PE5)k#`h2Td%ws&5fY~0L!S&%k`i(W@HL|u19O&z) z>+l_Lyy#!Cf|*3g2C;JpH!o<3%!{}r$Z+HlNa6#M3rY)OB}ZmjX5eP9=3K`tC;v_! zj^8lOG00*k9Q-N6)iYiw$MscAmPFaGo1)KaafG8JWUQvtt8lCp@hq~$H`hE&Up{^~ z{80bF2hD>pg?59uh>DCfC zez9iuemQIbZuw^3afx}39Fqd83=0zzpGlmd^UsoHm(i%cu^w*7xw)&;(ZwC^Ee<_r zBbT?c^1tl;$8&>y|aWOxw!h-edlIQ*Jc-IpLzE%Rjw;XuR>3n z7^*Fr%vTmswcGNVGnAin@@k|#J=q&{&~En>#mZ+@Xg+@hc~x<$bG+y|H$%@Z$Hc;U zYkB{+vOb;~`oiA5DR@O;iopjtg-MS>Q|2N0rQV3uz+01Ax7he`_Vk+drN4hsEL0|~ zz>mB_&^YBH2W0K%b^SAs-$DpYjzbC3kw06{L=8VGpnUr$3ho$KqTeaM6>(xJhXb@5 zbiTj%aD?Gt&2VizhqM+aUNrn*v_cGp1f_lf+O3~N_9?Hgu*>fTGOq%eqMiJc9LId^ z-0`C1JntcdzTZX_CUeGa{W#-&lT1BsNt6EC$wC?NvG(znaqXc;I5rH~oZS^bZnsG$jcpKAuX&=*;#Eznk~}dx~;2t z>jg4n%YSB`)VRhPmo)lUc5DRrmm7B1*N)$P@LF3!$hHXWGeo27P;HWSp?D30Qh-wX z(FzKzbY9Q=zAULqDN(D=*F~m^H;9fVwJGc&zbS1fMv)OKh&16aqRZgPq|LmedUEJ; z>wW4=%kw8qC{!I+qT``hE<0|?(_K`x(q`7M^!6;7s_A-3GV6cgdTzgo)RZ=-?RyHw zBWb2~rg2De(Bu((+Vn=cQOfgn+TM-1gWS7|MOWV|qIV{%>aI=8@Wy8C{NQy(dmwa$ z%i_EF`N8@3=1jHUrdG0WYx+Ipwe-vWVY$(G{9u{di<-K8wjA2h+S32b?W_@dAhISh zR=RMqZ}Li$O!Gi9eeH3L-7DXV>#M5^^n&ADYwf{1AR0uWS{O@sMr15nk&BBj5V$83 zRFBKl)U@8i)U+K$oGr$i09U~KQ}rCmFgDG7bWQudrGWd-)BDv$H&ED?$KjG8PclGb z|7rVM-5v-C8u|MR7$_|R0|_Ztvu5%uj&Zh0&UtQu~W?(EJSiq4!qWr|y)x$V$fA;bH8aFl=3j`nb`hzm-|86gbQ|$}x|F*lmN|3|(yt-XgUH1QW ziUmB``|A9^i(~{(Y&8R8gaL`cXMUcH_OW>afY zk_PrvtTZVRJdxd`(FS_itdB_SWX-z$3x&dSe4g^?|BTYJhX^o;OGJaPiVy)O=SyXC zQMI~ZSTw$#Xy>6G*uS-DT=uHHVf zz`-+mSA8-|Z2fQwEL$f${_R9{T);-vKiK*Cy2F6L*1JCTeSGMpi$G324u{&^?jaXh zGB_(55&)Kt3G#^w|BeRVZp7cee`BziIq~-te1-U|7EGqGJ}MTS&-wfp7QRwMIh-8v z{N}Ys_`cJ;=`Pzn5M=Y-$j9r3eF2lI)k8dAdMD%y0Pkjje+L72m|OIhCzywaM-*;3 z60#wrm)9;tcsTZY_t!hQP7(UI)zy)JGx0M>yPZB}Odx4An;ws6f5I1$r%U<553!x( z&eD-7%EpU-d>x3zpA3LvK6F>%;U(|x#$8+}>nrm|{}%^6TaacvYF|`lU{V1F+HFeQ zgaUPkZ1LumqYjK8+1;ZJBSxo|Ov;R@eke6gX;S6*Baq@ZuC%;egB`AMeTk@UCI1Br zo(TWvtgC|tzByPO63AMU74*-)-uI#9X;SWqsT*X9?C~eC@uNbsLDbw-#Dg}I*?ivn zzm-fs^8XvnB*=f(ijeQ0AoK5g;Uysg=gWi)PU+FnXwO@{_|j6M z0F4e&P7jWTX73L3x7-Fi^KW0F0$;#Sm2Gu+S^w}=D3nkxv6WA);ovr95=oV6q{(u3 zV5%~czkv5bLGmmB7)4Ex-@b-a>0S#>sAprNQ>)bqlh+|^l%>yT%!&ziN=_={-tFZe zDHUO!2#^f;Rm1=rt$(%g@$y2jwX>sAraG3lzjur`7yrefmROx4g)LnK6mPELX4Wu3 z^XeOsp&!F+Iu)NUHXf&WO3AbIUsj3!P5_7;_X0^GIg-g}LvsYC3{%?|q-b~Kl za5U)=VIvh$$b*rai@rcy`~4AK!ywa0tU(rJQ?Ml7V#JYKNHcHSq%YJNAZ~uW{gE#E zE{?a}S?|ZKnJVr2jjl|W1Br<&9?*Xcf1RAatVUucfBuYyZ~dIEDpvhA?bkfuWPfIV zzTQvrNrMp){h?NBQ7#zC`*N6X0r??1oyB9c!qB{|TmA5`{04r(srGuN8o&1Uq34$*mIq^fj0-`lSqFm_;<3HqEAOX`SA6gFqx02BcALxSNUOBCIFHjE!KkQ5<#M!APd&*yunNXh15l|K8S-ZjDz*1R^9%N0-&wi%zEn49@fN^G&5jgRNg&G;aIX?Kr93 z{`1S};=q9LKXA%Aii!*tA9$ic2o%X*&VKToY?6&Q&WNG=XQ%Q)SRAb=AaFy@_i+ z1U!aZa5z(|`%~a(Jp9q5C9~y{WTC`CvxC2z3;=drrVwY`uI)d6oDJ8907X3a!{e3Y zmX<#8eR(|16>d-|QH1O}Z{d!`lP_~!Er?)$o>k|p*1rV@qu16;o-R@|Fy5fEH-9LX z{kdAXnfZ8wzB_!-uF>P~_VQd`FLQSb+U&sG>TqqXsZj$5;Ftayk;u8zMVpV!%48)9 zSAY9NKH7AKYk|?IN5(;zO^1BD*=&beajX%iDeY!u)_*vl`t7{xtqL!?a>$-)VZGcM zJRU4I=c?&A(v2|G@$mcIK`-Mmjs)r>xlXrP=grj+s)?NpPO-J7%9|xkTw>yIr_1%B z$;#dwfolGVot>90?{4GA^VRbUD-Vx8KYyF9*Rt`^_+{($ma}cWaX5K-7(3>@EBw_O zeRGmlu!V4QtRWtUW2Q}m^F;>6G)A=Zo&IL)4L7_j6QT0vD%o!cnnnltMfdvh%KI?S z4d?6mMrXZ(z+s_e7-HKCvZY+^))!9WCuV;%1?;~&|8U(BmyrR2+qbl{^LK*42Q$@$ zTK;uxun^*^v7;^r`e`pitI|8 zSoCR1k-ham4o3&fY?X6 zdnf4OB_y2v`&WF1L4(y}f}2IF%R@kQIgX;&aQITbK91m1md`5e-8B2c?_|E2v3CHQ z{SlM7=p}|Q-R<#Rh`H8B#M=Ta%kbNk!@5;o4Z)B-InQo{BA>a5Ztl97eBJdSk%Ewk z2@#uB+T~33rn0|diCoK+DEr=D?%1hSXeNMu3G-V+KpahHzxDWbH>w@Z-$mU@WuE$C zcuc6@|2drX*SDOQ64s8yZdNUuGN5*T3d{DGL+NipK#LCu>K#z)+wyv=Ue?tm$JEGJ z%>L%1cL4ydcyzM1Fl~Sdj1;4%j?-g%l)~7q-^gm|_7gVc$8iNNm&%YEjG`TfVbJnQ zEiFa?cAjA^Hggb{`Z)i)UM!gx72{2=YidXSS~IfU!5FY+eSlFxxvF6x5%g@{LlT8P ze5*{FLrBR9#9F~eawQ^c>cgbu{DS?7oh&xg=mQel%z_5*3_6QM1&}#E=GzInmI_ z6VdvlGD0!pR9N)OaU`-U(bJbPgm#0u5=OVaZFZtat;%r)&oI!8H$`G4>U6#)_y!^J zYY(wZqRxg6B9XlaxRV+E8Hz{_g4>vDx|o|U&L8Ob{OJs7QL+a8q9vg4n<`}zRDQMpZT1Q9%fLd z-HBYcYn@^evOcMVZU;j&NOBwA1nGzKd{KQs&}g7R&lCguAL+RS-vU>7e)7+QrHCi9weX!(P10)dbtfIZjX<_lK2qVKiM}rGOpHJu}?!U0l(QDu3t$SL z&EM5X_k-t2`y>j;aqNmCA@jXVf_7iT#=1oJmlvqx%|Ix`HNw@pE3l{$9oUu?HJ$2mEFFJk@smr0DOLm_;_3Hg^El`(QO#17L7Dudd6HLIhD}J zI^eek;h8r*xj#9x6NQoUzS`=B5449|&hs!#9=fF|MX^$ovrDE^Zs8HEVsOA_+lf)` z5XwP|#_I%S^apxsb+WQa!9Xh(sl`&UidhK$K4xFvq~go_)9JpG2NT3BW5rxwOkYS8 zy9n-JyZ7Pssk+h{%rF<<@G5XeSe^>0 ztIs)3rfmkq9$3vk~EmUd0O|oxAgt(C4X@;Hn0@&;|%y-I!>0M6t zN`vIjAOaT0h#@S<_WE_Zq)G2^t|s0IQ=|ton9wEI8bB~uTB^t+nd}rJ!C+aMN;;j_ zG^Ti}5>fh2Gck3_y`PCE;;2(YR>lT1R!~;i*A)YZEepe3c12X-%I)0CASP)!c6NV3 zsklTpi3nG|9K(SkX-_t57>hY0U{RODbaoqFlL-uX_~@9J7#|;>!EkhT>vfmaI^)Cf zhlv0GwOT-iKqU~1vY<_v^8J#aKWo_STEp*VJv zn%FX?Obw`(8$z}SZcRe6i?8u*u@ddE2*dGVknWwbIH%uhnWQ+OHm9O zJ;EWIX)ANAfrFC)vL&4np2y{Svh&rYY$T%Tkt!TMZ$?}kQ8KenguVeiJ-wsCcU9$U z8vb33hj(|qD)Rdv7&sxq>46&^-w)B*uWpQBKU!1r9>ARCgTJgw+-e`2#(fGoaRELXK$D4Ff{ z_O`LPDxnxJ^g_kN6hk%ul`T&P@KIQ7wm27th%N2AqnSa12;9}GX8%3Rzg993^RjfafPY`6d0-QB%aB5M)!JAZG9 zeXlvXwQECJkP zSPy7)S{Uf)%?>AX{t)nWrz^FF0aAv;q6!{mlJ{x=O7$#;II^&n=6w%$e~9HvE(m1O z_Ze}6S*Su2U?6@q8cY1PKWfEDoPHIAjp2wnCCZ*C!EA7Mh+D2yT2ls=Up#jh0yTG- zo-!Q*M$S8(*-ja`qX2WqN&AQx4iZAowtcQAl|%@_3!WnTerPu7ZL! zUp#4B?!({A09qlsX0`!3y1rU&HPry z!(%RnP}^6K23_)()Af#v&;B`vIFopsRLE?WYl=bgzD1pY2 zguyWtDHUbHfLUl7^uYt%ZoK#d>DAT=$RJb1AyZ^WvPLYV)(MDDof4^(E!Nq$I8;gq zBIk_95>8g@kKSLp^OE5)cY~_2()jETV+GTL%8&nxab5oJcmb;G8kiRVB1L@^WKCEy zxtBzdxV0>nGB=H-B*b=*W(o?Vs@Q~(U>GZxSaUH7%vFo*86#j6KOb82X&mwsL_H*H zh4P@^uhq5(++%HZH5v^hbug}=_Z`wKnfKMFvr<0+>fsduC^c-K(SV z6b_1?x@6t?G#`GU0%|74et{dKk{UUd32L~4sBeamiHcR^Nw_+}T_G^B zOUTtGE&Z1<3&$hAl`VwWeQzznN%SCr(QQa;C>6Fhl`}wy*DOFw;MYST>fIbJwhZvVIvrl6K6|%Lkzqi=w(BUu_al#p1LcwO0 z6HKIoRpTo3{L@qC=l}ZU*w&#XGN55<@>8>SV9R?eN>a_xKp~9p?VNcokN0IC89%!r zkK$GAI|W>QQ-m$U66fb)1b$k7df3`tvjDNEfTE(!V73bF6y=iW>kPR-SZ?X=zJHD3 z*bSy6n$2|dJd=l9no1!*AnM1ok`uWgt+1Vjsj5!vYHt5E79-{-1iK0{RD*E0%mmc} z*Ca_^pXLNPk4laRXerFq)-d24PF(xUFjO56a>c=v$|-Cz^vnSv+n0Bsl{B}xJy>Xs zhojM^@wl@+ULX>S!eFrg5=_6H=eH=yP5G^(qA=2a(Gy+|qOX}wWpFrO&H!K#P!^|l ze~ylhA~2BBK%qDJYgcSQcz}i@tVOiJ#P<%AkytP)6YlUi#@$3>J9le|Q6uK|6<)xOuL~n}d5W_+3Bv7`4K|rD!KcMt&!ekdVklxB+zI ze9_3dCjYEu0M};v25Bjf!og1-FufV;jUif1w)1GdL)>2uA!BkV?4JO9)YjI_=0sL6 zk;5a3uGoN`@U2GArtA2jkKKaynIBika8br(3y^C8&63#nddg4A!>1ZU6-1m={xy`P zP(n5qu{0DJ&Oasw*RL-(&?ok?b9ZEU&BIuAW*D&UnykKcmB*dnwHEw-tAI@z=xjPb z)vDjcpUTwv4?Ueh-*Y8C5hmsACmp{BFIK_mr~sCpRqY zoz@J44SWc#<{_WaZ_flJ9oz-z$;U^$!$)UmBgZbems@svoqf1aKX9GeM5-F2Q6zQ3 za3{EOmC7_GV<>u_k)DCk(PSlk&d2+EVQd)*i9R~%R(MBb%6y1#rFH208UX{}^YMh3 z;LD=^fxi1)kQv`KG87rz^Yvu8x0m|kJ!)K>^S4yT-pNa_R-@r#~ zTR4fH6M-RyV(Yp(oLcxr=BXZyX+*BeY~CJAP@f=8=oUH6M5`R3J2rN!1l)|jjdm*$ z93$a1o6noN{Kqm+=0;OLF!-cce1~j0)vCcD;IhJ{L8@5(BIGR_Gnr* ztZ79Ub;j_UvebT;;xxh<+mzqi%;xoc?F)ns4GpEx;rn2@n`9q`_<_4tqfaI(qt%U8 z?RN2xwUvs6WnpOO5WtQEk-Vwig(=Vu6+2SuVv^RygqeTRs>?9-fJnj7QHol)(h$bf zUQ!zwsV${4FE3h64x3%q&}V6LVDlz{bRhS1t>YEDfQA1QBEoWlA9o&)pDrA7 zm1){hI|4l?6q{Jx><%7~mHFUV0+g$05dg*-63&6hVgkRYzO`RdN z`@v1#Hj_y^Eh!Z>mTrdPeX4urCL|3kEx$z6WO7Lwjoe(G7BuN)t)D{z&|H1%LD%N} zi4?hVH6B6Epf)@Jm|F~rEeZd5z%S+AX!WPRh7W$^enB3{n z?cW_uOUo$-!zFU_2;J?EQhp2vRSpn}h(Kc6+f$2p*#|=XkQXgnIm+(ta(jR>h>nj( zzFH6M7xW2k(pOYV4Lv5r0SHNJ~T1TkSNSjBhCSABq7`tcMAV!wJB<>k0(5?oSwL+ty!G zB7fl#sQgtbHB!I26iPs_OWn#-DmVwIu<`K5eWp>VWlBH{BC64sDGd&b^YSjGQfly@ zEIi(~*mK4h=;1Mg1BAzg9Ip?O-8uK|=L-B!I|dn(i9>#|vBmnL@VBDFV!+74M&A4) z8XWLJi!?y~yW+S%*8TXy&K)gVd*u9M2cn-WNGQo4p4}wnh~aaybL;(M%kxQl)4heK zquXk&iO2CYp|#*YWhM&+*eaaueRe+h3kl?zb#`ZlMueW&+#5~W_w&_KvemBfFZ)<~ zsa$T~wb#e(hHQP>7`_WMJ41TTbKTDQ>L%KJ62rSj`H3%K8fi+%q}9SLRVuz(g9x%6 zx6NjUbUIrtJfj(_Wf7gGlgIvuJ{#NW(X=k546RN_N;3Q%w0@LMt6iaf)?A89)_j(3duzkBTkMhYituWXI$3-?u!7#77wLw%UOSC;eUGpJnir3RR^6VTkc2WH`|@B zydD-+&l)Wj+r7PE>UAem5D%~Dg*;*h5X13RYd9FCh1pWy99ow;-AhvvCS_|uX`^%K z!d0~VL04=`^7^M{2-Ek+5|^ZAt`!0J3#gC)6+uN$#xO9K$wGkQ3s{ z{;KcBfI6Tr`Cv?T-<<7FIRF{4d|?1WS_Ux3>jIx2P#PMq{O5Z4rkpEj3EW4D;7o?q zddp21Ds>U9&if;}E{in%*Zy3glzLM%Z&Xdu)e*)n-Fhi7Xo5TQ#C(R-3Fc z4-O#RPM0dNYqftsqy3rbc~~gj0t92eUFCXjPp{pk2hfK-;qO?lkV8`PU^v_`dC#xU z4`!1Ivy~csK-rb^4vju1n8D3+6cjMspIw4Y1Z4>1H27t9M3orkWcu7zEa&E&s*IdN=YR@;b4)0EqB|n{r$MBwe&sS zT{xHw6mX_8Tm!`LSi>|7;r&POu#-rg_4sj?8t&B?vd)^f-bsCA8$#Q1|HDKyz@1pl zPIMOUyA2{iP)J4+z~Xwmq!FnE*|mnlZ02+C(|pr8pLt9g9Y7zJlohc>UI#3nGP@B{ zGSbSb+8wdw0rr?{C5lpU=mE$mAF`+jhK~aRYh}u=wd5O5MKYOTpsNI~FLv7lp~$fD zvJN5S%2b&~Hzp>uWi>M?nAh8nk1w@K$_fJzvY|p!AQnEZ9U-i@i*Z*x3zI$a1A0`a*xKc8DP9)HNCxg;PX+@n^tHr?h_ zS%$(oL`J7+{#SG-=?~DIz4jp+xfL%rD|PFPzN)*?ZsW-sb6Fq~^A4b)r2TZOl_f8S zG$Fx_3`!~7M@h0yGP#yjN^eIqxflh5{ut6@r`zLlAr;UH%^cYE1A#bItIe_GXxj80yM4uyos4f^mmrA8tt;6GdiIYmz zY_mn?44JFb>4pgOM@I*g7^crfbWrqOAP+5AE%0lue-8@ly5BM-@=?2k!fn@1RQQj) zZk}GN9rBQsyK_#sXKr_B@8IB%q>IhYw_bmUqlp({US2xYDr|BY7rWeiv1q%agl3jC zE-Sw>l$gs%Z1yIrHSlocj|D)$U0=T)Cn#h!gFP$YVS(?`$jg&_hGCfd`@9gY2Q`gl znbk66=w61vuQgq4Uw+O>(mNinbN*SblA!ORP`RmTv_KV0*~OdC>UK|dvHckmdmaBg^V`j+}qDMF7bg6t$u*F*H z#$SSwpZ-%GqZnzh#&2t5edB3!d`|JN{T#=3XF5iY}#;Xyy z$wSHj)y#ac>~IGqBn@a*{=k>6_Xp|R9|{I>$hj!o;qA%@cf>Nid`m$M3JMx=#JUk-XwsB&1XrQRM+606J2xo_ zb2VFK%8qVNWP2fT=qY|yRzIyJaM&+JSWIB{h!R3WNN5(-W?_FYnyrZ;md~BsNGo7K zyBP=QG-S7O&#XHrdIgnRa@dRtv+d5y#nD!Gw&hKcJ~KkpBQs5yY_zTW@U?e>b* z&*g@J?;dRrM>y2P;PZM<*7c&CQiu{7&==S5CmXV?dLd6@H;7jQkH`;OzPNH_>Pmas|fWCe@wKc>oS3C<+x0H(;RPp>YqPD7b%= z?LD+rA;HA!G|?;o%DzL)p238(Yl_SH_wB6{D}gS63+$EWiQhXgCI&%pP9l-&^5n$$GSL)Yb@^9UR}(7L zzN3A)IWHDNx35u`k&i2ol==fKxl*S6tlYQkjOc1@FQ+qPYkZQGt~*JRtaZA^CW zp8NT|AEAG1t-aTE9>=+#Vi5V}fE!_X$<N$crct=91ek!u_F>P45hWf^{0e?)p1`RZP)%b<_d};bvP|d#RQdo zF%)!QsMWAug&F*$>fhGRtH4a)F}m=fZH20}xY-}6AiBg-UbBO8TfQoHBC<~3M6w`; zusNR}j8c#~6u*6fM#n?GwHT~PMf9T4@kE#^+|&sZ2!d3H$U`Ll6F#qBo5{6Td7>ND zo;(0Tkp>z>ax%02&=ZhCQ#G6LZ&CsPm)vUa*o8x~ARrA=;ap8-GyWhi_PJQhgVA^! zTE;gE+Y6fXPhU0SYK zS#i2l5v>oRv5E$?tJfK!h6L+Ic|hMf@ra7fW~yaV%i>Yex1t#|7x6Gr9RiWTXv&qt z<@%&%lTFyN`Mh+ZOe!-Kl|+>Sgxk%259h}Kx-LTS;n`UPvnHTg8Dl8I*ICw=`OvTj z$%KJR={c^{!QCP6D{T@9Gl;5+HL=>UT`kx0Fk7y!Eh8ki`+Slb= z?-Rtpp*Yu#{a>ooDe5Q2n&rO~KGez)1rM9PqTi5z1X)BkAVwD8plBC>PPwzMot~3(aai zDiF&hlf%_K90*dj@Z}YLfj9fBjkRyBb$^n_3pK1WRGRTgT*uNeZkOn{l(D(^oEml9 zg1YJYkT6xWOBG0MubdLylWXT{Yilu<-4+lC<%5F|?11Zo8nfe}luB5A-6T^8(X{(g z5VZB|ZJBs8(R+&9G9qj?D|~GMkfot>b7NL>xy}AAi^iPG?8W^;O|l@HsOxN_@rS0# z<#;-{v)boGr!C0LJvW?xpV#K0!mape&LX0JLUCfRY0JA;={Hzu8swl!@RK!=8{dQf z7qd;<&%C&7=Lq|DU6oQk4#_TmE@sdpfQ}DP{jorm7@xiBYI`_I05&;k9Ea3eaCgmF z5u_rxM9nmD^_G~c&pKbumbatF?F0-9JhxYyepiD)%uTV{LfO&dWry2eBHG|k95nES ze;Y&WWN!%f<-t0rJyU-=UliWI+nk2t5i#KAz_R`G1D&fH#Uxun%X5^^a!@BXH>c<^ z0O_wW*H_T&zkTpc6PYU@ATTqp-fxVYNlom3f7_wnzQr=GcX|mYnv9BvV?K+c(k9gq zK9#N2d6BARM3Kamrhf)(T zcC@drugK^kRcx}fwjyv`TwKE%gPfBv)dcqbch=_O<|?c_ykzno-{2B!rl4i-c%O)~bIUQVP-+8ky(j;C zV*=HWD8_=vB(e#sz+PEaYgUa*$yR0W`x0^Ydeya6DH;=xN*#_B8+3c*QaSf@R*jA% zIzdHg?U+n@G#)4E4_B58YbG22U$I-617TeXCe`j(&;!I@BSXr$^!NvP@t!erD5nI`rS zNPX8uib#RuCM$|7c?AFrXoL%pB&JzcFPL1E;T!uSA)TMC7p2XX!DIO@$rajb9y3G{ z&W0F}Xo>d96#X~?GQraOJNIWUP|U5)7tzDY#z5?XiHv0qPpCD5NEioS8DeQsk6 z%0$Cz>1Yg#O)Pst@ozKhx)tt8((?}zi$p#duGHCgypWLWyIM6?!20=RQK=?l8yJc{ zqpfnaHv`OszG?KYUlq96)TJ7u5g^X~0FGH`h(Y~xJ{onEV9;a4<7%D@Iep@hPh%I& zUQMa(T%)lJp)W}?anaD6?c{O;j=qaVqArw*dLMI)h<5hbS{~|9fMWE{u0KphCh@$~ zs#z9@afU4*)!y@y7ksCuUgr^gN9@$RZ^Y*ugue^*52Rhm7W}Hb+^EGaA0R^v=qMQm zp$;XD%Pgi{Vg?^=g9M|KI=w?Cd0*dzftS8!vuGzgRuI2-^v&u?J(IHp()`^7SSq#pH`$^;1}A zc4C);=(Njj&=h7m5DK$lZ43#g&fwC7Qz5YCsXtce+f2Myd*0gk?uS-oPpAzV1ASKC zGzz$KCl80?0u&EHcitw`$fqp1-D45V)uNx%14#)Dhs#yG_Jfsyo9VSD@$dWXqt{n2 z7TJ(m^d+)dfmwH&ynNbCSQ7w{EMb-NAG%4ui1)EV4a(|i>87;}Si}-!=v>nGVtt)x6 zDNd#2_eV$SgATDIo3%f* zg;MHCegW%tg#T9e_kWML?^`!rFOgtB<;#2cGF>UcQ0tuW%`*E15h=MnW(NUcPm)xs zUj(Y2;S^W;+p=H$aKUmIy#ABxJjDyhpL zPG+^7wYtv_0tHI3F1*It_p`;i=cA>c-kNV`Wi!f)sgpvoeVZm&I%eitCu z6H_K!f=F2G@4NhhDQ6Fo-# zToV}jX~*hei$XZPE~`Xu+CX-|KL&Qj=$yY;Je@~KtYN%zIg z5OLb|di;UtHzEVUI>Ld=Y?|OL5ga74kJNGj$xT#Ygk^YGa9I{ic*$cAb_$q5YGoGw1R>F25x!O=J9wx*TnJ5E+Q-} zp4$c#4o3|bjg$1r(a!FY9{XV+BMTA>=lMBbZAvGZH2wm|9B~f%j=Q;ASwyM14$+G| z(z9$^{>K)0(`U4FzmA)ha};b?PBuY({(cAz|D8oE=q@)r=sPAEY>06N+dg}WBt>EX z#1d%cX%>gNRB^DDyuXK1HJvkp38*9Q9X$_^_Sw=@S}dPk^5{WgWAAz7eZ~O~56@M{ zBQRlpAFj#EL1M~NEVz@!wo$-y099gS0@EPhXfZS#5g<_g4~F~#a0B<%Jpj7bwO4~; zUW9p`BN+hMQuXvKWaKs28--Rpi_@8i12n%HTD5?t zL0*H9ZeZ8PON3n|GWo27{sBDNNu_1XVyP9iDGi*`5=I z`GK#|R`mNCZB~ugBtthA&9g!lYozUwbVI}xX?t(`PrE4PGdRztUzA|aGotuV{wR{u zWrV!yW_Z-zf^so$qWr<)yM_Bij;#dqY7^ohf^}#p8fU0%SrCfjujWiO zCD`I#!*qK#3`^l?dbDqwR%`|6`g+?_g28uN#|Pi|Kknva?|9WZJ=u#xYYkY#%_FEs zWxu-&N9w1x7G`}e~9P=_nkx^fN4#Jb}QX&bb&7*0@e!l+K!naI& zLzhZi7O8PtnD2Rt)l`N&CVfEWH}LkjRqVuk2}B{>2(VL<9T5?^xyTP!tXur!=Ns5R ztkX?htgZh%u>-erG0Z*PdnH8X(8 zbW>6+w)a1HT1|*=>6qbjEZ;N|vfC7cd@7UO@!?#3&@2;%K|44w(QLJ$f&$)L%kTul z8b(-7MWJ6uLRh2`2ZEo^*_ox+Sn`FkXC#pMxl)U=l`}Mag7@tjbkY1$|7da}EbMP; zDdorZ`TqRv3|#XM59LirLPLra?7wvr`1*Xy#Q96E+L-5~r%!P4lJ7N>*9RG=^nn0L z($3q(=XSyxnbK~hQPB9z~dxBaB2~4G`u}MWo*}*D^)5*Tj7m8Nk1%*aY)!x9dP>fOVy>83t_c= zDe54j5HW<=X=~Z>)Xr-?9^KbFGzn_$446z!Po4BUWtzP*%G7|7qFOomw-y7V;KMMZ zKlP1;l1R9j;9Wn@(8v{H`_gb|T+euTziF>Isn&hJ~71k`kQ8$!U}2(xi=-VzaoGq_x? ztB)=>HH78(Oy$>I0i1}%Qk8TD*lRGXi-(79hkXDw8l^&ka=A)-X)I@A>}G~KT_R}n z*3!~HXmouO$i8ltSAW?YiBUK?WX;P|B{31`wFLaPw=ju+zxQD7t zIRF{ZY$4Nuz6tmP3(FH(5Q%l%%R^-$5?)x1MLtOjVJ>%U;ShZn+w$_TxXWkhTf-;#5JK!j!f)ENB$gkl0NKIp6s;FV9wX!tSn+tnO5u>GX0NaT7dg)YN|($LKfQk z)~QbMw)1DSM61OC6ao&1%awtdOg^8QjpIhWe}it4p7PR47>ph~-~BZx(2#?!KsiU}PK=^%Ipm!ZbR?CO zC>#TL0Df?NruLIW)Mk-2JDs)Cs4;xK>D#A68<_(E8Y?}r`9Ru)55*TKVkD7C z@|6}aiy36OP{fb{UsbR&n~5|gYPzaavxHbypAYUf=Z&#!uZITn@z74_lF5>m!&fH7 zN)6`KcZD}F{oVsQVUg_67?;Q>+njMUK^z~Aq&d#%H}Pb# zo?4@)IC45Cmg?%F_W4TXA#R*oE|fA^4uq>>&C=w4(cASIQ1>_(MNLQi29_Bf6(z6A zcsSV1Z7^4(kw2+Yt%K|MEepgxZA(%*tIqTJcruGntP{ob$bi*$6%gtG;dJRtsbpKJ z-Sy05f+~e_uEEPgpteT-wi3~^^Ywb?X<6-_s)lomCfdlzVxdfVVPIgd+pl^k#-&mV zv!g|^Sj2Q{6B2f!uv2X0AQ6L@BAs4f^7x-b0VJF0{E~Hk;;)N~4ndtyU~Uzg3S{ zPV+$0&-yYL#fvl8T8*~aoIJ$d1*5R?>HcG^qDvW#E zPAZu!B%K(q>;3#lW#F|GgTXJ#|~Q?p6CO z-1?PT4MDXo@E=y}f@9f_W0g%Ws4|dJYlGZm8NbS5p$!;z45;A&<~GJ)4R{qF_G96O zS}|+{VWa%;!RWRANPEPIR1Nsxg%U|Ox3^62Yy1c9BO4uV5om9p0S1A%n&+TCf=Fy^ z^1XpnMZFvj`{Bq#syRH#gP{&QggbDmLwM9&Z4R3Zzp?VXHrsiQaHz;#lgf0`f+a!X z;s$OGg_~H!^85}c)fikqy>}i55O6V$?O*QX$NvDt&T&u4>;0Va>?7~f_VVAo-~*gN z1;3X;SQMf23yLB_!cuBg@1BZuFwGm*B)g*#s8`Ps`xTbT4-#r*t$ zZ!D{YZz_5|nWxuoKWJ9#LaY0m>k2q5kfn?5FxWQmE1^Y@UYE({%gmmAo{(R zS1FJnEVF%)AZxLSs4~vR#`YUuuu35w1d~7K;ZnQi*9nr-Dc@#w*9S*JL7kqaNET5e zigU~KI|y^1*f8_Z&Li7jG{Eb zATmD-lI_}MIjXLGV^^msX zMnZ)5fK}xW{9u$NRgWd|KMm?Id}$ZGA687WIGnyl?eCMYu~D6#0weY@5Al0CZMcc_ zLY+8B=I`2wj8!sahGmS3P_spL(bqL>!$`cAK(_O>$y(7E135%^<`Cx88DLCI>3Jhf zbT71o zbyjL3op;?_4VdZNAI`4LiN)KFs%LJ4Mud*Z%g2GEBQD&ZMY19Br1}2-`*1K67y?Im zLn?WP_r_a)!4Q?BZ(tv>AyX@lt?VC7G<5Ybyvjm`0v9VMEcMG$&J{zJaS0{4_wDXd` z7#}P!qI;ro!>rfPpdqg(vN%~o4%2wGlA`OaR~uGZ9LdcY<_%Cli2{~E8ZoW_wvdUs zp>(;Gi1Dc_LlC*?25Dr5e?(BVV&PS-Q3J;|1socyT(OF7WnpadCO8 z=rxI+t_-8-(e98lNxw zzh5{yEZdFis~kux`lNt)A&CaWT6I_fL1gwx<(*xAc+|}SaeuKfRs(F7Zjbw!{z9!61vsfg3I+8G-ZS^$C8O%MP z@A{PW@vb@Z6vI~#nuxNXO-0ww3Gx_nBXN!@X=QDt0o;Wig3!)#~=Jo7!dA=t4Z5PzIY}ad4&4%YBLjDdlJ~OLCrD1a7w~EF=Z{ z^xs>{m#^@j_;YqZSq7wEu8mFLe3|6at+f78^RU>gV_`K)e4aw!FcE5|sOFYN%poVD z-1~DXBQ69SiRPn?yBE|S)LsW4d11*iV}tB)e3;`(Xj-@NG~%EGzWG6!fvD=Et8V;@+St%)48}*%I-dIuGs==#>&r};^H+ME?Zs@r3kePZvpUdw#&Zi zn#p6`U-;Z8ox(T-G(mH0GV15;QL4TU*tr|Yo5#Hrjp6Fqf=|}aEF4`!g)CQ zMkLKw*VMq`M_dji z4#J*1{zkr9191Se#se&)*9YaZc|TuD88_F-Ob1x38m2#H7QbABf|JKnVwU%SnNMC( z3Wa8qMn4Xp_6pKb zKQm)}xo&v0!;V091 z6loqp;EV5#=6Qi5>2T>IxI55;)ho{i=*$pwqO`>FiWV_~p?!2?y1%kTErMjrD9Qc8-a+u?IdvZUnHPOnSPLnEFGehNYqT6PXM-}FTj3`0x1#iQ>650 z@&KSU^M5k|j+16Xu&PrH!p7u;EqounzQBcjb#Oa|oz8w7L~EfhHSXbzH~nfe#gMvJ zqe+B0-WDy#@z)ULmW*tB`9X*oaJX1@4|I0aBUTMmayq*{f-8vB{Qe#wYX(ISDL18!Un_nkztV{(b7a8KsUm91ojH zz7SG~hqSA2hPBMq;V?pQo{WHovPByJbV>qh0w6f7xTgIKmI^L(z}_e~5*T1KYu z8~5#-G`U;^lH_q?nLwmOIO|f49CzR{00B50R|`6a#R)(i(!3cFST$%mp&I^VH%lc8 z_F#S8=}$6MrwGU+d;)N#{hi@@G>8HMWU@gI-xK#?72PcuBsXEip^TFW+LAJm9NO#q z_X-!=a35*0Pt2dquhOq&-sLKB!_WhHG5%8b(YSMvI?riWXOe^HBvDH%SO@5(hAvi; z7^PYiMMSikE8T4S8~!vul6)OkR}IJ!I=+=kvs)J*WgU~GX-1-z7txPh%r~34TOewQ zte~ZRi(VeMbC#uSvO%TOPFE!A)1*^{IG*UY*ug@d0F=P&1;Z%5WS~pTF8QVPGcLr5 zPAODk(!nyElvaJ0yi9#WJe-BEgaopb)+1NH=Ql$+GtWZ=-Th6(fVn8e*GfY9fC9hx z0aQYa^S&^M|ihxx4zrvmAk02_G2u$iPqi% zog*O7(2%k4W;}eZH@Sids}1s3U=d#Z0Z(>1=r4~Id-om`S5k*#3u$Z?$~-j-ii;Vl zkW@wDAnq9XXpXKw*p??Zv+IfyaT{wJlfF9eU9xQ;$)|^kp(M$MiV8}3O8ldE zxsD*ss+&c&yM*|DF2rRZbdvZ70|_U=;>q&OL7o=^qv+cn%?uv^(kn4C^0-`r>n}ta z_V&VkzBzhIji;4R)Czr{;&Kp*Uf>rKyX)2MaFNM$C4NWBtizBO_C@;284v)k(^^2M zIz%1~*0E(eDgX~JNKXc2rdR9DjNa#Jy9FXv8y1`GkVYmK>n&EJC{BdFp#+qex*~ik zRt9-g#6#yJiZ!2UO!5TCfJ?cWD|n#Z=PMm-y=mo*-Cu#I=VUXLtqYF_=BAPbNXp>V zh)X8R;eveySiVKyiwF8Q+J@>8xE~dYWrXxfefZ=(&1-|&VMOZx#IzOzq(9I{^lvT^ z_Fu*`ztZdM%!aG}Ho*!3i4}i9GSSpWEwzkNrFh=m95{m>*TW(?9oxddUR2$gj+Pkc zhtil7o+_R~hl!$V`9i%SG@-$f54#tIhNwnFHfD$F+2^O(x zuZwsVttm?Yxt}{fOS!W+dPdDhJF>-(J3B$5Nk|5?y@7oD!$z+&Hbw!y`{+bQx;^wbl5tyOt_l-*?bQ)kF z$g$syZ$yYvNBxv3}-g$$W6-@11=dc1YD^9;E;e zwPEP1qJ7(6#|=oxCeLESW{?xD5FWI5j># z)~T&%YLgBbDNJnE>oEcJV5#nR0UhB{PMVKvUx4Rki`$>Tajo zPm`N#n>T=cb&5XEW+OhBO2*(eFNrzsij~TdIDD75Bx< zdyD58tEV*P<^B~Y#q+DG67W(&|1tO$J)=VcTVBlJT=BM6l;G%cj`1?d`Ad>34bS|3 zBUjL@8_lq6CWd#dF=EZmeO{XSVf~NwT&LF_xy&2OcJDVVPK7C;M@62#^U1)(EHxrN zZlTa_XLfS(6jz7WCEAM7N#tKe4G1crWKm(czu^<{F&@Qi*&sOq?ayO1jb2cMlh3`- z$Nej;wwqor4Jdu1;g!mGWuSC^0gD%vOoKfQkwz!PCC_$J4A_T*;yoJSJx2 zwbq&n1wtk@t%RroCCL5@tM6yk9q9Bdulp0Vt>`F?4QCeR&wR_XuEv9KubdpRrFkU80Pf?NWvM?fc`_V-{Q7utmf{eoKeF#{=Kga=5U2{CW<^1S(L| zvVV4#kp9BJo;q9##51uvg%VhUUpPI-(C|PsIT|~S&l?ctykKDMPJjXqM86}pO)1{h zu7K{rD1NX-2U>v$O0MSu^rQi@tu!9(#1bxVF0DX8&2@{(nS6)r(B0bG!!W^9h0=+z z(nx;r)6<3W0d@88com3j_~cNL2G9M8k|pR?W6)k3`tG{incP;bhx$3Z7 znB5w_h4oFD5FvN2-^JmT`=;G9`H8>dLkxT)-0#*~n5(qd`QuKN%Mq4;U2k{3ze4=F zYzI1e((##)F)%O^FH>174iHdRmX=tnE3JdRqn5-9Uc$f(@PkcD?W;hDpZ=w;&ds2w!ggGP=Up|yIi2w2)jvI z%7Y`Cu#|^|&*WMu6i2RN25Kvk={pjqW)R7LR`~Fm%EVBsrt>zVLD!gJxa({)0t8?3R%W=>;C)X+(tmYTL+wA)L-u_L7A#Id_x=~nHGe*L7n zsnmp`0)b9z_vt4)fVa$GS81e5ubyu?;36fN$KI%!(i3=29S&K$% ziHkio+Z#5$+zT6*6N^Sq3;J`Y7~|qDoo{HC>G5|;m#NGM)H)cC!MUyyI&N6=@L-ge z#vWi|Va3N@i_GK|HM2F=p_-2dUsGSeuzXtFMT&I$`qoDJ?lb_=lo%L*1_NfIipD`` z8envBa$Gt%XTJgDmLw)PQLp+!?0h|DWEkqpVKrzLmQqm#F&5!Z@p(ilZ8czyvVb*F$-}2nSgFl@Qv1QPcLz?czH|5N*S=8+Ze6;!3>=l0Hw;V?p3D8fGJIPE`_l3zU_Q@R9<))VDI^!`hc@+-jT-$niKPC6 z|Mg4t_9$;`99T0ToXnp_{%6vH18er**jq#73oJ%J4T&Bh3L+vfl;Ya4OBxI0poV8+ zD$ytil?w{+uX4H2P^Rh03Aaq{+S`J`cGmm!F8i)%X};4Nv;sH{4aQ@;0}=9L)29vx zlTj+7q+}830v&BYgq6M}?Bi35qgYuXGfvtLjUN5B2Y;((<{S`w!Rg#y>)jy^g$nR< zUkSQw*Pm{tb00t66ji2TfAk{5tT+;4_G(w_jGUaczs&wTIaePCS3X@5%^57BRndPR z+~P9-yL&h}83qNVU_Af^i{IALl0>N(+?2DV<1sNAU!Ekcs`@V`6xnKPx|`z7b!!Jb zA14+S7KUm>EmYAOBa_98Bl2WrPiyj1rdI7O(h}a}%|Zi+I7`UXG)YFr0$iWp2sJ3k zppCy9b`pUF`7gMO9Wb1|+k*yE0Zwf|(V58?MFx2VCZmWTV^csR`1Q)&-F-5Lx6Aor z70oAPI^kFiLU6A!$r9>-&gSLx|5*T1F*c@U0a~ER?DFnrENXqj=k4z!f7cu7Uig8- z*WgE)EUHnS{6a*M_WN~qZ~;BP7&!90uQry%q|48I|meJaKg^4mZ<VJX=5N#h~fe{-NNUgVQpah zt@D%4`tn)Gh=jSxSlxWHWOcE83FxI|-SR{(u2Yk2e~g^Tr@bU5hb5ezuS1i+RN#aE z*KO|z_Dd@HMU>_&T3bsiG!js4If1&c0bq9vu&eO^wF=Y3&mDvaDYfi;sf^{S)zMMt zXzju@K8rm-eCTt5PpyhkITU1U(yT7qGZUY}P(R&g4kh|yNbIuMcJ zF|s??7km8JXtGPZ{Jx6U);`Zgu#e4-p8D3Qb1Eu$1oAiFLF zoHRLLjJZhqSB=4m9Ua`8LpyyTV~v6Mx3We!>YBoM0L|%zf`dczO+hiii<_I*?bVt+ zAI`$*O2&;6FnT1*IZEG#%eRk7=Zb&@gL*pOQ}q3n4u_S_b&f1EvCU+h!t=eg?)ZvO zi}Za_EvdAOFhaBA<+OYV4o^v!lZc3z7Z_ZecRS#(cLW+B!g04tV+)irX57juMJwIL z1#1Z~DOHT2mh}R@7*zZ|}@N!^qnYOmg~&=@@6)UqVpG+M_r7jPTP! zD#bAw<=UVl(k%V_1}aE!u>Y0Y@~MbyBWkLsfD=XVww-^5*Z(8Ynlu^v|r5cS? z;QI3tKdf!2v_lRiQ)F`np~B(g8T3?2jVXRJ_Wp)cZxz_`hj2<#rl+}_bvj#|aTkk6 z@Dl*)Cul!T%U%B17Ejj8%`-G*b2x=}7fK&ZDvxGwJlq&>GRQr)G!1_mUU6+Zwr;Xz zXN5|Ig+dzihYC#~5SvV>T%c9X=KWESSg=qoT`ZGFSb6mI6g;=%EY_vpwO9z{D>1GqSzC$mQIXoeO6aPhStg^Abl_3Y2VL8Nw-x6Pcxu)enS}(ujQJ$M6&$S2KV;g~i3G1we7}IP61C2~$lDG+G`_ z=1e6A!{JK6pU8frQvRXa>3R6{cPZOw&7n5|(7V|B&q7NJ51y~Ddo-I`^*SH!g?-c98WA08lz1NGU3g_#w4y=B%O~w@Wef1n1uNb~qyJ^T;FGS5%Ilb_onwEOR=T#N9?~^QS7N;NtQ2F*_C=i0~-H z>QnBU>)6NlMGAmolzfy;xKyiDoa@f00GU90C;Pmg^Km|LNyJG64}1cAW;)G36*vjx zwwtG&o#jgvJ<$?hZqt}8yCYxCgNSSNV(__K?<&-%&Ey4;P|#PHA08g0zG{q>Dn$(? zCp(}tvf1qdN9(9B&OYG7R>#}2ZDsUpZ=3)4L&4{B5D#yswwCd`T4DNKFz;u(ku^6^xbr*x z5oT0Z51hc=b9;&x!e)!^NS?*D)h}NJ_wJU7PTpYlH$T`=*tD^Q1a*uSnD49Y>YemP z>&q7@e)FDMG9v5UxN{WWyoNtj|#wb|UFBFAG@5^QFilCpH_c9gmwHS3ov@tfoJV z5HSyfX*cD_V;QR=;#}_5-TnY4)k}dDH7ZTHj>T^i3*M}3qW8_q6bGW7beK#hP)V1XT zA(8OF9Nv$ofZpWgwSnaC$&`jEFL}Sa`todS^r4{o@wp?T-T?%WN;q6^2+bp4ed_a~ zDTt5|XgID=s>T~g8A&BNjts|i+VX(Mezg{+ls*{8Q4nIR9vuizDp#V6m7*dhmPd?C zS4?a{xaSUyda>Hzm^@g3(6hQCabc#>!9n@?g9VP)UVy!&E-D3!_!&}HS3N^9tS9e{ zUil)&PLGE5^MVBbv5{%U|}PkG^qOgmlM`5Jnk|ivN@Bf zyOTi7p?Jk#&p)~p_)M1geu{fau=Xvi&E3wO%pD+LIV7GZ4slKI`CP7V@qW0PKib%* z+=3>$2p5Y<{CuUDTW;b@54kZ%m8lK^4B zyVW301~;?P<@4EtOm>IO?YMr#_gbG#aQ>X}074Ma3&l%>g_V&)MHzuc+N0NY*4CLcvQ# zajJeN7U&j`hb53tZgp)9 zujeL1&eBK|G%=M83$N;~>&LfD40M_vp;wdfn&m$iIoo>d)jw?nt;0GG_wggUZ|GnF z=c)?d=iGfiboMuuC?%=!8k;O$wao?_wyz;ZO@A2+@qiHMDuntFwN`<}0-%$w;&GZh zJ$;(uQLDC?SgqeC$O>71X9xXyu(1&WgC!IbS9}D_!Ok|?#b1(9N`TL36nZ?iOD2yC zf(*Ah_4W2y$0yY8vRqF0l*{Gr+#dDi7S`0s=Ltb!TUyFH^#gSW#E+K;Og~Z9-M2~= zRs93byziCk6$;Y}m5bvIyVg3|TFWg_gM>j0JruMu6+9sOa!YN6-Czqz-s8a(e+T#z zDyIL8BLfp8CdPSo$Q{R~LW1!NjyB~HYAgTWb08VN;$6~}4EDNF1_Z*wu+(Hn2{hh@ z78mEicJ&uMTeH;jjb~-K(eP&v5o{7k+sOFL-@b>7t=F3|Z+x#`z<@FY0#0kSIgSCJ zN~%=E9;zhtj(pWsoH5Tmk#bM2{V5xh35kXV$;yh2pPxM(2U`bS2wucWJMIXVb8@|7 zEfGCP*Lz0+^Dz+Pwy8=Rd(Fe_G!AAl24D5=;c}zx(O)>?E2|ZxnAkO~y2EE*h%G0Z z7O#D8>G&tKM;7DH^Ys#WFMNCn3sBqI++3$kw;`X0b1H5Rm2%aXz6)Nf`OwiRDp9uS z%?fkHZ~?_{K_#qKSIpQfQf$r@A{i_tg{~v<0saA7jg1#c8mJ09DC$xL|4Rl0bJ;UdB{)^~sHM65NfwY1)# z2q3Nfpc*~C`!-wat6vH++nlzND+I-;^7?N{5+K8tbfGaJF4&l3O)q^JFO7|wh`Y9X z*StfZe4n`95xqFKLsDc?KQTbL0W$=^@a+7*hZQ;-K#ROT9o-j;4J1+`PcOoX*Q4CdzmyfYI3yThWA9I3H@p2GSKq*0SJ-VG zo+OQJHn!E+Zfv))ZQE&V+qTulMw7<2jo#h&9ru1?e81p~eV)D7v(}t*&FNGv;Df}V znOCJIy^vsLxxj*=2`H`5C1bM$+-#kJF4^-T_@34NR`9QGk1L|$AqC{eBx$JJxNqK8 zG3H8KCti`@;knJ@v>;WDf!*mgIA`nmx=wIWM7BMNVYW=N=~UKeF+xaTKW$mm)bvuV zUnwBk^0*x{PI12Jx>{w#q%E6E9Lky^BI26P!NluRlVe23q?=7>t9<+8wL3!0Wqa4s zj5$qbx6S=CgMrDSEUo4S%O*tyB!0>oG`}IMny^}20xl0pi)fBhr5x^>Zi9$I-L$t* z9vzDMp;Dk(&+7Af(|soB>n%}C1xqtP5NX0eLXTh`ux>mLQW1|-cp6O{d+t|_SGy~_ zTFlpOE)Wm^@BNU`O+x`Ayr&_#?NqA-EjZ{y7>?q~H{{%=hr^0w;lP(Pc=(RWQa)rZ z!{%kk`_5D>_CUi|6oCqj9K&t{QiIVUBgf?Ddgk&1)_ym_e8OgQd*e>cd%e5fEw2C{ zv|XsuN-mLKuG0P8>Up15si6kx;bv5wW3`ktmRjq!Sb>&+`ng#HC*^+H94*nk**0eI z^`<{4$!$bNxSt+3pcq)4GnleHx!e<5XCYDdpzE|zQ)JILyemCZC>XWEb7$koGrG!r zcAMTWJ%EBRwq?t}_$0*xUxt=8MhdR;Gd9_uD$E~!=1V}2v&?r2&bCpmO`&SBw&r*Z zV`lz9m?#~R8b+sb4JKMgA*bVo{`16r`Xi|0elMlF-IcjwAsUdHIbAEcVdCJRQ-lS$ zHYc*`B?)M9rA;PRAR0}JzvxP0=;tBgrv%7hlyxhphc!-{v!KAp6vl3kNxZE@gaRO| zfF6J}E`dC#I$xi`gUN28JFUhfb`6Bw!>xEU94RcO)T-DpOxzsXt%FH}g|dD=5xKwU zZ(i1xFRvA9x#|@|Y$xznFLQRgBW^Ou14)y5$C&Ce0*Qi}l~ehu#Y;=<@#P?Pgeg`2 z)yXOgupZ1L&n@qc9f+(x*cB7UO;Of6q?H}FvW z|7PmaAJzJA`wxV4+t@${!y5?%*#Gj;^!7ev)KdTwGxmnyF4;Sz5mbro?1aAt*q_i6 z>$fQ)6|pRAZgvX=MI}_Xe8&# zVFyiCR~KS@gjD556#XzPx2(>$AfdyXVdkdOp8K9t11@Cp@Z&S?C>$;QW#CYoR|Kv) z84K%-N4#CJT}^_uuFxz78@w|)Ij8rE__Jl zk{qTLirJ4h6?zTiuI}IKOd+=uj@XcEvFRCp`gU4jZ!3vfEkWg!Q)43-|uS}Q2c2xWaYg2mDNKs_7!cGvSip)2r9)r5rzNQ*SLENngYN_6>>y? zBc6+JE&o?A&2h3K9Gi50&w%9(aD8z>nI|075VTG<*WcF*7-FWXn)fCXwZ=YB8AuEH zrb$D;$}JOOk?}1l_+1^&^p_MR!yaiZCDSBsfZ_S`hF4N_iGgN5k5)e5IZDEByVZh- zf6&@UcXWKr<-sZw4(?{cFBHE;eN-qWc*@rBxP@Y)c6$CZ%0v)q{`Yld?G=C2Zo&!)?o``c%~|Wm6i$vNzC$JygbpYd~Rn*_w^KYJx2L- z8=y*dpJ0%=&y0RP-?Hjy(!gm!(F>ohlakt{r>ARc3WbX&AkX}2;>+0kamLBZ`7kn- z&4&$@QKj1-JsrP?PxuZPAAbKM;sI`fS0&=r+UZqXsX@9w-SFg*MDAIJFF$ zxGrg8h`35tPKFJ~9E_f$1UTgHO8<^nNhw1!g&jU|;WH2}(XUKU1~5#!+G zMgRS!6#|mRt=Z}3``eNe;M#^KF-az#lt|2=d6oSMbEqAa`J2?kY`0gy8=Zgv5tp4r zmH_|d3X3(QF>L5gj#>pf0biO*WX$9wwBfk;ArXJa{amq9A|-T%8_dg# z_CHNTBXhNAYW|iZ2}Bp}k&~`SPvXr%HkTby$+2@`;XXpz5nJor=XAI>TD$E4*J@Si zsjC0H-0l@X4fm~xWDhvA@ZX}B5iR;c>4bnKYD+=k1Ek9NFfk1R%;ev9u6?TcY?

|3@f&%1Ukv3Ob!X$ib+4rKN&1vZjfcYzd7JFFf&4pb zJg%visqC2k{sbsCsL)SsDfjoyb>;za8`j zncxO{-0o?ov~l5}!)ti_d_6i{pW(47v8@ij3bF{kZu>y5luYuVL-|Gc7|#o|8U>lh z{ccD1oFtVRYmHR%7b33Gu`F) z>2j@3Fh1$@%^&VVOZomb1MJeIT&U4^1TqDN4|q;wA%-FMy6*iN=5-;M&ZhG^O+h>H zSNmZQb(z{I7Qr1xEfW{F^Y}{5UxG_>{zp z1YB`bX(K2paI{n7`rebI+ffy^W2HNu}Tu zFt)$RsvszHathrpN5HEhYb6jCuO0gsHeBra4jB~vxp4ORUTlW?o?gG21|CNhuR(fE zz3FZ{OIl=SfY=eGfOyVzFp8EMw1(wj@cH%0dQGGgP}B(P{4f%S}y z4a5rdm|nM9#gI>VUxdPwHK8ll3JUlHKhoxA7*kTr#!~J(TZ~GiKNZVZ&mL;UcSp#My#arD@iin-t_BGeD|9i=tVKa?a`oRI&5V z*LS1AhAbQ|$)m25-*I6XQ*`VVjs-)%Bn&`(3yMaIq7%}VDF_<_0P9a;6 zOyvf;DaSbDv%XKCUU3{RZpz8JjL>RIjY?!J#wX+T2oWpHH9*5*DYPKRK2 z5OTC%Y`ojg~bB*?V2kK(Ns zY*6W&>3{BP41&jkHM)i~(!1Rfwl}BtkfEqhrvQ@)WN$>h4KY?Lu{en6aro@y9rEMn zdZ(m$vLl1sm=;f`VnlxuZa@_s)$HoOL*`Qh>bIbpc!m%XhmbuMxw2oKzuc(tJv3i- zmSN>U^hBw$Kc6;VNsz)TaxyeFiD{dq2c;{Qw+c-f`IyE_jujmfs!6f9l z`}0wQDI#2F@ilc~GoK+CpUYAXKc=;HlEdW<`_>Bpo z3*Amm&sWzR>cYVvE6m4gX^5)4qt&t$Ug`(vhf8m(d>J5pFJN%5(|?6oBDxZ{1UohF zIHUpBur%{s!hZ{>(V43ZR&yM*PY?Waf0R7{pa(BaudEc>CY#t`4YcBrm*qa=6H!3jx+8 zrNzV{%CKobt@4A@4Cx?v*5x#^6=p+_@#0SeITH+mH3}}x=Nh$X3dSF1_!V53$>c9e zkD8j_hp`wW^I6`d_w;~yLug(|;@`yadD8s7Jr7(M#U!iIab%*LIU&Txnp&AOUXw@h zH)sXc*0Eai#{jmyF<^dx*2}`NX}MIT=*<2EAaOV0D^v+9_0WGDH6iA0Gl6O{suBSA zfR&Z@My8xUMZm9J=XzfJS;AYrgFXD$Y2YvB5&nsB7>Kh^vfRuFGsax8G733*UGAAP zGp3P|V_EqNMUEzy4JNm%YnM5k^3TpqwZJxr?~Yj5kXUiddZ%ovgVW(n;Eh@$r71Ge zkzlhUT1y6y-PQx?SfR!`)|0?`S++gCv(A# z)!+!@_>8(UrCoiP5?j$FpIeTeCNuLgSRZIVZwE`p<+a-zA5j{qB*=eStssD&QMX>l zGa`>j+`;-VH43MU)WJRR9`5S{Z24Y;gV|(LKe0pH!ak&E!k)ud>GKPa6)3NF9HufE zblYZ^Eh2#3dXf%Zv3y$^DTOb?U;E7ggt3@SWyjp#G&q&>4evbZ1 zAsJSU$uygif?@vFd_q9`Zl0O}YA66l3~DT(kUvOg?=!j~N&LVVt20~FJFD@JsXK)z zuIN10ABgDh3e?yCPJ#Lb(LS<_(Z`4!+NJI>=5AKUl9T6lg7-V}p6W!yq|8nhJ2=cG<68oX_m>>^yW)sk8tUSW*-4HZ zkcrMV>x@o1Tz2s}#q4%$e;-VKD=g^wd(O2WWg_DWy~UG4OE-DdwRv-Kk+a@$e)DK|bdG4Dp1%x3rvVUp?}a1&LA=^o z4~1V&2PUk=4yT69W|_8dU)hd(-vOV(>wcKO(fFl9XMiFt>Ly(*-XsUADk`?eg$_Nj zc`RX~xW3$CwP&uCO?G0b!bwWp0#P3F_aeaA{$5O@%oRo4T7^1#j$F zX*x-utH&juwX!sMo|*jxb(c{}jt^UGIg@_U+aU0%diAjWtjokBfy}`=dWxP&7vvaO!p_eJ*?3;Bh*FI&%PP{KRSVnT>55 zgoU1vjp=nsR4jO9A9RHFbf4TO;&FC9U9D5Ru_D}_vCJoq@baB z`X9Lb^S{>OhiTKtpJgy><=${-%3?af;>OG|&cfQiEi^iuBNm*%+!Qj% zW$cBbpc_6pwQ3I3%OtK1YUCGF-6A1CDC}$Gm#6F zdl4)>CRUw{tPM(X(XeY+iR?uDFJnDEP@)mC0xU&lKR%^a83_K`G(b}209%lhmQGl$ zxHx(I=69ecOi`j|3GjCDdjB3yFUI1AXBjLF2IIOi3nB+bllEG9BZN1%(?@3|{yFKr zpkHVfPPJ4vJlQEyntDDUH*K5bdS_ubI^L))3pc!(h3k^v-M%~&u{D;k({r|}$S*np>ejX6*SNDAkex(+lncJaAPl zdi5cXC5hfrrcyT&`1BblwGK93{Q^O3dMLaaLc>G|ORYv|r|63t(qvkF0vfJ`H_{g# zqNAZCjEP+w4Vo~Is}25Cy-p}r@Y|<|R{gGDwk!g-$9wxOJ#y)DZSj~xNra5tzHcir z`1n0M}b#pqBP4o(K)Af?Y&p!if{Y@DbiU+pm7au$H? z8Y=(YHqPt+vAWYoXrwERI=<)apZ#J1r%aO;9{YAXeoF2vmFClZE5)RnGLh zU*-DB(5O|obbDei)g*VCxS-%ovvb;*m=!7&y2@veC+5|O+!FcEesQ8bTWr`5ik_Su z3Zt9%mY>I{3HED;`RzY1%a@Y<|2Glxh~B#S5Y%N^Kk6u%zd}#I3S|WFYq81l1h+t$ z`lM8m_sICtn+oOB2dfy}U{d3&H69c%rNZ}%qw$+=u>`Pj-yJvFmB6nmJaC^v!#(5R*-kj#dbd~F>J0j<2xS-J?{gG zr?rgq(S$@co{Zz|!Te@RhUlnQ>xr~(t7S9}y;eoi{S!&MKRCc9R0BBVCogxfCOTwd z(`hIpY1|X@jDyiZhq183>C2-7%5YY<{@tj>ip4IcpS+~tPk(qkSc{f^t)z*4=fV0` zD|)@tk&zmyKq=?`baj)%FM;Sg!@!}~MC4Y97t(c^!P4Z1c zb(7$DOP8!@NdEweyi5Xl1KE27GhIA_amUm|6%fK$CmkORK{$N7@6{;=*T!_W-nVck zbv&mBko`fj+zFZ?TjxwI)>`);uZlW-GzCh9Uw+1=da^3QNA|K9^xQOCr8YI}48abx zkvs+)7+Jtn070wBFmv4K5ozZ;Jv|fjx?RR_bK&3vb-O>4rTZ=wvS9OM&WcDPnBBZu ztfj9lSrqRhcz91vj6Blg(Sn~V{}bN)zh>A35yeYa zz7`CN-wcE5D1w3odowCZHWezW&0I*EJ2d%^M!TMxpk^$oHr&J-lYxSaH0ls`qB|jS zTxhNz((B#p^I@Z8?4O7jaiW7yKBbH>@#(dSqy@!!5i4PFxnGvgFMBmw?3fUC-J8=7 z8;SVAs64XBz~0s`)^S=R`fdoB{RiT|yuJUN&itv`r5|3s<1%7~(6yb5#f$)@yrC=r z%LKDUcC7`0clh+7;qf0Fx5%k1!0FYa(qg1hF=YUG=SOQdHrxGq3)eG~y7(kk# z#Dm=i8?gYRN|HE->z^Bm7-R`6n_H75`CCZHB5&jjq5eQ+!9&9F_;?<=x++*Ng9}Vr z&Zy~tl9K;q@rzQkW$8c&b~KKW%XI;}0b4w4%4N0Y_4`8kqE1WE)!x_J15`L z+%}wmpy}wD;LN~Q0hqRMGBUasX8Jr5vg6^qh$+T%m&A8sY~KLMuSrGZxVg~vHIMtL zr^-=!pTV*zq~Y#NNCuCU-VI|xDz(IdH7AD&lTru=Vn)~WW5E>rC=X}a ziyTLisnNmu$Alz=z+`lcR;mV68`H|~Mj6cLX@b9C4?sxqs{6-VZYD>eGX*4Eb)e3& z^&gxY8Ew>iUgO)o7E1+9`2q$n70q|9hs$SUQYNFj#VQ7TFk z9OgRtV~eaYf~epStpR5B3u6u|eL&!rQBKpSR%EHBHHaq4?ggf(rSYsla_Cm6#b)^7 z4ZPW77)Du31-YMbi@*>mo9dd2gY!GP~{o+5cCMe>)0n_@2{jmtnFQ`UZC=TC52NnKd z8}+y+Ml^Kt#^di(OD%=%&p{^R7n@zub(kiNP?4h2{k>frmU4isnDY9%AW_YCwU?Oo zuk>^d2_5)iTI#+nE`;M67OmJZoe7BfDw-KXD{HK;z_1P>^1vn!faDK3tx*nf*>ola zd~9K0;KB1!AxVYIZ&K&(KWSVn98VR?IwBu)+J}w%W~cL>hxyY}9?GP3&UZBb{m*>x zAL<*u+Txp|24vmW;&C?fw4Nfs*<{qd>0m{Cb^JEOr;;7?*_Q^2VK?0< zeI+r#NGRh4zle%OW`McGx*_>NE|;;p;9Jv_{%up9^tFr%#9sKz&xTOVVyT9Qg-!1!Gv1U@lkYzvk% zFq*u44uOh9NarrkN zWLW?WRo1aQe)2e;KP4I!MLl}v)$efFot!;ZL(&->=fhZ%o(2Q3QUdB13I_EN!JzB! zqSBAdIC0ln!IDPVpj}*eLchBwmTa6>xKfBraMkuD$x8L^%j4f2)>;qDwvQM69G1+G zlv;*!054|e_gcNMSUDMqElG4n-0tI*I+nb4e-R^%+op!~M+l5I!V34W9*qQiHq^i; z`R5dpb5eT$lX*HZ7%2a7Dogr327NpgK0fpSZ4p`r+G?1R&gBFHh$9|8Uuz#qL&&2X z8A0Qr5Y`L7F8{Lk^yc9~gLf8rM}BPzHac~>m4~*aoR&8TIA_FK&S=Cg68#A(@GuRKi#Rh$RMcS7_JI^K}aqadYnf^4TOr)G~q?f z8*n1zYBeeS+K8LgzY@RiE1=(4k`Oii)2@4%GkoJ^by=fk;3SGkpJ1a>V?BezMOvo! zt$~p_9z8`&7w6ZfKg#-|BdX&(4n%aa9y7>Q$``Z%98{`NE6{D393Sjv+%M4VXab1k zoEKV5WV^Z`XoQa{^g2)|DOo!An=D>Ur?EgbsCY5Anh|%#3PxXh9A29KARxBLq??a6 zBC5C))2h?#ZTolQtohBC6u7z?ELIHL?~%3z$~mQ4c#xtf&HJ-lPxFbe+1b%*4VY{XjiJYH zwfPtKSl~4cu1;7Nk&gP)|2kbW@RMIz2xxaIUc(VUL^cIg*ehyf2r-aO{~mNE`gnI! zN(z$E#3;fTZoK*w^c%|Fh#xENvmjjY67o3NrPP%ncYT`fF>()(WF|a|7O}7T-?8ui zKHK{)cCdG7h~YCaQyXB>Er_qAB(&TNa(o|L`EKZ)!mtHZ8jIA&8PIx(x634OY+{AS4ZGlYKiKwX_ZDGA$1!eWuMDC^!APsW6^o-jGzn zw64y2z9a#$cd>^bR}!$D4VWB>(Ghf}wlcaHA(;FEuBxe4kU+kxmq93~rA^SFekZVvXPu6$hg(P9=tg zp#Bm>0U8J8G3xdTaod1pc|eH-mCqQDsj=d7cbrY&B8(fGE_K@KHM$WjAwLmgiV2{Q+sG#2$_C*~zal+@i zNaI$T(e?DVglOOfFAuI|Qq{S8y*u-G9e_riP@5Sa?~y{C(lX|82!_NRNxF}(oZmvB zI9QSQEop}Fcg-?mRP3LasDR*n z$*&N+4l$}GuQ!jfw?QAK#X!UARELGdKX--TsFJ+jxq>8}C^%iQZ?EF^@U*=hyqknh zYJwI&Fo<+>VH+Zcg&w{nYBTldi085=3Wr=1B=z|-<8RE_S!=oP)F$F8+$P&S7^4H1 zbcrE#cg79s;_^X$70N{J-kX_xJQw{z_k*Eu@N8R%X2U+Rk*F5^hMdd@w9B~cM`Hny z1$FytpCxyAMta9--|%qpH@m&Hq(dCERXnvpBDWa4L|LZpQqn%NKT`i-W^u1e9aotP zC3@&_#Ajvw0b=i`1^;?WoJ{{|cY3`$Zu~x)uzc^~blnb|vbc$*%6N!ah}W{29!64qeD z74cs}j3yrLr`pHOun#nj7&z@SDAy^~V4+5`2Qx5y4D8m159xS}BF^uRw(**Q1|209 zziJwiV4fm9+SB5u`#o-lgpN2MgjjX4RBo5-dZ7*A5-C?st_DIx4!UVn)E_UWAH+tH zd}wvUkB-`%_CEsrR>?lejUGe5M;OK{c4xTEfD&}tmNPUtj(`~ z$MU`&24dDZ-oO8&c-WTXTptDJg@Sg5#TD79;q%5k356`&RaLDs(p6Uqk~=YG=!8;z z)2z^dejfc}dy__EJL6ryqHqR&TuY_G)3ChZ8xX*qfA|!2oEml-yO(AwT zGw)-Y#6g^_%-<#!z}Sj(+ijQDxUE=_&;!JknWX3kbBZmrOKt;Q70pa&kh6M1D%IBo z&0+zOttX4_U|gZy#+~_w_u<|s3B@8$mv-w2WK1{S8tcfpULsW^b(*}7h1`w|t*+Zl z-gsn_b&g1=M5c}o9dV>4b3-~nRLW$V+pzn?0V{B~MiwPB<%z)Z|jau|o9pJOl*w82AQyYU458evagUvPUnJ|O{n=T=g{HQMguULTjG_-WnOie({GkO3vcPOjgQ8?YnQSRlnq_V$TapvOp5 zJwF;-2d$K8FXg3<`CG<_q8uMvFPQ}1P{>DMDyt=)>WB>jDb^i!iR`NKcYEdsvrv2K zbMsgc2zgw(JnTJAPL2?k#zoFXQ=7lrFjr}bNsLdgL2WmE6c>S2Yg7tJUxPcbTUk=$ z=3|*KTE!IEWu>I6uPM#9214L_-)_c%9QC)iH?UdpC@g4b=-(?Ve71dl-`j{k`4x)$Q%+^|qEQQ+T0G9%B? z^h|7xA@~KX4^{M=J916NLN?aHZNe&8~!2#51;r?^Z4D%eK4H{^B}^- z20tq^^V%!;a`}=r7KVL5|ub0V*2|O-aK9>{KDVSw!uS73Ea+vt{rM9n8#WhkH z#~S1Dyq`TRG29HP-RP;yqaFVNvX&MS zeo5iG-*rawVZ>yi{=Z(%J4_K((`5PjXN;8;Wc`M9&%jA&uwsUByjVe&rdYoso3$u8 zj%7TY&&TwX>>)c4HrLr~wCj2W2guPkJ3SZ4zY_gKqkzKq^;#&)vswFf#IW857z)>6 z(FPb)Qv9zLz}t157v9QBIt1R`U8tm(N4tc|!8hpUxGqI>^0$A3idAn7-c~M@hjTDx zup<6NR;$6;%{z{wk=~;OkR)vF?S4t4!Iw=gz*Xv%I{%4#a;R z15$7#BqY#qcXv1N1*^>G7o>gPiB8sORjp{~Av za~Q_X_NmXd-ud_hS>Wk@g@vrHj8GmKhV$kUoyH6cOoAN){clD4&6HJ}w|XE4WePv-!m7WoRkpp1kxk4%K2zQcx!x?E5|+u7#dCMIwT zrzGHViba6kG~z6WgI$$sPG9Fb-0l=&ZA~=v^A(Fhh5+k_mbi@#`09pQr33|e6Tqws zkEggt36^XAXike^X5<@!k0pjVIA(uk>fzw-N4YCj-2X8g#S=S|@9wZ-`Jr6QZ31q^ zW)K@JcPq<`t<{S}ArnvLZMx1&Fp_jea&j^t_}4IMDpT<9A|%W&*^>VHp_PvG01;wx zrfl7Mh~C=tWWYRbnMtvQ{=h2MumG_vSGWD9+VwmA>UkHVc!JGb9+aLw~nviP+f$YZ|(`Gm~7ejWI%o9$_a3 z(OieMBOZ#<%jV0Mv#a+0V)Wx+26m_2-zeMk;|cOc#wK3=vt-W~2eQqULoVyBx8m{F zM-|(bY&djxi5~mi!oufREb`1da@{Ez9rn%Tvmvp|t+2o9f~zOE7XE@1sAq4BPhZG_ zaz&&dDslK=tEbHkV_zNc|0cV}HajM;DwxRdro3@%Y19!`#@?hXNkHNuFMZ&4LE!lx zh}h5^pXrsM1Y;`5l`SNwl!kjyZgSoA2MtNaoiWhoRqwHaPv7YoP6^i6%cZ5MSk%2mlv#*u@xLr}7z)dH$IrqePz{f4@VOx| z-2f!)ld|pAqQ+X!YmIRj(lQcZp#EpW^RtP>P!QCW4-T|T(!_o*_&Ds+n`=NCe8dXWR>frUM4>pk=vtfm&n#8fcNR*lXi`Q4#B49c^GDo zMu@4vs__)O_!vv18|O)7V_R~tI+rvT8ZTs%|_dBN) z8aR#!v_Z9Gcu~IGwwlh)PHvCVFh~g1YVd;`tO0Hps&uNq=0_toO#w&4GJV1kiuKO`D%vz^JV&JHA#f2L-XI)z=~CZnmK=K!rXF6c4G1II}m`v_X@jvq5WPSE^Yp zFO<)a%!R|v76dS$UfR9qivmKAu=5V@N_8t3#DWBh{SI4b5LTsDA{9lh`&yfW8lYEx zaCxfvF*~@{YLk`izzL6Vum{&OQ!mx4YUxqPIhc#)4Q&NIGuPARMCC`q_YQS&y$tRg zavlt_lWlV}AbRK4`J&-i-Sjr(*dq42|9i!+6CE+=ug<$wrI{M!7M&)%rij{KZV$AE z@%@0hI#(j1J?!%yPfUaZZkU|{2e{9Wb|Vr#5w%n*Dg!xRJUwF8n=2_x?{V?wZ0`?q ziGjpG3R)UB@mTbqV7-^eqfPF9ki~R4{jyN&!8s#D>%&LpUBG8X7_vK#VXv~`f zLbVYg&_AjBqv@Kg+>B1uNw9F1JitXo`V=7YeFR>uFYk`UD>Td&&bJKLL_(w>K*``P zN2ubRb?%>~q3+M~NOF*KVmR$7-H*6z<&WF;qY+WC_iy2M(7uOZ=k^8~ zwzAiGUxfx2?-d8GMSmEg?6m4`bYS{eS;EZ~t!C~50DOZ?St`bbrRRJAyWhEh6; zeCN>Wau*p4b@vFfUJET=9Ulc+=yxbV% z<3J{xMIvNn=LD&&dwVY-60Vw#MEAZt?57zG1JDPJjt5ASAXhX1s81lA6S=I%ZlOa&mL6N`lB4-4Il zw+~{s&>;pHq*XMBI0{3~=&~l&2?jM6AsBt7WcRf0_Uz`e?h}%KQZxKcesF}$$6rdw zcXYnv$jwL4YRlr~WjzJNcf0_1O#_pt6fZ=kv>KUm2Cr7UVby$lT`#r(bQ^4V1HBZ2 zX5i3wYVfQl=us0gV|Y=D4TD@Fev;0gDfBF@4Yb;YAfvg?Qn_vJux3b@9)4peQtf9I zNpQ$00F`MqLuRh)l0+_5E@rkEsB?h)mK}=HI0o-Gv}`8a6d9J2kV~czDSMS{K=`NR zqbJ2ab9D1k@hD%ulQZNTQ8rvQFGF&%J1^s#XCMC4Lc0c#t#h03_)L&CMfaUY! z(|PMU46-_-@}9kOlaX>lP)v-6x3{vKoIG(ZtI4=+w|l+oYyrmN3y}R{|5$jl?&3O+aF-91N(4m{?`PTf>a$ZI5trAQ&%EE$)DNv*zkrP`pHw|PH= zVVmap>Z7P=y?CMofy*uBeceS!DCge~ud=HoS~U<9)QskzauwmPUt?_m{_R5MEc?p@ zq-xh(q^D6PjEP4+xb0{gwAHhT1qoKAKyTN4S6y~j6TAZw{IjfNEKxgFh9wv?OFj~P zDY5Ix!g2BX?d?ihX#ccmUZ<9l5E+lE(BpQTs%GDaPtb(Z@j;%XL9nK z|B*HcNU>Q1iHRv9L{%AQ{frnLCI7T5Lb;9ONIy`UgY!yj5dR{Li)fh8L_t65eVLdb zc7@d20||O#GlIx!d+-M*@PYlts8ot%G(9A~YkT)`r;_zkGhdoS|4)6P&GU_0R`#-7 z#-3`!cVjsOjOT~ps3}H9y`HxQxpdarhR~ex4T?WI#%AFCCJqzdqz~-!`)wp-?AALt znhU>u`<4tbH8n+WtGKlvJE~~=^m`3J-a3(*arLu6?rub1Tq=kIX1YC#%knhrut$Jg zO5m5&i=@I1CO;DibP-{ScVW`>?$MxDhUjT$VO4QmEMCM*$!)O*<1C4fBHj2qKn|M3 zy*_Wh@OPOjIa*skx7frnYOB_(lom!|d$HT#`Mxyi6-v;(zX8d8mzzb>+iYexcz78@ z!~C2a@=bMZ-;EnKC(o-Qq?>tNR$FW`ZR?9PbR4u+_!qFIJL5%GomAI{_}0el|E>1BgWS$<{aDB3!`rZQov z@Q_k76*sqm3P8_G^B!9078l-$_NCd0Rpg_GsW zyA!~iZlDydg?71MxY*wx^YzgyGxJeFhBYE$bPqSq-Q(TF;@S7{LKBG%)vMUfNMuGU zK?PNVFTBhF`1`T`RuQ6M86~6c~lF zA|e9-wgMP&ZFn4JJBJtqDF8(!NN@-L8W!y8Q1DHty$q(j8MEDEa&c*eJAc%J>`xt~f(i>i z|D@3S^odr#?J8tzE2*umH4Z5{Ut6ly%Z8;*uH%L(Bi!Y;Rh9uoYH;Q(iAj2y5DRka%=uX|T_?L`ll`gs znB3#b79^y~d@c~JRO$Qvq1pRUUw?hY)MGwZLs&|+tvzSZZNELR(3REe`O*E$3%-Q@ z=^)cC6ag0^U@6s`-xC;R`9ET7pH;m19rpkM5u@NFcvd@>3?hNM^O+_cUFF2E{&Hv1 zeV(RYfa88(i}m)Kp^V;YXNNw&r!&x(uuV>!IXN>9dw%Ks^>E&KcXX{xgNTEY@?Ag} zww-H35USZ{aJL95{3EKo-278`V77Aso{m&JP;ZA{Czr`#u$>$dg6_k1*z`+ZK)^ev z-N*CobgoN|hASVEVM1uRzQuRTF)bI2QSDA6(am4K(8|qr_s;4MFd21C?vDPBaUovlPw=cBO!zB4qpJRLX+k@U4I4=d#$V5tGa(pd$NUmBzo=sb}C-Jpv>ZKo!r(C<&tU2s9O+s!=cb<^B8j2MicM zGE!7j6oFUozWeUr!GrmS1oY0{Kbu1j`_}4fV_|bN?C8#mJA2mXnb+(kDu10@y45f^ zw=}8E^ISAQDAw11{<%Ps*QQMk2&YUv+vkb(YSl6rJ66}zxUs>lTLvdiJaIQP)Szfl zgPAi8TwHdSELr{N88xgU?dbhe2)*R zU3*kwqGQ?_47Zb$=iI)1A}%f>DXEr~RYMyaKFtjA=>7j^ba;4ZTF{n>p`i-a?({@j*=vuK&`8Fw! zQv6Q%QQxG7o38Zi>-3{x@ay!~d<~yv;lhP~RuU|Jfbt=P0%^ZU}c5E?k z-dZ^E>-EE%O6}UUZ{4~@$_IWp(m8hQ*pZZ!w0QC2CQX`%XRTPVBG|lj>sI;b1Kr)H zq#a|o>A{08SFWTO7`$y`Q^3SzXLR(*xVZY()*Id3&C_7G;`#Ge6B6FCv}A^dj~<05 zCSFfU8sOwKwQ%7f9v(lY7O&Rbdiao7RzlFbckezKyM^LMwFmG?)NXvKEXe{*)4|kC z(NqvqVdmwDm3~u&0$$Z7|E@S}8e_720BdXOAAb0uSFc{O=Q%a@U)jGUa7(x9-KghD z`(*gZ&Q&_UPJ~!HhU_{0%h9II9SvN%bm?T5F1a%ov)I`M6YKGM{oK2EuW{qX_V)Jv z{{BUw`>8*4Qitf%Pd~kU`7-$-`cF)i=>}=N*Af%khJ^T9SuHA7Ow!`rF)@uUTcZ{(ko z(=_v)yd-zQS``4ZvFPaNsZ*!&QdPZr_2;@zh_81M?_UFcg$j4|?poQk^3r2V%e$1P z@m$w^VUueZ{K8bNEtC@t5TDskPs^frK)&xoEDmWG@l-ujn7{P1*MUzgd-!d-bZpdwM~VE)$LXTydKF?&3} zf>s4>3XW_v5_9y}tz$7wV?X)h$RAMTSN-%u4TCfK*QNEp#2PR)Rc|iMXQ}SjyEc5i z@`E8L1Yv#h$tQXb`jieUe0xSZ8*@Z*@`IF=;_2C|EsTuHT3CR=a;~GXu?;R<;4<;? zV=UN={ijqZf#uk{Z3-6L=I>7`XoUg=E^OOYuWZ?G=FVOA{r5%f?Ez5^IZlp?ONfq+ zx_;f#kyD#3g_&1QPD6z;z^(EwZBB*C1Y>j-Dn0+M6i|&7@;Vy* zJc~uhR}Izb^75%KADr~eCZ+sS8^gW5-Q3*9j2T17)fEGSIs*qDGcdqVvHigVUVkS| zo=g~>>A`~r8N9j18pLuWh_Q(=p#~#IjwILUsVhFCfG|xHHJ#5gAExO&A$y3`ddK6P zSJy+VVem8llhB6eng%!!iQ|F!(@8z-(RZM_@{1>G)UXTV1y!{=b?W4mE4SU;q}Wp$e4DIZ4IUUM3qOf+JOtOo#8|&x{$M*hJNJ?$ z1NQFy_dK7_(5}s!C&a{(o4z1f$GS78bU~#v$pE zLhgnnCN@ZgYN2+|Jb8Xs{4Ijw=6z8`jpPV z?5x^U{rdHX4;?OrA0|FL7Iy4t_|g4=`)wR-2w-m9w6SVPE?$zSglT$n;O5^B{no|1 zOJGDGtGBPv9)!-tRbIeUuh@29fJU?>a!Ui*!rF`$CADVmrcJlAo&o^|CnXtd-fW<( zX18h;Meqy==nV*HRJU$MYyG$V{}10yuk4Z77$i@4NmcvwbaUH&@ZjC+*Yg!BgiNiH zC7YxbkMwhOrA#!Z2s6ZT@nXCD`DwEd9@(fzkHkFC+3I=oZi|lQT4$S9{G5-ECmE?m z!GemuCz_=#EN}*IPK}3(O-R@f5Rl)_u3huy7It=dY;D2d@X*j%zy7*v)25E^z8iM* zXxzPfQOU`?2gq5JhErjQ6d5X?-G4ZOCvZ=VM`HYwAjUdeh3 z4lQu9a$;%niJN;gf3*m)hQab5p4P~r266>!(_K3-GIC8=SlodF21*RRchBJW-_`P% zQag3hb+&A2Kp1d`4hF-98Tj}Z#Kr0Gc=l`q&Rxlu&)}bbhKN!g>hZaAEn;e9WR1>= zh($fsNh!dFwust=6be2nFWG?r9`T9MHH&7|0nW}F8Z{a?aUwCU*cIiIkTi?|I4M4! zGaDj;g5bdq5)=3T@=LyA#h5%_>C%y*p~96*a#5>Gmwt|j_*`MgO?dpcUubB4Q`5#7 z7t+Ej92g&8#LO(zz`(j-!2`$;LDz=(SaLyNPz;h zaWxakMO^g$m>9p}#cw)02bh~L+p%N!nl+Vc*TyhT@D!+zza zfWxJgf-;ye;pYk!YCm|O`^wHVdX`CaP@0w!BXCsLnR;l<6HEN37d(ilGX-Tlbmq*; zZQD*>ycoeNh=BnIz%^^uj8j*g(x@%QLvn>x`~At)^@~(YrGN3=yMA}?QXwnm(W8Q< zrgg2Yf!m6yRk6e`Bj2LL-(7sF_N-k#y9Sha>}uyqz%zgKXP-79If0xdBSWM3U^ap z`>(K8Ur9Qfb6_AC+^~4@>aMQ(0%rpQ>+RWdyL)&2TUqT13oAQk&hC#t<}39T{Z<7+ zFw!cMbD(zuv3Me>htSYa!ld(Icz@c@$X2ZeT)r%`goQXvmkN|_>7^=VG@pLGXe0qM zY~U2eqRqPtIWt(@X=9+qFfLvlxMb(Rt!t%9A`OXWOt*UF^8jX6PxnU&)w0dwq$^CP zhS?cuChPsz?q4JEH|%~GLvjU=3DH_{Vnx*=Rhbz=te?5Gbm!6u8zwyZ^pQmwi_G_# z9GU!J-h*n%)fNt3Xz?5(IyDT=T>sDIE{E~`@y8!T8&X4`xJ1va6_?2IR$d;ga5RX|nX0_dF)0TQ=1~T4U zVpyW(HrM1v(G*|eE2XChP0W7@^m-B+JHIksNC5?LUK z*_uALgi)sMU_R{UX+HoAaFxZ#th7Pd`0u4%yT*3!J|<5dH~pF?S)PXTgDZUfb>g2*WgOY-Ege`oqc}LWsXy_~k~&jTY}bh5jnHA=+4{ zO~;w88n7yNVOYapeN{ilo*q4VtXsE^fK}xh*f%(Gt`aBUX6=WNC4sd>En&ZvgQz@I zGG9J^vmP6z{8C^Z9f-!q3tuUCNrV6Y1(b3ZVF9SRZ-g8jxb{?=vwW6}8Y34joSK{* z)2&-Z0_y&l7|YS4->qJ~d8tyhix&N2%a&1f>b&LbtQLFW*ZPRd*XxS}kH5e8f_lOH>cbXxKC3T0KC zbS1OisbO%|b(K}gfPjFrXU|f+jf(loF_@q^>Wfchr)XEMT$xg8{GF-mA@VYUj@7TetqJZ(s8wMK*2OG9@L&Dz&bbQZn=QdRN!L%9RI? z9GSV;Q}6QcHuTUTVF19x4IWeTGNVywQfIinc8!1V!mI-2O_xv088QN9P?_J?hFNA8 zu*LuE=nxHvw$#}3Xmse%f%7yuUQ{Mu+L9EGsn!-zm>Bd4TE#0w{%9WUAy+$wQC$5!y#_G=y47S z`RpMfA>^}DN`?mi1j!+ZN-wy>%-p=o@Zoo(qe0MHSFbwS+v6OqS*cRKl#~K_^O`+) zKvpHUvB+gkrNRAu7tXVT_`}UC1rDHk2LG?C$ z@uKCLH4cG+b*ojgbau{bW@eu!54M5xw{Lgw@*>}9$hK{Dixz$Vxw+xN2|xnsCNo^q z4W7#Jvp>tgEn~*qIDNYPufO7v_50+Lvdx?4b937|Yu2&w@NXYKc1cXk(&Wa*^W5A_ z8aM8e{W{f|&kyaR&@`xw*i*q38L4UzNEm4{GI6WcMIO4$FIX;b36jmaFT?*na#zoz z!Q-R4CtzEl2UZly6ra)reB@wVUsuDGpOpJ45i=rUkH;oQBmt5;iR8p^0zl0=*=4NGj-5)fgBu4Y-AeK};h~%}F*NaX(bGj{7Rlc`Km7@p5`voq zCxs>D^~q~q+&p?`G(8TT9g5B`YG`8UInZ*rpsWnl^J%PDvE=O8Q~YnP9+`UD>y#-|sHgyAK7IOh*|KFBTdGv4LWK%F z*Xrl`J9(C#+_gDBbte<{?j2UE)+cM%Vq4xDN~I=)F?H+KUAS;(moB4r>_F!bac665 z=&!YznS-Te%A-fdMn*{~DZQ#z#dkhn)24#f)`M%*5cd41E++-VH#HnW_GE>zN#Xyb z5wvydzL_%{4j*2mTD8mj_U-ub$I6{L8Jn4%S-ZAE`}TnwH`X6EZ11dDLD#OGj*4=8 z@Svrs>01d2<|!#AadD4LOgP&v($&@9%Id7KakYB&C@Gvt6m+gXAm9F^IpI9U%mWx_ zZh2O%uKHu~dX#lI7gOm2Guz-V4mQ6Dj9H~m{mYdrSK{Ku<_Ejrqz6YwNB82z=g*z{ z^`Jo)&z!L_Gn=t+;o9xnBlJkHn5s~*LW{O8nm2Ktot0g2x8g0Dw&2hR{Z+EtqmRe; z6aL5EdBEp%{eL_WiJ3@(#EvZpLSx6?s%WjYwES9%zfn~!s){P5t=+0sqr+&aQEC&h zVz1aFu@WOB2tthi`^lH%_C7;~#JXu-PtQH~oO93pK5;+y^PSN>4)kzq>BdqC!QeBW zoQa$q>2uP@G3P_~>GLDcpX+z7$jTxv^<8p}%{9LHcuGzsc4Ow_lVzDRU+z`zp+JpF zU(TN+>S&}vBP#0np+oa=b?Vf~fByVzbLRYME+vv2wQt{E%~I!A=ei5Z^b76+0RkdGW@L8eU$9BO}?TN2W|zqo?iO z-NnyuRD%XztXRc&vOI%J1^dat=;U|SSwBi z1#SBA$Esbs-inTn3Jc5a>w9L$jzYC+g>2lI(bcttqhpVL{bqIPvL#=>aVuBmC{g0V zp+ns4mD8syRH{UQ*bd&_seiLzUg26~Av6h`FCuu+B3!QM%wM@Ocx-z=Ca~1_NXxg$ zn+F_@i6&6DR{6f=}Dq8`wHGeVfnlDp(|Ni}?fvw=+CElcOV~R(EMo`8e)r z_wMb#aG`0jV*eaFhBp?&F2uxmWX`<(?Aa;1cJ;1Y`MY}czTdq0la(upS&~I~yjpag z;-9nb5--Hzwe#oy?9>US>-XM!Z1~8^l|-gfBNk@@yph3=LN#if2nZTv`392EV_~3DUMTS(69Wbe*s^5{`f8FHp*Q4I6)XvDG!fj$Bubs&Kz{+48g(Zq8k)0eBk26NjrCT zDO2X1^yz0GK0Ivm=GTi90fxnS%KPc%!ASItJ-rEACr{r0_us82Pj-vfm;qNIn>N+z z)93ny3of~G?U_3l)%3;*6Ka3{`R+M$-16oHHh?$a7AA*TG@Y|Ej(%AS6}lE4t}K5~ zZ+FRE33!X=Ii`0bD5)tY7omE$ZzoDwtzCq51d`#|;5xU=Av%Brnv+?|9GGQq2vdNW zF|0`^?*Xg~Q=K6B%wljT|$^S7hIQcTP1A31fSri$0ghW7j@>Hr)r4J2j>tZk-H*TD@v(Iz?GtoI) z6bOoFLbEtUT9S zghfS74hm}1xN+|uJ$^rY_>WVkKCW2N=Q#)+ws^~l>O}wid)u~Ia^>o@Y?(OpZ5TZ| z>dcwSAANK=Bn0SM{K+TH#*Eo8W=zS~Uk3*6#Kf4^x3}j(2U>U(DRLz&jMouS*m4H6 z0Ox>>L~SG02J6Ui(A23wUBbz++9#q=6q!yIK`8=DM)j?QWG<(sJh8!G^YNk*=e>bT za8Z7Ffp~F&S?T{&LnjuD#NU4V?Wj?sf`fzQ6godLB9JVB!>6eKL(9Q}7V}O|37DeW zmF+`&NBjIU@_Y97j9wCbeeLzjGcRA7bjhWmOTpO%9o$TI#=8zCrF8Ot3=B@X3}a)X zf!^roFc8<F#ByEAJp2v@88ejWb?3i-RHk^_0XZ0jvf2o{Q1Q5uv-Z6!jC`B;ObhX zOBVnb7+(7M=f164#ofAf{rq|V9zDq9?BMA5;NCqf$Ix8Z*zN1rX&hU#Mjra6GQjul z0RzbP`M-Vb4%xFJ+Suog!1d?_N<+I5_F8T~`dnx7;huU>83`2`c<|vst_OV$I-E#E8=m+;6 z*gM#(!Eq6Bw9^$fVKs3|C*M4odw6b_1}5J`c5E!XcEO=Tef|9hu3HD_5*$oqFkV_^bLQmDeCFQ0 z=^Pw_FJ1)8fnJo@fHW#@;n@~9@O*Y&Idy8@haVPi*zn!oev=(Srxy0?NkH&x?b_}9 z?Kc1z>u+XH&*F_6(||0Zd0sPoI2K>*zWg#PBrp(|(Z-|Wr6rnJ;Q9Hy@h9$z+u>$~ z9CH^(mM`ayn8lrf$I0QB3g%@G009A1I7=gWdSJ<1BAIT|i2`OWm$cUl+cG?H;>6EC z|BU|%QJ18(s#vk2?eQsbR?%79vH)7`B~ayqvQ%3l?HRgg4z*`hJEwEbvyo@hW-~z^ z82lja!L=3Fvb=73Rh6HpzoYEz?a~!7O~(lf|L2PnE;`q7CK~wB5WBc@ap{Z3+ZoT= z70q2#^(UucVDJ;9hm{xT#o`O-`ucW&`#xd9i+uWd(>W_y@=#Om%Im|zaJiyrd_?;6 z_BCo$iBAi#C?J5rJb;SF#6-kfH%aY@-xf|-Wcqw9S1w9fU;`#p2{8U)%a(SfOH-&A zNG44OP)%3`E7PV8Cf@2Fe@uw*)pO^Xj2MBh7V-ps$m9h=gB($(PJz(Ox^Vb#)`A6Tpqhps zr}AB-!sXz>LXNo>u2Mpdf#HONIX!WE!cEmM1gkDVC)uXJNN8?yP+u0e2FzSer>a-uZ_P0kn8#;le#MFc57s92f00bf2@Z zUbN7FaX?rY4qWhH2r-}If`a<4UOl8%EtRJG`TR=3+P}JV!Ls|oqD6|oBE7|*eu}@9 z&zo-oGydJXQ?i%pfsMZT1|{>oxVWnS_kRqAv3YuX6C8YI`*se|+1|W-x#Syf5V=J9 z4k^p=eBSj_*%jb~WLS#7H=rwE)F|4J*MilnqajHCB?OWsteMr&+-M;gBuT)?dzb7^ zd7RD-ICr}H=?8aATI=-1(=&?%9juV;Xg2STPbXI1z~IMU*8Tf;2`d79fnMC3UwY_$ zess=YhLq5eFGF%YTGSIlUd?@`xDf~|pO3%G_!hcl#!D2F3@G60dMY{^zKd5EX*$qO zgU3*0fRVe{P5wNK0eSvl5(J)<@46nt0kJ4o=i-1%#eYn zGYzK<83cpDCs`QGpq+dU_h_5X(f_$AZ4M9|p)r%E5JrkhoSR3O#5W5O=TI(}Kxj<3 z>?{Jayx>NyUOi&Qi~%2hD9+681qy)8^71ip;zTJM)22-uEdZ#%2{C|BFlJ=AVm)3*X|> zrQ;$a4`$AMHzp=3bLMohu^H2*b*o$VLto$eDIGft;!032qRf}i|D}gsZu8mdoOuL5 z2zWzRf4ltSw}`lEfNJ1BB}txf$O1J59ce>HIi;+q8f3~r6Kv!9* z)#X&;EzaT@#}_2uf{k^OaEdkgB`0nHU*FQLT9HG8iZY%jPhQ)y<=CM^d2{3l3<`oe zySuvX-m$~t`takvgZpOj$a387`2NlNvt-W_eJMIPz!Y~R39U?SQ0Yt3ww=w&;^I%S zb7RQH4JSA7HsF@Qt!3$!J*)I=FtdSkpZ`P&JLNUkru}CN*n`DLta?2cc)m6CEp?d8 z#ETep7n+G`xDOFY{Ky5c`S`lv|l+U@7h*Y1Sprli3_$+_;k@9>4@QNHl= z&r39J%;O%ba9SrPpXSY(LT#d=b?ZWcK^F$gB4n==WAK&GP>jL4r$2|hIlc54tmLW1 zNd9_}EuLI-K3xT}#mmvj8vqUva#0SsF7oHnx-UgdPeOw#GM=8EOkA>L3GJ{;mrjO; zma0&J0w~^9s*rN>tAPX4TK5S!a^Og_x0-p~_3Hk0chV*^{?m4!CSe;XF@;20OAjqQ zclF%82lp~MWo-9ayZr9?TV3%=#>8nxNBoqZqwX8Yw_7rWQNZSbpis`E?YME z!w>g)dtbMB!uk%U;mz>LS3s75G0R?h~v}tPfwvb?|HGH;e)%W%5cUrQ9M4K>X+QR|^TTxGK_il(XQ;^uV z@i6?`zI}~~6!FWIOD^#YKM(%%&#vEpZ#ZNK?=jkK>|e0p@bcvqKllLmqE$nOR_fRh zeoX8yRWI>Ef-&E?bjhJ(Wgi}Tmw?tWgQuTs-dUpITbS)zhc3*Kq!bFTPEfB_bifi65UPi+6`0YUdMa(aczBva zk7&q(DVpmMw<+kXTnH;Kzw$mo15&H*2%N^{iUiU0U=n7Qm#Zu4Xb3T-mgq;%3|9uH zJNx+oy+do)CPImk%)$(eHx{LZQCy?GW(CyUP-L)qZAb`;1SF|0l*$bNU|GL@7U2eNTcD`eJM{-IIPT9qy2Wc zIxZ9xpWrH3_Zg6c&}7Ld`QCf)scezbXxFZtWSu0R#^;}ZP6;^Oy^v$_{rBIO0=jkT z)KMua8TPDd%Ilq7Z z?=dkKD4g(e2lZ-&iG38{!RU*Y70`N$E=8~@sd-`ba3p#7@ zUSPVSpq9bqa^{5hOB)`{iwJSRn0xsIs=-kj)A#J*?c~eq)v*V&rg&3>%1DfC(tt4& zah4_sigg$OPUFCJ^2!yaKawWnp-v!RSrmDam z9?&(6i*$mWfG%k|%Cv0B6j&5k&Umnd#K;(oWgT9moIN}QkJZXX!-fPDIhGtCxWDm^ zf@K|zF<_a}lH}_As_luzhi@D_7NX1^5+0hYvPG(fIBI?T@y7%sty{MaY*yDI@gyEw z+6wkESPe+c2ENwoPpsb`zTex^yO?LO@T=iVrVE8Ub1Ac%s-~=Hz$-EJ;7B?KiT6oIvOd;nV zj~JfaaHEud#F+0KHFBLt928g;aFt=abAX*{O3|!YfBkP|Dm56f5UPZpIgPzNbqoPr zJg^8`!T{WL=~8sjv;k&lF~Vfo;P~7fvNA9XjV7tG_-(1nr9xxVgb8PM?P@k^l=iVJ z*IhAqaP+BDO-7CccPUDUgEZd}Mcd=x?P?odU;=qf`T@ zcx1B<-!H$8@ycfi7Ft%}zNv6)FTo}Fx1h)|_(u*9u#i_DzAW6WQ|I-RZ=YYbA@UTR zIqz6B=s|JKcbafW)_y6}TA-*5#nGt{j42SsHJP7F#Ak}t4om$u^%zzhSwW;z^@7y_ z-Q$;zo49Y8{0Z3!F#PMFUk42vbh_B-u(!kF(wZ`4S^II@>4p!IR7vprAAFM1Kn)8Q z=2v?v;cXBXdc<=XeX>)=j67L*YGWW&|F9FjR7#B0DxztVa0B{l@j`H7?I{6ZI-#)Q zBh4(K!YHCq(SzT-xS((zxoH!g(h^CG%^0X=0Cr_qvRnwL{tI35$7xjF^gElVBmzsQwLN;;4j=K?=(tL!J7~+au@=T zIbP+sv+2IUl)F2=Bkez1zBN3&cr$=5%Uc)k2MNy>(l`Dm<#wCcm5*FJ0{UX{ZBnAi zuCu!WLjpnH7NuJxY8~DuMh+cGc-61{f4$~;?N*ap5#A9u?H*fOT+X^=S(7Ex!Awmn zHpL72DLP^4_L-^}JZaL`?b};+cGqpo#v$=bK0fP%f`UXDLQpTzd+gXU8k7qJdNXEx zOo?o)^AeoG#)(1|YbYVJWJ0#6r?qyQJluH}Lvsl}f#BkU1G^lyX~ ztLg?p(;Ij0DDEq44}os83$>Zi-i~uEL^6kT8K@dbc=_NEi8*<`4Ex~PUrRW z8Gr>0{ItlYBDbxcbYdyTU3g07; zjN>F*;%|w+9KKfG6;fyePitA!&RfH_7RpwrZMn8U@1o#ENkCuiW}vqGP*id0(xs>V zI7Jl(U^tCknsoQl}(Y?(V=akgPRD zn_oR?Jv_Dl`Rua?4<59hGR6E0g3T=L*Dp(+Ja0{yz~hyNI~+>@c;oo-R9~pww=dU) zgb{0mrfuUu@NKQ<^kPVW*6&LaXaW^Zc16K^o32Vf~#9K0B# zZ{4DGyHx2y!i{-*=RtxK%YCiw6V-IbjvcL?P3ivB!{8Gq4py!D&{KxH3JcTU8d>+x zpSOGTYHm7vv|T`~m-{_(zUXG4|?J`4=wk%J80N@(BOM9a)fXswIZk6(g^GUQ2C zGd`0h0pLxNn(>d67aG1kkPm5NM}RjcZOvrP;;)5oR;kcgjlUxHh7yIp4c^cA#$*V88SQ;W81b@0|M^VsgwS3*9+JpMMUGq zjlp05Shl}r&6kRt81bCqNy@(&P?moxsJ!@BEd)pn zdvP0Dri()sx_2Pc#_fQw3{!SoA;AKikX>QG7<8o(8Xb-QGyPb3amcEgFCT8za)`9y z-&_kjvC7>c*JU;OZ{NEICnoAi1uomzRYFcdWZ_AThYb^6tDC|X9rVjDe60e&FkK$p z0>I>RIJ|6G`3@cE;m-i|GLO!3ZQIHU=t42~LV}42#tebxSv^lpXzl7M#$Xu?21^u^ z?&|z$v9hkyI82%ZkUzl2FVi8%VOzI~^CMpn%IatPuFTVeUv*!+_VI^DUYEOf?hxU| zYXsXy9g^ftQi$oh@4iC=?Be2ri*=Ppu4L+pG!C9W2=^t!28hdB0O-5@^!AqwedT^r z4}&8k4_k}u<#F1ysU4p7fBa$hh-sL%H+{MtWyRaIvl}?jZr(h*4jp9Arq9BKZ_b*v z>+$i6*=o|HiO%moG&B?pCdQ6vcQmf}1RcD1k>WpOmQXrp{z>3rfeRIlmlAPxUnWqe zRYY?rv{g4udng#JW-%VJ3*v&84C5?DbX596g==_u(T~ftIA=*1F)$CTqIf{%02Kz# zS&grQFG5|29^QulY!>IsN@fNR%yN_(%;4_+-kdoKAl0>KKuz|}o$Hb#2PvQ#08IgI z#Z(MxVmSsl#S;hZG?)t3s+TY{jn#Yg!iNhI4EIu&V3y_4?--wkiMG7M!OVEhYAr4zC^e`fr$85zvqmFZn@>Qwbk##$sp$JZTy`1Kt>Zntrx-R#+RUS4+9s@YAN^dAh4AN1IV?cto~>3;;a zckkYvx|{Ro&nNE;)i()d-?wicg{(FhQtf{An3lo{;JxwYhm6xE&ZTL4&S@iqj+)2W()xH#~=Uq#K~Hz{g_Vs z-+%ulMTLydo>uON_qqO{E?N$HY8LC8+xo!CbX-Cpk;) ziWU3M8AvdY9G~6Rgudhw*t5s3Y}vBEN)hvxFe_vM#gmM&fTnpv3vS$FT2Ej4G) zzI)}$;cM4!9XXQ4)3f1+AC}ZGFdJKX`A%sS(cGPIT;&vP@qTfb+J|$2<_A+Q#24lY zq657&fK?hog_{;DhKD&ER|1GZSyE+TdZ&jVCE}ORQA=V=+@x7Si?x6>K#m)iy`3FX zpwXJkKDh~kF6q-J?&i%m$B&0?Se$@AGxP`rpcw5SwLJI~mn9MjKN_^q%mrUDNdTha zkt?ggT1+v)W5?nY&0}3XJvj?7rz6YKqZF`EZFJx|40c3qaPgurW1&8L%F4!P%1JeU@GWg^0O z%9JTu09Grn*|4VFXYI;+m9JI0R`lWMmZMvG7W5q5b9AcJR#H1j{YL6xaOTV=k6hi) z#EEt@W|(ZfSc0`bWM%pFSG(4&AKHLhwX*Bc!|vyw?WRn5Jc`P`HERxjd~B&jz!Isl z__TD=)W=zhH&{DMa_Pv(Bi7E+&soM8ww0 zm5X)n-jM8F)+X9JI1p$xVcN7og9cr_dUepif!}}s{i}(=Iz=>i%r6FVT^O{^0}JAd za#$-~EWSh>ft>=qoB}(^(IE(mJ{fw=%Ft^j;+zFYV*p0I3?7RL8qX|=M*@|x3oucw z&*ebtQCqgWU8)q+8Aw*AY&S5|QfIJ$zd!W{t!XvLJ$d!)S!j@eE>o~U2peYcHN%HP zWb2b&KOPt1)37LTU1^~)mu~?)ey-k!_UzdMyC4Oox3{-Xks>eo&W{CLTn+^XTa?)KE7xxxx#?2EW%h>I7tdUrJ!$sv!NXq#07e?Ahruy1`lwS> z&Aocrfv-3}>wfUQ3=g-%Z>wt6hi9-8qnG~S2Rl`9fQVMzEb~JcuF@bEfy3e>FGK012Qfdgmi*Z;`g-X3e+*|X^{Tv$z&67$Ou)6WmQIVL8i zcJ11;XV1=_J^S;J65V{ODcys{7{@4e6@g@;cy+pH9alETU@#R53kH*6140ZfhVs(G z;!t6FB;p9fr2*DLd{IH8ktTT-;LR0LI|KYoffmDwiCL1e5VAM+14skIZx$~uKGn*J zT5Vp}*f3^H=%!8e2MyAO3F*c_UV&H&5f;QA6Q4{2N~Bz!NqY6*-aS01MYoKJu1K9a zr-FhiJc|5dF2-HxwUA)ITaZ{?1+*r}Sj($)&vZqRWeOG$1A~?HQoq5sgIPgLvwRV_ zsq@J;@Zi7*VC>kjq_c>Kh-lcbA>|yvU}?P3tJjbrL(*r=cU%vM3+cUg*^X4kKIcDJM z)vM$Gyk6%ZZf8d+5_|=*C2I$(Jg(0wC3*sG_vsG-8d+2eX0TOY5+t^LG3>rFR}B+O zFo#fEA;dC9@E8R$8fM(zAi+Q~q!}+QEX2enp`!+s;l$uNbD`jnW|C?F&wOfOn27*0 zp-u0VDI=Aav^!Tjp#j}o*sB+@L+xhI)?uC+227n*R9w;0t#N_|_aH%oI|O%kZ?ti3 zf=h6Bf@^ShclY2f!7XTj5Zv$1Ip-hazV#UW(tGz>t7=uvnxA*|!>t~>9ZrR|GHDH6 zWD`_$GS6;rK!RfC7zpQw$nlQf+oDaLr0UOwS~lWY^&)Jxhw2$6gzdQuv-eFl6E zCEsj?;QSpo`mGtLS)TeUyM#ECjLzJ8Hd^DwA2eQqmTKO|L}t`kemlv#d?fRe(C?`} zGhV9l=zHxn4Z9}mcCnrcfSoJm+exp`9kvnE2^nnn$?k>-o@-YJ@S+U_QJ(Po65B zFX2&26X_u_3|L`9Q%a)?Q*fUxaEMC~|Bj9?5)FAO)A5vi>#SgsTJra_V@?Fqstn1qN)b{%ohH zFgKSZZ6kQ8-dvtUV-bf=j{eINq2aml6m%7@TIt#8n&a7O7!BuNLrjcl{KHT*w+4%s zL-z|q?4L1F4II!<3Jf78O^!IuxoqpUY^HhA-uK? zmqDvg-+oS`qp8G+;$4LXvsy)jpbkk{p?XP(79}C9wN@CXiZn`qRHh#aS7*t{t4vVa+MA{N}w_C64Z^5~gK+k3% zFi->8N~-`{ZqOzH0Yzzoz!jHB2k${6zOO%DfyP)KiLVhm=GK`3I*rXw*NXpsC?@uu z(>x;t;pqB>g&mX6kL0#UieM$7vQS=NANmCP*XqPX{X3jvdocc{n z*rs&E62_}z?Ne!H*?ZL=;`VF&bffTdlvt{Wu|R{&l4{-V6Z@;=+o1H;G zB$+5<0`M@E!T~T7#KlF}w4iDA4t1|Rs*L8&Qt8$>d99TbkV` z&*1dtuhZH2)`f^IjHABP+7!#UzG}GqZb`0xD*8!&6r{8oH=!lkWc>McuQ8YJS1cVh z6J5v(8@dmuJby3-PB~uXspr+}PFi(_V{NbDTzuZFzQ5X|j_yHxEoC*-Y49(+hJ+d* z**=@^^=l%B!G**XPy&uKA{Vqd-eQrxN!{1Z9lqrze@4iZ(?2)ZlcO;{-oIpu2JsJD z2~T5fGt7s6fE^zEy9*f^Pw6}?>OYv4$f3BBYHvK|(>zHkYu^;+!R7w5Zus%WWj35# zuUX8^O>vS#l2(pyzS!J)wtxR?YAI-V8(#_LfPj$%$DnTM(<;$>1m32v;5MHCV-NZy z?XOl0FAe$qi4i)8?Q_LNDM6)K)G%pE&$SM|#n3WAKziiUh7qega;?2%^y;`b@ zSyy+i3uQKGQTaA7^ zs@*L>Z#9g9Bz0E#p_f&^9a*!c+SSVfvwHkWS2}@ihx{1d%#nFg<_tn~hA#u-?L$?0 zP!q_*)Ghrgb`PJd+EqAXpt5_{;ktE(vUQie<#mzbb%z_lE7!`ZjvMq70L^lQp)ouf z@f-BJ+qSrr8LT#vJw7Zd4w9H2q0m7)OLFo+OVKkw4SKEyl06HQ z)~aUIZK?j)CtwJz4ZqKhg?o2{g5PLk=}K{?b^t+AX20P1YRHK6$5lKQ+SJ!NjvOly z(NDEh0n39DsMAqFW2V!Lw#ipBT!9v}35a>!fY!SG>js^(#E{Vyo0PJRDeJTA(Tt{e zD9*zdgb+9%%mp!E^j9$qy#R}ImEiiLeD8NSXC5fh9P+x`sme?68UZQ+!vS)E&p`@tip5-aij|i0@Qhh-DcGhZ+VD#C`kvO z^t90SP!EjDu>)Iag6-!+NtbxO(d=TPqN#c7F7405U(ywpST5lPhk%ekT46n3%$t%hEn)`q_}YG@UD>no(xGlVv)#@8 zs-Z?4^iwXD#L(vn0i94ykewZ~MiST+(b2!RN~ZORc}%~5wWb2c_Vs4QfrXk{tS3qB zvWtU2ky7(B)dlx#rr}65?fCrmtfEere3lp*3ghJGf#PAUAHRDnZFcR7#>n26oUKuu z3-efu!Oo!?XU%sD!kerqWYKeLi^b$E5M+X&uNrE-+fbkq7zu+<%JsFOHrwMVN52Qw}z(c}Zi!b%)PR!S+{?y1y1m zOT9Rjx~;TWSyLGM@V&P+l}No>La+bGW(<)r8T7XYN#z1ft&6Rm9B-Y%KkDv^oU_9* z#7Jy>n4TsYy>#WId2aScifz+x&$gO-F`)^)9KGP`82t5IQHGHi+oz`s_=&%8rCow{ zzt@jvdi+2}TRnU(wY!x?ImRSCD_FIq;Zm;A`M_lM?JclwL_v}Yi#YHD{*B0n30Uat z2;%J2B&~R(IYo%{sJn8RA49DQtxXAvJy_8g#}YDvl8>KVu8%Y+rLnkzKWNJ<2Y6QZ z>yHjlKSEXzNOhj$bi@NHlJ0S1I8R|Cug#p?YZmGX#LNX6psbS?R|gYOX;8x8$f3{X z`~^=%GK^Q=);l|Y^oZ*D_F6kDJs%ZbzG|T8iYhS3teFIvkZA#J+CD7HuQm;gRKE{M*8Na z-|fZLa~Rp!htK)bFv|3W=^TPWPnBvqE5Ui zdQHPYgh5NXd6gOYbS!iXuUg;dzFIiaX%INquwQl?HkP7F4WY?g(f96-u__STk+%50 z;SAE7lu=+j9-9tM4>!j0^DxA;7Q>uNK9XQ8PsNyAePPxmaD8nm8}~1A z)=&y6Xrz0*q}O|YwJ8<2!0XqUT%Zy!)hiH^YgJW3YT2z`UWyfKs`T9NUtdbsT5zE_ z>^9uD@?bS||0oU8Z4Ei|K#m#5{44v}7tbU>REX$0SK= zQ|<%%NKC?(5=_q+A;f(4)%8p0tg9@IaJN_=x4OKVY!+qIg&nqgfmvo=e*S+JR*Z}j zE&6R11|4d0RIG6hCdfAr~2RTbTTuPolnp(2q(M@3paQj;Q~Dzzpt zn$*roHlT+ZBEc6=f&)B3My0h!r4NPbFo6+|MQGr?Qur)t6OZk&WZb2ys1C=1<=z1K z`~u~XL9l$NxcrzENbxD1Ry?%+=@#PgC$}ke`85z8pbLw#_QLgk;IneXyr4(KG6F=# zJ<+<*ELO;{;;~JHpLwI&&>`Frfgy16q|5{jo?^_=-u^tf6-!A7KaeeKd2)$!(FslC z4J)9f_Civi9OTjTNv{Ag0uliaE3ifwwq2M z&uE+Y=*Wo#J?i@HF2$^KS3_-=S`+sW{| z^}n)e=yS^DYC%>dW$V4Pdm+dLFR!L=zJ1`~`3QEBaB~jt{4!<&>^&~i;bQNHP=HW= z(-M^Rp$QX76DEjFaxa+Lc@bedS9HD@mrjj}RUDGc4sS zcLU9itPP(+%R2wX?j_yA|7ih&;SlmemWVP5!Moj;DpfM9;OM=^wDd(}&OfCvwXR5? zLY4z7b>W-bQkt1|WDn@=&j<=$cI5!*K(iWa=nSPRzK21Xeu7!^$y!T|=VXh;B>AWT zgGMP7xFI}4elMAxyP)fmwmGMc`7z>&1EJFft2VqZfnq%L<7&QTisZ7@ene_LFktjO z4R~T|hf-EPOCingSca1#^{C{&)@Mhv@`&%NpQrdCLa!cAzsBcSj_bfzX})FR3Ni`0 z7*LynUd{5cBp|0l-?JDV@}-r~Sr0N|e(u#}eCJ9cBq=q@l%(0gNIDGoCqh8SD&PSqg1)HFpNC#byi~>JXE5WLue@`I<{=QAU`<{B|7%PmP64K(BO$ z^+|aoBpfoV=`>o+4n|;8j0MB@Tn-Z(0pT(R3YLutiZ-v^E!Ze>EU|(LO zERcyggr|P$)9NnkV#KhHammR7S8hXZ;mN|mL_W4T7p5FtMUe@XpdENT$8YLlo)+kl zR{1`Tu8pIFG(?l&<4_ITRm&F{k4C~X(YTM~ZFDF@HslQHHP5xHTYdiS*U)ROZtn-1 z6X7xE4J`2gWmFAZp{T0IhZ=@b$i>tobI}Fv9l3NagyFdUBS-SZ=xvd=*e}hWKTE+g zozEob%JUmOHVI0t#rex@XSr{t^Sd+SZ2`6V>x->7&FYT0@4`s1L5Y-trswJ@P<)mG zag+Fpf$E%5{vk;`tnV=olJffjj#*>-$FSaJkxyAF#tO4@)+aqaUPHN}56a))dF{n~ zg+=Ja4EO^sr-_3WR?PUupsDtgt`2dYGEb3gW$oKLR8(MIsi_&#glD8i=HcOmUt!<{ zbbvb%dkZiBJid;zY((L-xj!{89TK`ll@snUJJ?o>5BD+#HBqIx%=AYuR>{J$*vfQH z5lqzU0_O)CIvNrImds})0)p>O^;}v?-{BZf5mgC|7-Mx+Jlo(Rtu%g~9!$zRA1Xy19cAIt zn@wfuDP77MYr_O8|pjY&Q0-sTEAH9~V`@dnSv19$9B0##cC{_{EsUk;foJmMe z5SI*OdyfrFoQ>KbbK;{z>`}S%oHjdA)yuzdg@j^;7AA+za#Q_rp$3Eh%qN%B>CRDu z6)_<(B((c-DmqaFl^(27M+>vO0#WeVO8_Uu?_^;d1>d5=AoI6&sb!yaS_N`J0tU^n z_-@`tq$j?R1Nh?Q=@6Z?a=F3s@bN~KYdj-PqgI93QQ~dw{5iHnOZg>!~hl^p$)7oj2M74-djPl&ubmSOu=eKNLC4VUxNsS~yVu7I zK|yPGCru35jAQx=L-dm=KziS(lZ)?B+)D8$y8N?Ook`Jj_J&R;Z-!ZCR~if|Z0~aa zfU9wbKD=cG0yFbDK=npLOGd`%L{@Q!CS$0@^XF{YgP9Nlih;g3NEWrNvhqE**s0c4 z?D}4j++H%RH~1S|M0Ojl|9sL`mx+sG+SyZ=;u4t?ZcOV+@0|7**%^8vwX76!NoK&l>Oq? za*ePctAnK()kHER?MaN|bgWDrW`dqHqJp*6s_W}2e{wTYWE(n0`@YFWv&8RB>RY`h9ilY?wk6}XT;?ZMuy?)xusAkt_rYF+(~cNDPp)7n zG;Bqy@?N6;^Rs8o$zs#82{Sce?XiV}b7H9x&MIAt@q}wENt;IbLU=mIWYf$TRS9blu_GC5FF0s^_ZxYZU#+!g5FbwE<6C%W%y$&OTTVur{i{^=9 zmsd5mV#5w1OP4nuUam1TU~{vJUv27|%IeVVc^#70tnYgYkBb9@7~r2n59xy+#)=rM zPBi2&43W?@UqN>M6qg*deGh<;JTB zd=B2_lL1<6dmSF`CG)9Dv(@0ne`kuv2@tyO%c2%^SZ9HdTvHzjd9*dnS7-_7Yd6Rj z#~ViNp^^-3$=L$b%Qq;v@HrNkvQ>? z&;<3GD_5drIhp!oJh&1$PzG$;fCBS*81i6Y^7&$}=o=wNd-Ip~RHu<5U|KC?NTTMw zs+e^oR%BF)8<+KfJ5#&hz&Gdn`?PO7Us3;^d+`6>FR~an^CcXTj=BWLB=^{=(6|fh z{c8|np!Y%MjGFPnEOZ!p)d#lS-{%{_P@2zw#B;uXZc3-AI}jV|)0qDu4J}iNL|+gX zs+jRyLa%A_`?q(g9q19-RSB~hPMKO9?^AUx;aj6mM=pDx%mQCD>iZrNtU$k%rJbLJ zldpG1!#PG>?e2**;yjQ*`J9;R zr0?a)wdY>!ciAALEIMNo+AwU(D(HLv5-|~$6L?RGbD%WIw&#vi=F_81z0v29;i+u? zR2*#)%2I_LF~^N|V(h&Zue)7X#LjZ9#d-*y1ivDZv3I6vjUkg-`2sMzC~B0nnf3DA zjf6Ugg1TF=Ufy)B8%!m8Ce~!Uh~aBzkE#W0$3tfoK9XV|J*2cjus0J2oTqF5(wk=Q z=!Q?s(6;wuA&Z~Pah3Y*1xDpCKR_Jw|A0wCBPdtdY*W-9{&GFtsHc+$C@^-537Yev zdR!gPUt0l%Q`?h$9FG$m1YOln?u^<>SQZ9R;Xk^q=WpmWtJm8o8U85su7?0A-s9t` z8kbGy&V?{Fy_^@4RslCF^*bcZgvCF%AFDVom3|{q-=;QL3VTR+_ena1mZCX-rqS(G z9}PCQM{VZi4ak!6P=kRtN9CXbivvgb8b+)mJs0p9v+BN%tK#3LSFUow`Iwbaqpork|u(2b=Wo=7r}H!Qu);5&G)?1b6%sOcs~U1>%`w* zYQOF{{QNlQkgZf1!)idz6>gB{$d^o>l6S%7OHkpviwb6#zf;sgW2@fr`?^gQ<<@qgv&83cU!-ebCp60tp<9xad2zu?ioHp!v-0}vpha@DZ)1%=xJPQ zW14Ib*uJb9&q#+_dub4tI-{ZrByur8DMV1{=eSs*K{ni9JBgHGuZs{x;LXFH?6`FT?_lL@^vpEh$W=cjWdF;h__Voo^eqv9`Odgxf%c6$hB{gat#P_jU=SLG1 z6SW^Zr6{AcTTu$g(lP>zL}V9;@(#P^784#{v1Hzb)9l-DzpImk8uqaU1iV1^1+rNA zlgr3xhfhd7Mv;!!-T?Il z(ryEFX0a|tzLN|kC23RB>3p+np7%ZUQ5SGvj`UAnfYS#0xL;7vKjVSxteO^QhtN5d!bqO8>g`g4=l!qbg1ydD8E#GbJyya zY~&}{zhOZ5m2j(0qs$kZW*czYHlf)iV#>hEd6O)qm=Kgx*?OpofU-gDpRMS|*E z181#edgJ^hfk0)a$|z-NBh%abQCGBvtK$DDE|1^YOwCR2ICldFS zNiVPiRV8!Uc}I-XTF}H)5+w$k3)yC9+?)W)^YB0s*F9K`Xq@e6yecp1Hafzy$Jb}< z!->TxkephLGZ6(Xl3J|{gU@6j#5YYIan}4X|H;0P{;hwc)MU25pX7T(Kk9l>uepHu z=S=t~&(^BtQ$vTns1_f8b2zJ4XyH#HW2hShV`T|h&odM7@Y1@yoHNrpbzdcCA6(3x8=hn!X{D!HKjJ8v-loi`@` z&KnbIi)cSSEE$@58V77Do8h<5eA?B-Xhd{}pRU?;322huEz_S7;Ej4;ABCtD!HmSh zOXKPzp_;}EVq&P2N}~LlhZBTHfj1w*Ycfo~k^lFj+(4uh*LoA2HxD zvN|M*kp#bWd#TDA{geJBLQ)J3M9RPfX6btk#Mf zE(2s;$}{KCRUnDXk8{+nr9?EO&qny%u47gNSgcfW82>UFj8BB7+1+q<4Ec~jB5DVQ zKa!0d`!heZ>rDD|o9x#ZIVxqU7-J6`)T9fBB(vn9=DK=&|8{xV*2!5^5ejtz#^NY% zD5@s32>XBfLhak}>Hb)6v}?ep+yr>ji(Ehb!slG$l@WI~vfXG0%>McfG}ayT%jRo+ z4iwT_6!wdI1%5TcQy_d8KNzL7W(2ueovp?>Y5tVen6VoWU{8ZT4*M}SX0=*me3{sx zlu^IBYa>lEj^JnJEU7y7pWBzG<_kqjECl^B z+d-U@mvjtHKi@#V)%S$#7yGgsd=DusJ(|_}szyf7Ik5ZDbiCn3XRe&MtnI3#YT zdpmhpDu=WwqqA&`@_6|{y{f(-=tGu}c~lx~F4@oePWvi9=@hB%t6COFHDs_Rh$}d( zi;U;u?CL|p`LZU(HLD1N00GCY39JpMz!=z+BIUBZz1tetO^$gZSRNniX+|he%vNun zE?9f$fr07%EAsGfHCg_6dk=zjzJdp8*r7l*qO%5(qPeb4*OH-Lfyeu9qnESTPr~Y3 z6F2zl_fZ^!#-rmb{^a-Eh8p*&5!D24Pkv{sJKvj}ADlEH7!Axal@3JXjsO;ghwBro z@qT1mf5faRLlE;qTTRcKWrbFqudtur-+)>&<1@UNK3%r7q`4){CEb&nEc5>)>8lTyKaWGji(gd=i=o|WF@aawPJX9JZjx?(Q)3GZu9zpvcfH z$A~TG1QVmvur!)j(bd`5ik^aK)Jb=mD|4Y%wVL95NM%N-ri5kPM0A6J+b&P5@$>|6 z+RmX+)i)>%WMpQ})>Mpb$;%&ACDyk4xVO_!iFRa9h}nn$%L#HIDkYM0n#ws*=W0wU z>;FJ7UCIKc|5?wq9eKWJJsweBPp>A6N+WuCIVQ~A+OFv)vJ(8vb^6sChmq8{%Rx=4 z3R35Z;D&_)%c@4ec!Io}EhR%JcE9g7_)cfLp`)*!?tcDtc+@(%NlM0`(FYO`+=`Gk zKU4BorekbyxY?B=Z%TAO-~Y3c!Il3rrB6ZoyN;rzPdR@y3Np-4LynL^tyl>3)gd-n zC4v?4*q|*t!Qo_uVDg{lQ6>hVKG*&d0?L0^Bo!bm=|uEf6(+#BtXTVM60d9X>r@44 z;oX_~HsCSp6xnJ!^FjwfG%!=RaNi>sjaGOIaxSoOYlZ!aX52sT{yK(}s^13ZZ(g0> zPnWn1Sr31}^B;^s`B<&E+Xp7FeEh&~|7+a+c9pB__%xLP|IyOGiP;XA#eJ+0@m58& zP_5d@e7HhcsGv>VoBs$k4!WG^r}7@!?Cx%HAN}#;xW(SOtQmFhM(h~L&cWPO(iROH zfw+2hsnGPA7|SC8SW3PL!O>UBdN9fw5*4VR<{DHkPV?zj4@Y2HA*o~fcu&39)mD}0 zl!kqR+UILkIfzFD{bH*lEHr&C;3%hWwsNr|D5*VODD3^09&ZpzFRDMX{A$dJ_1trt zpRn_2V0wX>tWsWqR8W;dr`ec9!$y}_2quT6+DBQuFQ9xl*v5yp@4tW3=LmNEj%VSi zU8Uo6xy|B7mQ&o+A?j+hPb9hhWaPVbb6VPTwbAwDhs&^QQZ+wpo0X4C!EJOHA&Bta zH$OZYIuy=MrneVN+|Q;|6b_eN8mRYK~Q^K`jFLQW!sIjRJ`2`-o{;H!;lVkGfOY>VGr z!siu;iBprZ}P`z{sk?ByYTJqnOFAnxE`JOSgNm)g%`31qL=9Cfx!v>I<=r zXz{hxn%iiec*pNEPk7@OWv$8?n<3*lIpbW^0h_P=#1o3>!L-j}!!qJ9EJgqRjZZyG zhj+!ESc%{te2P_%)UFfx~P@iSrA;Zq3IDFbu>v<)X<(k`pt; z3x3McaV`)KmLQ@8H^uZXbbwcjBHCl9H@!+CZ~n6g(}SJrIA$|IF?*UMPE1QAB2C zRBx-TOGakg1&4<3ZnuwU(gMKE)7Yi?7Wpay&TxN&zs5T&6MBI}Q16E~h_H34}D@|@ZJ(~NFB2E@_Y$k(M+VV@%iF@mzTx2_+@50)yOv; z=$w8Ner*DA$&gb-TD?2{SzG%G zu&RFlbUr_tb&zOdMOS>nV-y)eCZl$vtLvb+rZe@%HRi&+VTX9vEcx6S0G3uEUlTGS z70cOXjKWMuI>~U)1pyx)fWTaB*j0`oLwS9D96w(S?Pb=VK$TjU9y~K31TChb@33q= z5PB*kr2DoHhy}L^dfa6z=(+_zw>qw1dx_!mDZ1$W;jqEMDho*RB7Qjg{%5oP0( zR7P2;bp#>~C>A-@o&Z88*sGOXa<@fxuUnV%^Mr31ea&n-Rp6>V7fONA42Ni6ahPUm zZ~)0lHuyUiRh$`;)y>2b-p?-Imwtc$f=l7(&)uHR>2pQ=+U5J_$LU=T#xP-(ijA|v zkkv-v*T+*2ofQ(E7QWY40M6a!{KyFq*TGp;z%7D2MIns&3JU`V#|3a)baB7-XVQNN8v^;0e4?rjEzb=(b#KKAyrx!{>Z}-KyEqAs+G+O^n}St-g1+W!ab8 zWQ)W*g@emAxrj?M_7R}B0jl!>zsg4tM9EPNenalNGLJlPo-dtuptT$&0 zDlUi4aI7`oWUdLSnhHe7@(&Bad+jxRD$9I*7sxT!?I)xF` z3|X0}1stEKlEvO>|bIIYzdhN7^)kzD@x?s*TGC!ep^c~F8l%rhFzBHSe; zN?u>a?;rBYd{3)d{sMC4tFtv@9(x?$>tT^(suRvQ0{IH-u-C|Jk%}lSZPA~xEGzif zw6W)_i_<>|rK>P|p4mq?4A5A=yvxtLt00B@(X@~^%Sr{7A>YAbb(#HliDL(0`73U& zH6YfPYJu9Ee(jGYE?0sIzHUOcg!Z>ZfxAe@w-0))zJe-^W}^v!HrnTI!#zhhKgcF} z2mw!P2_H%eHa3fo>#AkT0-Y%MsSXA_RnKd$bzGy5v{FCxbiNVia$Miq24LUMopf}n z+FgNJ!%X0eXiWJ%Ht|aX@FgZl+#D?f$C7AYY;H8zYJV%&G|1q216%`I81{a~P4uWZW^e8g?~--zXeJA`A8Bonfnr&Zj{fb7uf(7Z>Vz128@2u1;%uHa+%hDrn~XeWWBLl9&xvr>AQG z6|rn@-t^&^F1;MMuCQ!aTdj*arv!^1;AGMtwXJ8^5WN+nMW zz{ufGns}w@)q?vHsO9;1d7)C`X_ZM^9rsRRKRDXkdjW7o=X3q0NL>8NV!j}JJzz^J z04$e*YQq>_-}MZO4n{6o0YTHbqA110;kEH7e`6z~ts(UiylN1~?f$4RZ7PI?1icR~Pgobjk`Q+>8TM1tE; zB<|hrZX_NP@?Nk$OjvAeEMR~9f!mH?xxSS=mlTUgw@?OCB$dY>fD5G82LKwS&BgF= zgPs?y2;RI5E@+Jd_5@$M(z6P!BE+;2{EQaFvw8rdwho^<+&Z4d1H{9b`%A@n&ZS46PK{wCxYOSanc06+-`qs!1AFj7jgu;VE6T@Xs#!7SWZS3 zun-ih5^j{BF__eCR8=D9kB^?jm#22~AU5wxQwU*QT z7u|X-;)pyvEx?Y{< z+W9b?x4mMo3J>d%RyzCM(%E%`%~C0u*?=1n{sD(YXm48B)*4F%R<)$YFvKx1H|Auy zr_IZ~Z!Eci->gQhL@tvX0=_!I+Ru`rcwObcV151D?E~Vl01n&<6F$50&tQ=25(NWn;{L3NSvdYv`XJUd%0O@XaJZ)9P=b$Bb@P8j7(*+2G@#{XPYx! z>w>?US)4PvK|c@PP+4JZ!#3L8AZI_~9qa1m2;DD8o7K*sR)MjP)NC}sy}$iy@-11= zYaHp>VJxV@eq?Ya$oMHpdM(>|yZ3TDwP~PmQnM8ga;BzNq7Z+xc)xkJ=Ki)D1;F&C zv-sWjf5hkb-2R+M=K$W@;}t-|8HwF!vc*pqSD{VYq7ejsB1|a7APOEM0Wi?|^$9i# z&w$TYAFrg3;;gL0?Tpu5i4UJtXo3YYoES&d5{AfvIBB~q)M_4u%KElXEWvp2BK7TG zjSI-Xj`Ep=TBD}`8Ni%YT=(p1q?P|@oC01H;!sqgtSP`p6nJ!$3;ePBUUR*|WBXA= zEMlU>*1+>@54SUV)=_9Z*I|6DJYWZvkYlGe+PO&=W8?R&k*_Fx)kOL|apqz3pnv3( zV{fVICK|D>{JD&ld@H$RjE?tH(x>DKO?B&rMm&92>+7opaCl6_WZZ-CB$6yenL!TG zd#*Y?FAowvPpqjJ9U-8KGBIuSx59Fw)#`L;eN#z zSf~Fpn$W^#q{C8OGq6f7i;fe|8#Bb)=O5q|{*wy<@+KK_QRTXQ{ob^vNO`E{j3KpHtKY>M z?BqQKXA9c)pVvv|KTCZN{{iAH(MnKeOri=3;1r1$Pyw2U#fMVA&qQ;c3k|{~k|mH% z<+xa(c1bbtq%zF8c(U@msi7v+f?#2O zXcT3M5g%3xZH&Z{ZAEdjz)dVBfcaPefu+DE`bMW((T;p-q;Li!yZw4AfHz2vjv^Ma zOS?|<6ALWDK2fN2t}_{mJzLrFc@-eC>s&Lry?871bVhgvc!xS8j(?Q&T3HRjKkeV8YI#`)by8O`aV5bV|D$Xvb4j$zuEyrNSXK%+NBm}dw!BCRUHtK)+(#6IcWu^ zx(w{s%HGlRynpVx3Z7bRIA}LH16e07d^8%0OaOzvvKJ}4)1EtGip#O?_*OEh72*Z7 zHaLg$m4<(-0_~|3A)i=rF50Boh#Zx@kmDk#Ry_@i3t3X622=Qey7(YzoUPUB<9m)- zlWYdy`=9ObCS4daY2kaaN%GGNYt)A!kHDJT#N+g ztR6PSILW5#bNpgRznCc=!L0iYjoPfriR8)vm#TrQ3zd>p;;Q6F`G^3aW5-$d_J2SV zQ|5WOr`&0-)q!YIN4KUq5h@!~$nL~6xDU(bq&verR@SZFv+kS`_)Srx~o&tPw%`k%TA*g7d7GS=f zZeRI3|1#kI{k%Bp3c6ZQ*(!@?4iMIaxWYUxb9qzI=DH@m#d`BHyKIbkvUsNx3y z=Xqy-Ku+C1=%+7 zi{nEkNl~H19T;4Wa{X2KXA4s94jwW`aUhs^O_UK5Cfe6LT z&TV+s<^55k%`Q=RoaBMqap&iJ_m8_C)D3{PgpQd(FyF#*VjI-pLPcQ8gpX^_6!cKI z8I(k>Avl+|(qNhX_2%y(v&?TU!9o&f&?T+3Y2}Ac=pfz>#T=n5lI6Arb4pk`3I#K| zwN^(;(G>{+*hR@b%GxIEqRIL}&5B-ouwHS&&+dzpcGk^##mv|RZ}@_de;S7>I8=j1 zEh4F>Pcr`cgauMDyLpUA9|3De*Q1&qAf5MhoF-)@;4Xtr4%VNes1koz9w&Rpt5R<9 ziEH!Ydj$BpaT^A<(#tuSiyf!kmaPoGhj^}KdM?sMwnC1tzvMbA*hehC!Q8k?MiNA8 z5{EReCzWk9SLuopsj(B%D+H~_%!KrVw{Q1Fga z(&2x?`i|=gw&}y?3$$Idyu4wmCaDKZHW!HPIubX3ONJg z4f_Ly2CM4r+@5Lfq(a_6FS2TxV4qhfktM?${|F49H!C*35p-Zno4TgMA_ycC_ z3({+OHNUfOLd4l-i;u^HEZU(qG-@$0|8^UQZgTEE&&hM#W7`|f3*<%w(+ioq$9jJrXsc3AIpA)XZO3j z22(t>DFWUs{X|oP3q;i3poFCQ&F7^>*hij|6(q3M`D9b;c*bM%W1(jw2{@eyVz8m@eB33SG8Y@Fe3UR2EW#Y9yPi};$ zML}zz@14Mj*^6hjD=fDY36co zWcp?A+Zo{R6BE<*cRSz#{GZ(k1ZJRzw=Z*{`JF-1t?X-|w~1qXG9!k^sSd+%SYdua zK~X>RiK3>uC{{qB zMCB-bHZ4pmiYqG6&XyJof|eLjQV>D6oE5`9s&u_tQruCI523aqsO-XU>*MI5wIil( zR(NPtqmE?LM?!JF`&;kGkE#`Fe9l0_xAeK9LH#j|1raVNk2Mv?U^OevB#cv%(S+Fm zi>ciWwf%D3lI5zs;A~%4!#ypPCI&Jq>yhA=56B0>fK~u#3p6v4?Nu7W?DCwRpI3XE zEQZ@}w9nR{yP}~+E*uB3sk;Znw_ez<)Iu*ai7DMd)_;I;7TP}@gn4#eNo0Ks6rvAf zgd$L6+_n+Yx$%#04Kj`?M&u0;XGpc+l3MF6)!)5Yt{ZZ~-F)9zVufwO>366UH_c@v zgf5A~bX?~`=^Ca>AI(l!NxePxc_ZdInJmG2*jcg}pVuGD3)l@FjixuCL}rEt*AKVT$4+8RcWoPYC%ig zwo20`E3zmUL!J|GQj;a3qBDuX7=PwHbNt#vjbu#a$&oE^nEjOPeDSiS@OHN`n{dgj zSD(f>Y&lM0TgQgMxmjvT+4YUof6Xfd8FK-me5M=9$}`t^oreksElO?P{*Bh%l15AJMv9X0u=116%cO7P)U=L%TXAS*Px;jjThW5>T zY%c!(%FTO!#)4LRh;7gL-Qm7qU-Zj-o;W&u?@lTR{ZWS(Cq-6N1Op0sHi*fLBJ308 z_D7m#py&_~5Rj7H`an7swySq&!6DS8WrT1=(+u)S+l9i#E2!}5r(Q5}QAtiawZqkA zU(KdA#!sF~CymBT^E=3H)n;}hY0K~iw2c=k2Iqg8OZ zG{;9FgQij%y&KH0)7s!ZwlzaBz!Xo$5BU7}yp*ma=f6N><6fFE{S~RLvsgZD+j2Jz zsH3Sej-G$6A>HU2zY#q0S?Ik+Hsa58x%vA*qFDt}!wL6oHx>`Osb%bN^zazj>wnnq_}US&HNj&ZXnwX&xcJNAHDOy0EA-V00leJ@F|p1;|7h9-tA~x^$yO zXz%9;p(&+S;(w(yE2WDeipx_i49*rv0|D&2Cs zP3RGIaSb^feV3O4@XiNa4S)-Cv)BfwrjBK14FT8Q@m>2*OH^CWZVCtM%pAfV2%KdKh6g7k>!xU6RUN!!ER z&Hyr0@$lbkLXKB9r^1uG|JU6+zUS3_QKJnS+qTo#PQylxZQE9(#%W{Qwr$&1W7|4A z&+mG#_c|ZWKXCH-&c5%p*IE-}jyZ>?^V{<)2)u*aT1fcM*+`W@QVy%i)pvCSAC1Pp z4KRQpwCd#H3bl`?`0Q17XP|-_Tt?a>uijKzXSq)>c+d?l4)_6ajsym_xDK{8X zVE$EGI$l@VR}VJ)S2XV4u(z*xldV?~^SKX6P*9MmWs7FP@9kuN>a=xlpfLa$dcQsy5X1*@0&+e@cEF=h&>!6*w@NH+E?y zHc^_W+4MO&6zv$dJaT6-RFf?M`IFkARXeaAqOmyKz3ajEG!jdY!BHaJ`aYdj-v7g2 zXp(>cr^KYn%d6Ew8F+z*(<0@pbQEB~J+=!HyeAe8;^d*c@fUn1*+SjA*{LEdWi85q z#VhfeRT?mgu6-GrmBsv~`nOf+yOm1e^L6oPXT}p-$?;^Z37u}sEzpG+8OPAcKsD51 zR{KPRw%oPa;(4}`8Q`N+?=nA8MMv(qU$#*IFXK4;tHTZmTK?c3{ez^A;V`CGGDJ!L zSgNO87$1L|E=FVV%u3flK}uL z+%Csxs)M0FQB>JEIGjsaRcbba@ydskEeS6k%LauJi)-hy#GEO-hjcQ^!!o(qcIJu; zy}aWY8$8O+DP?DB9$_N&Iy>LSiVqJBTsABtV7@x+;k3Iuz81(_w7=L~CeW?Ao)Yav z@FR)zG@4EO(+A&c4o3jnPX(T58t_WK#sdyT_N!o$1vZ$$iZ83ag+}X#n_ZZz?{?h2 z=&!TY6O|v2oB+4*Zo3ZggY&A;&q>>h_v0v&O}{6lbHtWwRV5$F)q3^*F?xppdO5K& zCAm>JSXkf{2^m?wM2T9fbGA^HkWtUK)`Jup8hY{iU{W>9*VN`Avk^i(G9su7Une}m z6o7*l`8@BxHBZN-`_Gy4Ik9pB>Ntxe7LEszBXMU zC-KFpv+U|uK#gKExjhsnCU(|n46;R|`xGM14#zlygP8#@J5T+K%wu4{?Q|@H*pb3! zDU`zq;>?uI7d-CuHzR}r?_M*$t7`+8uz}>U7q8mZ)`lhb6#m;}-K7UKEHyRNRoR0C z(_Eq9)O8Rc!rSw9t2=;>`8aAN4XE$*ECNA_O^xRUpnG(ejlxx_`|SO4fq*$MFmS$H zOG!f$CRhb3FQ1O_&1~;vwOEmo&Wr2F;X2>wVeQ;Q4ABk_5gLJDrYIVat^d&|zuGW( ze=6^H*fiG5o^g69As(L8E6gw1#ppGjlDV_<6hrhDk&RGC8l}7r&9Z)8bg2rv@+7ERS|(jw8tIRBpSJ2zrO{(;iU8U z=+ERy0F@0P6kP@|a_LO|7v(WDT9t@#Y$X~cDo+C0`4Cm~SB3lzr)~!AR8*IX`BKbo zttuX-c`s=^E}VlKJLvFQb=`BIvikA<&l8bw4-0ply?R<`-w>2MGibM}$f`&%qyG3#V)h15EWOdm6@kO`Y zqyCM?1O*ipQ1|q#A5kD%%Wji~0ab?wLWtTG+)jp!#5SUyVi)T_w?k*g40b0$@PDp% zo2T+5&UxMCqCjA-?hRXAHF!2jz z3h&iTOC}+`PG?%|oT*pcQm{ST?B1}$4PMU_Uq`ZfZ5}0|@VzPi1QnbnoAt9(rOpy` zh>GI;R0z8Gct4zP_4>9D1@ML^9w1AfA1*$Ck#q*=b`;cr4aN}Zn)d;zCBnNNdVmA@ zHnT%PcVB)shaFLUdz!&#U#>}cz6uHHXqb{f-_lIJX+^(#X|qkYFYkzsN{d|-p3(Fg zL^Q3)03gwTY>u*y;eFI{B)f-F=#d^1LNp=27w4TA2`Op3cFT_rFZfJ1|B7(Qq|JD% z=7qk#EUjZ~$II(chTJ2XfN}%ZFuQ2hc%D?#lLL)JXsTM5-XJ<$_9UsfCgoCVTkTfUddb&#bNm%>~u@-_{e+MWXUbXlc zs3(5gt539Wu3e5Y48rq(#9-~lV}FqxkU6)rR#1qkp_ijA2d3tuK@TeGb-=z2|Fdfb z4ofl~NbwRi)N3bFQmx~`DZ{>e%knz}ih-fcrfW|6066mj9!64T`GI| z@s8r?py^hZ9}}cO0esQ`g<}wcGFu~^losB#*r{%C%C^*=UmC){OeQiUEtAfXe&yXb zqNseND&yk9_~7Jw%RSuK1=V1e{i3qrvcmkxL9gxao?r9TzZ{%6&qCu z0>((c-Z;6e@$#g5Fj1eW#By6vq}rO1kHx8R4cLctx@h;$_}`CJE$8&Zlaj^13!g+# zdg{*#{^qyE{M4hqt(?C2mb9>f0c{xg)D^pGuYL$b9BJ^H@K%z+;-8TO#<1u!<#xr> zP3F@Sm{L@x(QenjUjlpzwl&C#6~E_I8_#TSXBdwvJwWB8I5R5!sWDEi^LF0&{j}t8 zMTlxBttuWoDj`c!YH`-Ej&7mcC3o?SNUE~`qlKJx?jKuZMCe+TGZ>o~)q6xcj^k}Z z6KDJGJ;d>y^;(O4UkEY?2*}|TKRPWP7433siADm!iX90$g3vb#bgzw^6Y>RoKKM-%(3Z1ZCwV~nM$viiJRDVOqJ z90ac>;ZUPJ*il&}v(jpnDE2LE%TP|G_j1_0t0fF&15rwoAmEO_aylH-llY$h&=hp` z6Uv8?7qT?zj4WjfX9h(a^?|+vLQ=j`5>n*luGjz_r z21i95vspejT0JeBX3EcpTbalB0GEk+Xs%B%xkDbhPd=KVBK$4OtU}9%%OPq{)bh`% zygZn&@ZiH)s(}~ACVdik-8;O2S(hI^m^rvP|KHE>g6Iw^gpT`8E!4<^M9&=8%SN7i zCj9?!0esXt{)y5zCw9bIPI*-RNG2H__*T35dnV!*yzF_+@xCzWd+aK9+w419T&&Z6 zM2>ZBN1bR)%5cT%TZ$wj=8XQ*sW8YHFXA3?&B~16umdzgfGGsW=~R@??-vg1Ynv%w zUPAwVDszd2S-=}O!!ZBaTh#HiemGypxG39T1cco?PnWjUtBAHu=JxY;e?0B(&eiEK z_Et4pLp7;Knh15_r~zqEqo1$crL`}GlntRD;K$qV7BrW>nMUF`6EQ1T*O?s3t#meG zJ+4$I8FpH~oZY)NE*Ay5^WJloR5cIp!D99tj1?QtB{NSl2enOS)5YT1q%sL}=nAO| zxIgB#!?9fESSB*^5wpJCM2NP99ePt?2T^DA;3ss5X$T^!g-|B-7LZFdc*AHzUr!fhUnB1>o2Le{PK6GybgKZhI&7n**^>d z2i(h6p=)ac`9D@R#ERlz9a;I6($+?x%M%Z~he;{pSo``7)BYb2%%=y^Km~!4Y5&+bwk5*f@Q|!QgF%aVle496 z4)DUqFLFKH@DlLHiyFgNjkd7SB9UfehrppzP-{&5Upj09T zA1iqmH1b-yO3_2h35*?UJm~zG?<-Ew3+K0Aq84DIQ$63=(!=ZZ@L>~#9>^_=*Za1y z)Lsuq2owKmi|=xEm)_vbQ%_d!i(>}5e|dPm-oen`x79onZ%@oKn~JQ)YjKt9PHYJi4YR(GR4lI5WSnd!d|oqHpB?K zQ^da?oPN9MgVE;oWHNdt@z18=zxpjKv9vfB0R3lSgkwy*+1=4P(4}dOx!7v@o+F4d zsl2W#Frcw=<9u;VHnYFMi-c+V7iw%kt?^`q4cvHYQ^Tx;ghw-1S2;l|Wy3h6_3=Wb zR3(%_B^B0DitqgOcX*sjd_9|ftUG+MO0`z6;_D~6!d*FF@6N#ipoizX?7}-dhzFV6 zkp#!9eBirs8+|g14=Nji+_aNVHZU%<2J9uO_C+mx@DRZWY6FLRZpJ2^0)BCJXBr}& z-HS)DIAWigkD?cAwv^wfOO!aZ_UcM4I~18kHs3@GP(yk~PL5fu(BiVc%qkAlw3@yp zoa^VF6KbK}L1*V+f=-f!i43-hhxx9bjz(YuQ_4h$dU8uX4kGt3Qk z?&}99LT+XiG|`JBY5(*cZ7+;p9#Rcf55{}ZttXNUoBouMIR@F!lZd(y3fPqN6iuwW zN`*Ow9?##Q7<5nVpTTOsah{4dTG{;8w3Kj+GWb*OML5^kN{w~BV zoml_&zKn=!25n30Q>Zo<>Y?^t>BNBIj^bSPy%l2Obp%70K6=s-(tGx~{a{Q)&4x5y zfbluM)55lx+=%RCj>6>IJXaB;a;vEI%eZbEHqjsEk>a8=c^V6iP-JQo`Ki!?fdDCB zG@U-!PWNzAY&+!|3~Zwos5ZMOyRk6mV{j(B$3RA)FPksT^m0u$9?U1`MoQ#yX)R0% zs~G-8zeO#{98by_YBXhJkJi;@Fhgy?; zgkTcHqo@h_v{qYs`-B@#;i0Qq&g}FPLc72W z{t0w~&a;Ol9Q+eY(ET{OssV8bf58?2E=;3m9HF2ly?j2YM5X?7g4xQ-)Y8$>q2#gB zVfwNG@2TB~Tg%{qwJZrOj(W@XjopZE(zi7iq z>+~vr8|!4wiu1J;2c$^3(7?g{&;ZE!Co&204-W8=L|ix+Mi(g?&vC|rCG^cbqm-45@p=T~qY@_as=AxxmCb1Q0_$)Aua>YsYDp^s{AmVd6W^XynT&DWHFsbkIJy7=;5`1n$ru#?HHk<5o+Z$g1WGlq` zvKvy#mf}`gJ3R;n$z{o>*NtSfJS7Z6~uNEof!)%*4^;%;sG5VapUNYgq#lK(nB6beG zy?dTs)X{{6g)t`{GOeRiM|(eA@A=gt0dK3!Qj-<*;MYpJ97&^!pnczU zbarDsT+vnZJ3&?2k9AVDCToA+dMewqPiubF*2hDm7V1g3R)-j>m7Ba54 zRHYAIUFu<4ys1m}iQf_UoeBrRRcFRyf8W|5^~JmydMD+Sk$O=pl_^g33yO`6QH2cl zRK3a>iFXg43&_I=rV$TZ0VM7uXZ}(@eD9|x=`lbtSfEpP?cW)ZrTvvdHUuP#XHQ@! zOv)lfGw6>ZL|^GuthK%_s+6fkm+p=wI|BUvx|A%5>5M48K?cIYQ%qtLyUXjWJy9u) zx3euh@!2nt;a{V79J;yMe5aP zD{BFZmwg^v{ z0RZq}VwJIe0pVO5Z*q+k!l^(D-H1v_Tt*}ev|k$3Zxt4|6i9d+@H2frF}`x0_UGrR zySvO1fwL3y(MGz5t1Bipy>{pTp#6mRvI`7$4MU{j&nxL5As7UloaxK$5m@9*9u+aM zJ|F|_y8?oYj9kJIZL7a|LvRq8&bxnfbX0|emA&-`mm>pU+BS?XkEnd(F&;tdGl@*a zU!p;RA!7iPzib2Sht`m8<#TdU+OGy~`8=~)PB9tSZP!ymP7Hk5v{j_03QgfNl(XDG zgsPK>vqS#rCLp3YI@St8K6?XFuI55s^!sb|;fpKYZn?P-o7jt$y1H#{Od1UH!lq+M z+hvGXCCa6*udk2N+0wlb71(PAfG$_={JvAp&l-cwMessE@DJxzt#nL;#Et4P%Ganj>A< z+3WCeVNKyPYLJB|d?z;9%!4Aw`a=k6GnEBmV?v+j0t;O5e?w+3KPt)EeJ;!NCSNlc z+Ftxqj|C^jJ;sTSTGI_t^Xm4Qw)8FGs({1pe}2Jlz_$q&yRFb;}u! zrQ89tnJbD8(6;%h?;+S3JmidM=-HH39{*_!A5lS0e_oS9tp!8jh4VN3TSZrmsZ={Z zn{_^ksnjukPwm*W%3!ABq8=vcB0kr7kuc%+KA+8M{*F$gRQ2%z$iFs2*9t+T$3Gs- zE2{FW>91Z}N2ik972{jFUdrOCPKDgAJtp!8+|TW99cRf3~=sm0SpLaV;{Ti&&z%wNycATp4D3D&iWQI44A3GbF#Z@kJ+5#EF7`WtS0k-XbI zMl8bAwi(&i)7%`33zl=m5y|A0r`5+}h&;H!atewG&F=~4OsfqU)T(?%Oiyn1@$5m(g=69T!-MviM(Jqg-@4=!6vv8i zjMq2ZtP?SXM*gIR{})i%Z5R836U2szp!@i?cJcQ1b|ei4hRMH0 zx!0E~dP!`3`vlx5Vc>NBAP4Q~E(VwEdl!I0N>@4>mK5g464ErdL5QK(h6xXO)IUO$ zRArZKCTM&!UHt>gzJE6+)yI&}GDHotj-v6hy<)@8;i$jI9WSplj?E?}%HFL06d=Q- zB#pTS;geXbFc7ymF-IaA2Ipoixa|-KZ7&2VI?dXDi{;~@m)@1Nm1UBi>K=awngF!Q ztne7T@ZV#VRO#eH1|wt?sERb|uYXaIT|vC8XTYupx6VM~;+N@ynNRZ|t#d!$Xnz4Z zPFCwJO*n4ei;3hoReRv{_90$kN@ZxmxLyHeq=cVMarcxg%S zmj!vN&cnd>j(1kr95K@6uDQQN`UCxMQGr@bO8UE*r&oCxrPE~)(Q+ncD9`kww9d1F zbmrKC!+GCQWA8l}+rqzU4p*7ZR`~GaFDpvNWJRjRmk|_sUp4nz^PBaOb*QB+kg?g1 ztfd8)@2Qq{RfC=h*y^@l{hB#`z>(<45ce9J6nf>St{?lEOwUUy#gXjODq1Q#Y2k zKa~+c${}v?!Gu|_Di{yX>tehU05WLKh;^G^R`f#Om3!nj`+y5*6d8&ZVulQ-H=05Unsfs~u2KlDn?T&N5tV>-r zMQtK}-``T#SgHQhDMrN3YK!r;oG7Y30-FH3Oq~!{VOesE7e%*`%lZne)0Al`%i&J3 zevr)p9y$~=_h-n9x~m5D1KvE#9gYP*Vq{yxX)k}uN}HJqJfEVbra`KAukda9cb}`=X>m zUdqX1a2{g2`FwxGB)e03?yeOB#T`JvI6>Y*P^%%b)ax_SDIkIa= zr-#4_!Xhz&($6tgYY{OP?JcgS@Kop z<~|jh4gAkV%}5$Gs*ccA+!&5yXpT$^L1viD;4}hHBjsP1$RuDa=BlH0dUnR1%~Z-U z>M!SxhtVK+nHaF!cjuWZ&oi0IH8(rr?yv;BpZ~tS03C~#FnYy%SeeMAV46^zqP(I2 znu!bWRW{+3j3=GMzDNSm3LD5wUf&j8e#liD;lVt$BIHk!9&DV2MPa7=eqz=#(=>QF z$7xA`0aNt#PzMkHs&NDhfhBHcmy)78RHg|W3_m%)a_GyiJsv@>hhvE)kRO8o&5 z`=)Zh<$lsmfPBBhZN$bE+0Wzdcs!W}@_>r#2T-X=lGU$7L6^#6OJRk}oc~q8?v1R_ zhVGyGvp=&3tCYOm(jyo1SUA|pvRXdX$ei##Vxa61eX*ujnletWwg|(5V@)Ehp##!` zrgu}D|03tl?lW4z(QIL<&_YCU{cuv$wf^5CG|f@^_(4WEUviv%OCV4Up$)+T*Q4uaR-h|D}NJ&_AjX~LedRJ1&9BMsL%2Y(N@!FngUz%YR{uyJZQT6DlPF#R z%fo=zT=9nn`_a&I+$XznD}xw7{U);#VAlbmN6&H9 zZ-nxqQ;cD`9dO#tq$=V`EYERVSwwRwN0xOo5jbJYC136SDHsV8wh*YnIGGVLHtMG& z-ce9>yBXaPkNb{y9Rc*m@dVP;565?nu4P~DMBT_o56h{An0l!M@4RpQJ#l1zzNXU^ zE96T8&1FMJGuAMQv9o(t9Lbo#1zi;ubcnS<g#*VEdW2&#x_9CkfVC1LKDx4Eu$^u;j{c z#lI;WNGk)8D~gn^_J`bsQp2ORpI(IF6Q*V$GtT^A0hOz)S?E8I`=^Tf#VEcX~3+eP= zY?#pgdNoSXt(kBloRqz@1sPSn; z@D=YAAz~*{_Y{Yr)HLn6g(+8%}1Zt}ZRA!8lH53%Ga zDp$$6D>gPvM7CkK_*ZmCd;-*60fvH2)57Rzsca@cP@I{^UeeHk05Wa63ji&cr0)Pl_qEmtQl!HpW!4ElNj6xRhXwQGx1OhF9e=*f7ybonR3Gi9lM5an z+b0jHn~l>!sx_shuJkfYi)7t|kLz{FO?jf{Jz@q+okde)BETR%paYiXVg4j4Re}(l zNay;P{d9FHk zE0`oaX%m*PvKlhSBT-V!H^VgnEl0``?mtkOM>i2Jl%?bDEfTgeFv*xg?q$a^C8?@$ zpK1GA=BH)JybwJ<{)sHd>P2;pl%QiB7j(8IU}^<}tdQ@%VR|!HFX0Y@2%~Dq9G4c| zc!b!{X&!4cuRhB7#LLxg9Y#?{GR+E0FQFmO824cj<#@E6_Gan!l0u zT#&49zh5+c9RJo^>&h>Ury$dmT0wM!xixuKxD3?K&}MY% z&TFJ}e$R2dQB6&~Va5Oq!wVizTzPr@I()W{49aO)JQwXm96Hh3f~E4(eOBC)-8}2y zcd+SAAgLHj0k6p%Cnp>aQTEo2XR)qC-I@}5N3Q3(HXl|(9dkFd6G>HD>j2hy+byLr z^GmP=?c*_MN>wDNs}vbuT&u;-d@B;Hp5MAt?xB@u4h?5`G|N*kv4GL_|^PhA?7UvYN>c1-?_e^R%R`M{-ALFBb?wo%5W-sul{jf1HIV<5V3z@3~8p zC;ir2Pt}*6WI7A3TeLjO^m>Mzm2ykW0&U>`8Yp|pf&5qet$bHkVscavs9P~(0DFmj z;yt{+!4Ilt@5mcvcu~-<6mNh@qZbu$!f1|_OCQl>zD?1m#~&;qse!11VNPa_ob7B7 z>{=YJ$Evra4ZSlcx=OFwp4Lll_Z0U~aM`SPPr#4$>oOK2Q?R{K_SgD9ESIKGfDh!~ z$w1gYDOL6@ZHqs;nV!}Er+rBVAWzr(08d)=P` zBHN}XPUDR&H(AUqGAD*xJx(Z+IvUmrlt@L4JzvI8!RX_G1ihnG0i)?e?c%YY_Nba=-f z4Uyg($j`>h$HvuIj!ZLl@m0Ow^vhppURbVg)&0w%o0(zF%*e4u#4G!~J$vDK&kOlc zpEYVKq9Y6;_LyCy8a@{eoU5k18qgE8;&n_d`^lUl+$g{*Y_~)zx}Q>)u;p zg&~7K^d$fEW1o%Nd%?W74c;zeU+<}V z&VX3E!?mQfqspQ$vn8Kmc#kq(Puy>k>*u`h#_y$LI;qXR=@XT#tF~;w6fli+be8!D? z>GTGt>PDVo*Spwnzv0h>?EPLx_NwQ_?%GGn1Ar-vR7x<|q(Og6!IjMF?jcKu56`BJ zy+>2_s-a3vOdH7Q!k!bS*xlQe;#ezl2KqFSDGFFkOs3+edp47=)I>C7w$fL)LK}ZOMZ(1#v0+{c! zJy-kn|5Y%t@qt$x@eZ-~et`y3-gECoB!zpGqWj8AHR-BVFGAZX z@6*j(SmrRhRNzpBduc?Z@M#ol75pWj?k)FSpO=o%Kh&>AE>&-ZAQ<_Wba#o6i|QTE zCbG|h7c7-4M=`aPNFfy!20pQWY9Jz+h^tg4ICeoIx-1zSBAB|2nUR23CM1%?cKK~nPgBN_%ZX)LWy&x6+BYUVCSpr0Yb?neA?}PnZ>u11Mk(ftWOjcb zByDg~U=7yKk>%cA_+Nw2EKoR4JJPM@)x$yZ4bsK^z(LR^sU4b1|Jd_7?X~c}xN0|j zb%anC7E#@1DZol-W-u|0_l|R;M;_RV8-XsE!H^%JC$^b zzP_@FJB~2w%RcTrzp6} zyqGd{9mSm)P~=+=b}jTU#<7tX`Q&fa5^cEvjTmFs->&ie1wBXv;T$`9o^a}H1)UL` z3>t#?H-DX;ZJ>G(t~y%1_@5znil+VLN#9ZU+qWwcnMSaDW8C@ArN6V{!!vxtPy#^b z5@p-u(Je<>6(L?31=X|V6Q|3Exq?!~T?iyhl!9fslOT?7y}m3f!luZqDsc` z_#yly5WR*hC+yHz+0enEav0V}E6I&!`XEJ?OOs1Bwb0!)w4v{<;f)D#04aICLYK$P zZ6QnDcG62T-dl`?LNFz&bcilGss81yFjJL{!Lj+jqhI=Y^yfF4cx_jZy6ipOKDGyS zz1GIZt)DNC>@9bWwTcb1bzblzSN;aM{Z_gzoja-38R3)eL2<6paeD|fe=F2(^(a3$ z%*c6P*`WX|hKev?F-qI*-8WcC1yMJ>S0BoCnp>Wh_rF?yRqW(nqgyL&UG$Xz)7u1F zMR;ty&3t}W%4xxIHj8_f5mESb#!P@SPJY}Q$<0Ru1qQ90skCd>?u;8*xh5?r&j11YFwy!E`o>P%;P?K1rmy>z2YWZj#385l^P zbn;2V$*9$6)D{29n|kBqnBr#)2lD+3@Q}Sppm!y#s}6aV2EVB9h_8Wf)q0iAA<-&P z$U6JCVCfT-W}nTf4ILPWkLe2n0=+fuThZBcdN)t;R`%q`=Z^*lK|(@E5CHq$#RU4> zpt--vxV8Kvk_vRgXmzf8fp`jI=xf_gPwS-S?yGns72qFgXn+@^em6eqSom8|PEM}a zJ6|Ej!A=MH(qwhNqM_o>`Yv8XRI~r9A_iwv0`;fE?)h}s+ldU3=O}(dAw#dwPugLe zoW)RX;nq@!nvvWM+#3FE1+{~d$HJeDQoclAfUmeVC4%A<+j{P2O;k_?kBH?=q)TiZ zU#+QJ{mL;K@yYEF#1IG0NB9TG&<>C2mgEe2;Xzv3lIbd@32ZN9nw3NJQi9^Ai};Eh)ghsNnLkQeh~Xy+^tX7J zszw)S66mVrtavQ(j>(mwHS*ZN0Nt^oi!UG-M!iDhv1RGcsOxWsuFF0H=^m z!+X*;3Y>x|)>fXvK*qY-PC^Vvr%hNPC!qxLI(0a}Ie)wF*|Wd+;x{xj#1t?XwZ<$JqjE*+L3Gq5ofJx)2)#B|&9OH~K&>!=ZZ5p>J)ZYVth# z&0%jem==#;^>9s!p%V=WDQdsm_`h(D>2u9Xu!(~*l;8#i2LAs3e+l?fy!fA<<4@2M zC+-1~AGs8}^~&7p>PkIhdIk0BC+ukQX=H2L<=qMXGh*O=5&ph?qW{I$)StK{us_Sd z#hI|ORmu?m&tQBf%Kdbm@*8n$g}}T1|9Xg>otV3#LniQm&(9>o1Mgz&UTn|(a}$B* zFv9?s0DDhn*c-UYpPw;e2Yx@xDsKD%@S;CI^M3>V|9||Du8%Ljc$`;8YbKYz0Dlsq LKSe5p^!)!HB0#$g diff --git a/website/images/subcollectives-impact.png b/website/images/subcollectives-impact.png deleted file mode 100644 index c5e7958e372a38013ea3b8253600e30277decfd5..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 57834 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
Z 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%0AL%KV=`G$!|@=|3`Y%n^!#M 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�tMSHW_*gs%7?zFyhg_ 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 diff --git a/website/images/subcollectives-multiple-middleware.png b/website/images/subcollectives-multiple-middleware.png deleted file mode 100644 index bc2eab64f4a2516fba98ec17ba0b201e83d36aba..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 48589 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 This page describes MCollective's components and global configuration. -> -> * For an overview of deployment topics, see [the deployment index page](/mcollective/deploy/index.html). -> * To try MCollective now, see the [Vagrant demo toolkit](/mcollective/deploy/demo.html). -> * To deploy MCollective into your production environment, see the [standard deployment getting started guide](/mcollective/deploy/standard.html). - -## The MCollective Components - -The architecture of MCollective is based around three main components: **servers,** **clients,** and the **middleware.** Servers and clients also use various sub-components, which are mentioned in their respective sections. - -![Diagram: A simplified drawing of MCollective's architecture. Connections between a few clients and many servers are mediated by the middleware.](./images/deployment.gif) - -> **Terminology notes:** If you're familiar with Puppet, note that MCollective uses the term "server" a bit differently. -> -> * Instead of thinking "web server" or "puppet master server," think "SSH server." These are machines that mainly perform some other business purpose, but are also listening for MCollective requests. -> * From the user's perspective, servers accept inbound requests and react to them. From the middleware's perspective, servers are just another kind of client --- they proactively initiate a connection and subscribe to the types of messages they care about. -> -> The term "agent" is also different. In Puppet, the agent is a daemon that fetches and applies configurations --- the equivalent of the `mcollectived` server daemon. In MCollective, an agent is just a **bundle of actions** distributed as a plugin. -> -> Thing | Puppet | MCollective -> ----------------------------------------------|---------------------|----------------------------- -> Service that makes changes to the system | puppet agent | `mcollectived` server daemon -> Plugins that enable new actions/functionality | types and providers | agent plugins - -### Servers - -An MCollective server (often just called a "node") is a computer that **can be controlled via MCollective.** Servers run the MCollective daemon (`mcollectived`), and have any number of **agent plugins** installed. - -![Diagram of the server. The connector plugin pulls requests, the security plugin validates them, and actions are executed by triggering agent plugins.](./images/server.gif) - -The `mcollectived` service uses several kinds of plugin: - -* A **connector plugin** to connect to the middleware, poll it for requests, and send replies -* A **security plugin** to validate and filter those requests, and encode replies -* Several **agent plugins** to execute actions from requests - -The connector and security plugins are mandatory, and the clients must use the same plugins as the servers. - -Servers can also use: - -* A **registration plugin** to send a heartbeat and metadata to some kind of inventory database -* **Data plugins** to enable more complicated request filtering -* An **authorization plugin** to authorize requests on a per-action basis -* An **audit plugin** to log all requests, centrally or locally - -### Clients - -An MCollective client can send requests to any number of servers, use a security plugin to encode and sign the request, and use a connector plugin to publish it. These plugins must match the security and connector used by the servers. The client can also receive replies from servers and format the response for a user or some other system. - -![Diagram of the client. The client application uses discovery plugins and agent Data Definition Language (DDL) files to construct requests, which are encoded in the security plugin and sent by the connector plugin.](./images/client.gif) - -The most common client is the `mco` command-line client, which can be used interactively or in scripts. You can also write clients in Ruby, such as backends for GUI apps or glue in a reactive infrastructure. - -Like servers, clients use several kinds of plugin: - -* A **connector plugin** to connect to the middleware and send requests (and subscribe to replies) -* A **security plugin** to validate and filter those requests (and encode replies) -* Several **agent plugin Data Definition Language (DDL) files** for constructing valid requests - -(As mentioned above, servers use **agent plugins** to execute actions. Since clients request these actions, they need to know which actions are available and what kinds of arguments they require. To do this, they use the [DDL file][ddl] from each agent they care about. These files describe actions and their inputs to enable early validation, and also tell the client how to format and summarize responses for the user.) - -Clients can also use: - -* Extra **discovery plugins** to get lists of which server nodes will respond to a request. The default discovery method uses empty MCollective messages to find nodes, and querying a central database can sometimes be faster. -* **Validator** and **aggregate** plugins to check and format data. These can be referenced by the agent DDL files. -* **Application plugins** to add custom subcommands to the `mco` command. - -### Middleware - -MCollective clients and servers don't communicate directly. They expect to be connected to some middleware system that knows how to route messages, and they publish messages to the middleware and subscribe to messages they need to receive. - -This middleware system is external to MCollective, and everything that interacts directly with it is implemented in a **connector plugin** that needs some information about the middleware's topology and configuration. As far as everything but the connector is concerned, the middleware is an abstract cloud of magic: - -![Diagram: The connector plugin interfaces with the middleware.](./images/middleware-magic.gif) - -The connector and middleware handle three main kinds of messages: - -* **Broadcast** requests (client-to-server) -* **Directed** requests (client-to-server) -* **Replies** (server-to-client) - -![Diagram of kinds of traffic handled by the middleware. Clients send broadcast requests, which get routed to many servers, and directed requests, which get routed to a single server. Servers send replies, which are intended to go only to the client that initiated the original request.](./images/middleware.gif) - -[See the middleware overview page][middleware] for info about the most common middleware options. - -## Global Configuration and Deployment Decisions - -Some MCollective configuration is global, and must match for all components. Deploying will be easier if you figure this configuration out first. - -The [standard deployment guide][standard_deploy] makes several of these decisions for you, and goes into greater detail about the rest. - -The main kinds of shared configuration are: - -* [Middleware type and connector](#middleware-type-and-connector) -* [Security plugin](#security-plugin) -* [Agent plugins](#agent-plugins) -* [Subcollectives](#subcollectives) -* [Authorization policies](#authorization-policies) - -### Middleware Type and Connector - -All servers and clients must be using the same connector plugin, and it must match the middleware type you've chosen for the deployment. - -* [Middleware Options][middleware] - -Additionally, the way your middleware is configured will usually dictate some extra settings for the connector plugin. These generally include: - -* Hostname(s) and port(s) -* Username and password -* Whether to use SSL -* SSL credentials (if using CA verification) - -### Security Plugin - -All servers and clients must be using the same security plugin. If it requires credentials for authentication, the servers will have to be configured to accept the clients' credentials (and vice-versa). Each security plugin has its own requirements re: credentials and their distribution. - -* [The SSL security plugin (recommended)][ssl_plugin] -* [The PSK security plugin (only recommended for demo environments)][psk_plugin] -* [The AES security plugin (only recommended in special cases)][aes_plugin] - -### Agent Plugins - -MCollective servers must have [agent plugins][agents] in order to do anything. Not every server needs the same set of agents, but clients need the agent DDL files for any agent from which they'll trigger actions. You need to track what agents are in use. - -### Subcollectives - -If you divide your site into [subcollectives][] for security or traffic reduction, you must globally configure the list of collectives and determine which business rules are assigned to specific nodes. - -Servers and clients need to know which collectives to which they should subscribe and send requests, and their expectations must be aligned. (If you send requests on a subcollective no one else knows about, you won't get anything interesting back.) - -The middleware needs to know about them as well, since subcollectives are implemented in the connector plugin. See [the configuration help for your specific middleware][middleware] --- either limit certain users from accessing some collectives, or prevent certain traffic from crossing inter-datacenter connections. You must configure these restrictions in the middleware rather than in MCollective. - -### Authorization Policies - -If you are using an authorization plugin like [ActionPolicy][], you'll need to configure its policies appropriately on every server. The rules for the policies must match the unique identities of the clients that will be sending requests, so the policies will be tied to your central knowledge of who and what the clients are. - -With the ActionPolicy plugin, all policy files can safely be distributed to all servers, since each one defines which servers its rules apply to based on facts and other metadata. diff --git a/website/plugin_directory/agent_file_manager.markdown b/website/plugin_directory/agent_file_manager.markdown deleted file mode 100644 index 3d479d0c..00000000 --- a/website/plugin_directory/agent_file_manager.markdown +++ /dev/null @@ -1,8 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: File Manager Agent" -toc: false ---- - -The documentation for this plugin can be found in its [GitHub Repository](https://github.com/puppetlabs/mcollective-filemgr-agent#readme) - diff --git a/website/plugin_directory/agent_iptables_junk_filter.markdown b/website/plugin_directory/agent_iptables_junk_filter.markdown deleted file mode 100644 index e076bcbe..00000000 --- a/website/plugin_directory/agent_iptables_junk_filter.markdown +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: IP Tables Junkfilter Agent" -toc: false ---- - -The documentation for this plugin can be found in its [GitHub Repository](https://github.com/puppetlabs/mcollective-iptables-agent#readme) diff --git a/website/plugin_directory/agent_metadata.markdown b/website/plugin_directory/agent_metadata.markdown deleted file mode 100644 index 4753710f..00000000 --- a/website/plugin_directory/agent_metadata.markdown +++ /dev/null @@ -1,8 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Agent Metadata" -toc: false ---- - - -This plugin has been incorporated into MCollective core as of 2.2.0. diff --git a/website/plugin_directory/agent_registration_mongodb.markdown b/website/plugin_directory/agent_registration_mongodb.markdown deleted file mode 100644 index 7555ba53..00000000 --- a/website/plugin_directory/agent_registration_mongodb.markdown +++ /dev/null @@ -1,87 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: MongoDB Registration Agent" ---- - -A plugin to store data from the [registration metadata](registration_metadata.html) plugin in an instance of MongoDB as documents per node. - -The intention is to put all the node data in a easily reusable place for web UI's or Puppet masters to be able to access a cached snapshot of your data - -A prototype system exist that lets you query this data from Puppet, the code is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/agent/registration-mongodb/puppet) and I have a [Blog Post](http://www.devco.net/archives/2010/09/18/puppet_search_engine_with_mcollective.php) that shows how it is used. - -Shortcomings ------ - - * It has no way to know when a node is not around anymore, so you need to delete old data yourself. Will make scripts available that does this based on last seen time. - -Installation ------ - -You need to have the following installed: - - * The [registration metadata](registration_metadata.html) plugin and [Registration](https://docs.puppetlabs.com/mcollective/reference/plugins/registration.html) should be set up. - * A copy of [MongoDB](http://mongodb.org/) up and running - * The [Mongo Ruby](http://www.mongodb.org/display/DOCS/Ruby+Language+Center) extension - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/agent/registration-mongodb/) - -Configuration ------ - -

-plugin.registration.mongohost = localhost
-plugin.registration.mongodb = puppet
-plugin.registration.collection = nodes
-plugin.registration.criticalage = 3600
-
- -With this setup you should start seeing documents show up in your mongo instance, you can verify like this: - -
-$ mongo
-> use puppet
-switched to db puppet
-> db.nodes.find().count()
-47
-> db.nodes.find({"fqdn": "your.box.net"})
-{ "_id" : ObjectId("4c3f7fb0dc3ecb087d000049"), "agentlist" : [
-<snip>
-
- -Discovery ------ - -As of version 2.1.0 of MCollective discovery is pluggable, the GitHub repo for this registration receiver includes -a discovery plugin that supports class, fact, identity and agent filters with full sub collective support. - -Copy the _discovery/*_ files into your client libdir and you should see them listed in the output from *mco plugin doc*: - -
-% mco plugin doc
-.
-.
-.
-Discovery Methods:
-  flatfile        Flatfile based discovery for node identities
-  mc              MCollective Broadcast based discovery
-  mongo           MongoDB based discovery for databases built using registration
-
- -The discovery plugin takes the same configuration options as above to locate the mongodb instance and you can -set it to be the default discovery method in your client.cfg: - -
-default_discovery_method = mongo
-
- -With this in place mcollective will default to discovering against this data: - -
-% mco rpc rpcutil ping -W country=fr
-Discovering hosts using the mongo method .... 9
-.
-.
-
- -You can revert to the old method of discovery by passing in *--dm mc* to the client or by using any *-S* filter. - -It understands the criticalage configuration option and will not discover nodes that have not checked in for at least that long diff --git a/website/plugin_directory/agent_registration_monitor.markdown b/website/plugin_directory/agent_registration_monitor.markdown deleted file mode 100644 index f2b7870f..00000000 --- a/website/plugin_directory/agent_registration_monitor.markdown +++ /dev/null @@ -1,34 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Agent Registration Monitor" ---- - -MCollective supports sending [registration messages](https://docs.puppetlabs.com/mcollective/reference/plugins/registration.html) at set intervals. This is an agent to receive those messages and simply write the content to a file per sender. - -It includes a Nagios check that monitors the directory with these files for any that has not checked in for some time. - -You can use this agent as well as other types of registration agents, just not more than one per server. So you can inventory your machines as well as monitor them on your Nagios server by just enabling registration on the mcollectived servers. - -Installation ------ - - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/agent/registration-monitor/). - - -Configuration ------ - -By default the plugin will create _/var/tmp/mcollective_ and create files in there per sender id. - -You can configure the directory used using the setting _plugin.registration.directory_ in the server config. - -You'd install this agent in just one of your nodes and then install the included Nagios check onto the same machine. - -The nagios check should be invoked like: - -
-check_mcollective --directory /var/tmp/mcollective --interval 300
-OK: 50 / 50 hosts checked in within 300 seconds| totalhosts=50 oldhosts=0 currenthosts=50
-
- -It includes performance data for Nagios. diff --git a/website/plugin_directory/apt.markdown b/website/plugin_directory/apt.markdown deleted file mode 100644 index a233abf6..00000000 --- a/website/plugin_directory/apt.markdown +++ /dev/null @@ -1,66 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: AgentApt" ---- - -Introduction ------------- - -An agent to handle apt/dpkg tasks such as finding the total number of upgrades available, listing the total number of packages installed, or executing an 'apt-get clean' or 'apt-get update'. - -Installation ------------- - - * The source for the plugin is [GitHub](https://github.com/mstanislav/mCollective-Agents/tree/master/apt) - -Usage ------ -### Update - -
-# mc-apt update
-Do you really want to operate on services unfiltered? (y/n): y
-server01.example.com                                : OK
-server02.example.com                                : OK
-server03.example.com                                : OK
-server04.example.com                                : OK
-server05.example.com                                : OK
-
- -### Clean - -
-# mc-apt clean
-Do you really want to operate on services unfiltered? (y/n): y
-server01.example.com                                : OK
-server02.example.com                                : OK
-server03.example.com                                : OK
-server04.example.com                                : OK
-server05.example.com                                : OK
-
- -### Installed - -
-# mc-apt installed
-Do you really want to operate on services unfiltered? (y/n): y
-server01.example.com                                : 755
-server02.example.com                                : 832
-server03.example.com                                : 744
-server04.example.com                                : 755
-server05.example.com                                : 832
-
- - - -### Upgrades - -
-# mc-apt upgrades
-Do you really want to operate on services unfiltered? (y/n): y
-server01.example.com                                : 57
-server02.example.com                                : 19
-server03.example.com                                : 0
-server04.example.com                                : 0
-server05.example.com                                : 0
-
diff --git a/website/plugin_directory/authorization_action_policy.markdown b/website/plugin_directory/authorization_action_policy.markdown deleted file mode 100644 index fd330d77..00000000 --- a/website/plugin_directory/authorization_action_policy.markdown +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Authorization Action Policy" -toc: false ---- - -The documentation for this plugin can be found in its [GitHub repository](https://github.com/puppetlabs/mcollective-actionpolicy-auth#readme). diff --git a/website/plugin_directory/central_rpc_log.markdown b/website/plugin_directory/central_rpc_log.markdown deleted file mode 100644 index 44478c71..00000000 --- a/website/plugin_directory/central_rpc_log.markdown +++ /dev/null @@ -1,88 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Central RPC Audit Log" ---- - - -This is a [SimpleRPC Audit Plugin](https://docs.puppetlabs.com/mcollective/simplerpc/auditing.html) and Agent that sends all SimpleRPC audit events to a central point for logging. - -You'd run the audit plugin on every node and designate one node as the receiver of audit logs. The receiver will have a detailed log of every SimpleRPC request processed on your entire server estate. - -There are 2 receiving agents, one that writes a log file: - -
-01/22/10 12:57:34 dev2.foo.net> b10c1a33ad5e8cfaf5f564afa9957c32: 01/22/10 12:57:34 caller=uid=500@devel.your.com agent=iptables action=block
-01/22/10 12:57:34 dev2.foo.net> b10c1a33ad5e8cfaf5f564afa9957c32: {:ipaddr=>"62.x.x.242"}
-01/22/10 12:57:34 dev1.foo.net> b10c1a33ad5e8cfaf5f564afa9957c32: 01/22/10 12:57:34 caller=uid=500@devel.your.com agent=iptables action=block
-01/22/10 12:57:34 dev2.foo.net> b10c1a33ad5e8cfaf5f564afa9957c32: {:ipaddr=>"62.x.x.242"}
-
- -The example log file is from a remote node _devel.your.com_ it is for a message with the ID _b10c1a33ad5e8cfaf5f564afa9957c32_, the caller ran as unix process id _500_. - -It sent a request to the _iptables_ agent with the action _block_ and the parameter _ipaddr = 62.x.x.242_. - -The other plugin will write to MongoDB: - -
-$ mongo
-MongoDB shell version: 1.4.4
-> use mcollective
-switched to db mcollective
-> db.rpclog.find()
-{ "_id" : ObjectId("4c5975e2dc3ecb0c3b000001"), "agent" : "nrpe", "senderid" : "monitor1.xxx.net", "requestid" : "6c311d786b2d187b231d41f14cbb03ce", "action" : "runcommand", "data" : { "command" : "check_bacula-fd", "process_results" : true }, "caller" : "cert=nagios@monitor1.xxx.net" }
-
- -There are some limitations to the design of this plugin, I suspect it will be affective to only a few 100 machines. This is due to RPC requests being used to create the audit entries. If the central host isn't fast there might be some overflow and discarding happening. - -I'd be interested in working with someone to improve this, we'd essentially write audit log entries to a Queue and have a daemon that consumes the queue, this will ensure that all logs get saved to the DB. - -Installation ------ - -### Every Node - - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/audit/centralrpclog/audit/). - - -Add to the configuration: - -
-rpcaudit = 1
-rpcauditprovider = centralrpclog
-
- -Since version _1.1.3_ of MCollective we support sub collective, you can specify which collective to use: - -
-plugin.centralrpclog.collective = audit
-
- -And restart - -### Central Audit Node - -#### File logging agent - - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/audit/centralrpclog/agent/). - - -Add to the configuration: - -
-plugin.centralrpclog.logfile = /var/log/mcollective-rpcaudit.log
-
- - * Set up log rotation for _/var/log/mcollective-rpcaudit.log_ using your Operating Systems log rotation system. - -#### MongoDB agent - - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/audit/centralrpclog/agent/). - - -Add to your configuration, these are the defaults so you can just keep it like this if its ok: - -
-plugin.centralrpclog.mongohost = localhost
-plugin.centralrpclog.mongodb = mcollective
-plugin.centralrpclog.collection = rpclog
-
diff --git a/website/plugin_directory/discovery_assisted_ssh.markdown b/website/plugin_directory/discovery_assisted_ssh.markdown deleted file mode 100644 index 4154ee81..00000000 --- a/website/plugin_directory/discovery_assisted_ssh.markdown +++ /dev/null @@ -1,22 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Discovery-Assisted SSH" -toc: false ---- - - -If you're using the discovery filters heavily, you might also want to use SSH based on these filters. We have 2 client scripts that help you do this at [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/utilities/mc-ssh). - -The first uses the [Highline](http://highline.rubyforge.org/) gem and looks something like this: - -
-% mc-ssh -W country=za -- -l root
-1. node1.your.com
-2. node2.your.com
-3. Exit
-1
-Running: ssh node1.your.com -l root
-Last login: Sat Dec 18 17:01:35 2010 from nephilim.ml.org
-
- -The second uses the [RDialog](http://rdialog.rubyforge.org/) gem to display the menu using a curses UI. diff --git a/website/plugin_directory/facter.markdown b/website/plugin_directory/facter.markdown deleted file mode 100644 index 912c4be4..00000000 --- a/website/plugin_directory/facter.markdown +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Facter " -toc: false ---- - -The documentation for this plugin can be found in its [GitHub repository](https://github.com/puppetlabs/mcollective-facter-facts#readme). diff --git a/website/plugin_directory/facter_via_yaml.markdown b/website/plugin_directory/facter_via_yaml.markdown deleted file mode 100644 index a06ae88e..00000000 --- a/website/plugin_directory/facter_via_yaml.markdown +++ /dev/null @@ -1,95 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Facter via YAML" ---- - -## Overview - - -This method of accessing facts is what we recommend for Puppet users, it's faster and deals better with some strange issues in Facter. - -Essentially we use Puppet to dump the data it gathers during normal runs into a YAML file and just use the MCollective YAML fact source to read this data. This avoids an extreme slowdown from Facter running on every mcollective invocation, plus it lets you get any in-scope variables (for example, parameters from your external node classifier) available as mcollective filters for free. - -## Creating the YAML - -In your Puppet manifests create a class similar to below: (Thanks to Dave Ta for this recipe, and to Jesse Throwe for the Ruby 1.9 fix) - -
-class mcollective::facts ()
-{
-  #The facts.yaml file resource is generated in its own dedicated class
-  #By doing this, the file produced isn't polluted with unwanted in scope class variables.
-
-  ##Bring in as many variables as you want from other classes here.
-  #This makes them available to mcollective for use in filters.
-  #eg
-  #$class_variable = $class::variable
-
-  #mcollective doesn't work with arrays, so use the puppet-stdlib join function
-  #eg
-  #$ntp_servers = join($ntp::servers, ",")
-
-  file{'/etc/mcollective/facts.yaml':
-   owner    => root,
-   group    => root,
-   mode     => 400,
-   content => template('mcollective/facts.yaml.erb'),
-  }
-}
-
- - -### facts.yaml.erb -
-<%=
-    # remove dynamic facts
-    obj = scope.to_hash.reject {|k,v| k.to_s =~ /^(uptime.*|rubysitedir|_timestamp|memoryfree.*|swapfree.*|title|name|caller_module_name|module_name)$/ }
-
-    arr = obj.sort
-    out = "---\n"
-    arr.each do |element|
-      entry = {element[0] => element[1]}
-      out += entry.to_yaml.split(/\n/)[1..-1].join("\n") + "\n"
-    end
-
-    out
-%>
-
- -Apply this to all the nodes that run MCollective. - -## Configure MCollective - -Add the following lines to server.cfg - -
-factsource = yaml
-plugin.yaml = /etc/mcollective/facts.yaml
-
- -## Verify - -
-% mc-inventory some.node
-Inventory for some.node:
-
-   Server Statistics:
-                      Version: 0.4.10
-                   Start Time: Mon Nov 29 16:38:28 +0000 2010
-                  Config File: /etc/mcollective/server.cfg
-                   Process ID: 5387
-               Total Messages: 9254
-      Messages Passed Filters: 6443
-            Messages Filtered: 2811
-                 Replies Sent: 6442
-         Total Processor Time: 23.27 seconds
-                  System Time: 6.19 seconds
-
-
-
-   Facts:
-      architecture => i386
-      clientcert => some.node
-      clientversion => 2.6.3
-      country => de
-
diff --git a/website/plugin_directory/index.markdown b/website/plugin_directory/index.markdown deleted file mode 100644 index e34efc2b..00000000 --- a/website/plugin_directory/index.markdown +++ /dev/null @@ -1,68 +0,0 @@ ---- -layout: default -title: "MCollective Plugin Directory" ---- - -This directory of MCollective plugins was migrated from the Puppet Labs wiki in January, 2015. - -## Agents - - * [Apt](apt.html) - Perform various tasks for apt/dpkg - * [File Manager](agent_file_manager.html) - create, touch, remove and retrieve information about files - * [IP Tables Junkfilter Manager](agent_iptables_junk_filter.html) - Add, removes and queries rules on a specific chain - * [Net Test](net_test.html) - Performs network reachability testing - * [NRPE](nrpe_agent.html) - Runs NRPE commands using MCollective as transport - * [Process](process_management.html) - Manage server processes - * [Puppet](puppet_agent.html) - enable, disable, run puppet daemons. - * [Puppet CA](puppet_ca.html) - Manage the Puppet Certificate Authority - * [Package](package.html) - installs, uninstalls and query Operating System packages - * [Packages](packages.html) - install, update, uninstall multiple packages in one run with fine version/revision control - * [Service](services.html) - stop, starts and query Operating System services - * [Spam Assassin](spamassassin.html) - Perform various tasks for Spam Assassin - * [Stomp Utilities](stomp_util.html) - helpers and utilities for the STOMP connector - -## Fact Sources - - - * [Facter via YAML](facter_via_yaml.html) - Access Facter variables as YAML - * [Facter](facter.html) - Use Puppet Labs Facter as a fact source - * [Ohai](ohai.html) - Use OpsCode Ohai as a fact source - -## Auditing - - - * [Central RPC Log](central_rpc_log.html) - Logs RPC audit logs to a central log file or MongoDB instance - * [Central LogStash log](logstash_rpc_audit_logs.html) - Logs RPC audit logs to a central [LogStash](http://code.google.com/p/logstash/) instance - -## Authorization - - * [Action Policy](authorization_action_policy.html) - Authorization plugin with fine grain per action ACLs - -## Data - - * [Puppet Resource Status](resources_data_plugin.html) - Datasource to facilitate discovery of machines based on the state of Puppet resources - * [Sysctl Value](sysctl_data.html) - Datasource to retrieve values from any Linux sysctl - * [Agent Meta Data](agent_metadata.html) - Datasource to retrieve meta data about currently installed agents for nodes - -## Discovery - - - * [Registration Data in MongoDB](agent_registration_mongodb.html) - Discover against registration data in a MongoDB NoSQL server - -## Security - - - * [None](none.html) - A plugin for development that provides no security - -## Registration - - - * [Meta Data](agent_metadata.html) - Sends agents, facts and classes lists to registration agents - * [Registration Monitor](agent_registration_monitor.html) - Writes registration data to file and a Nagios check - * [Registration Data in MongoDB](agent_registration_mongodb.html) - Writes registration data to a MongoDB NoSQL server - -## Tools - - - * [Puppet Commander](puppet_commander.html) - schedule puppet runs on a group of hosts with enforced concurrency - * [SSH](discovery_assisted_ssh.html) - Discovery assisted ssh diff --git a/website/plugin_directory/logstash_rpc_audit_logs.markdown b/website/plugin_directory/logstash_rpc_audit_logs.markdown deleted file mode 100644 index c24e8238..00000000 --- a/website/plugin_directory/logstash_rpc_audit_logs.markdown +++ /dev/null @@ -1,8 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Logstash RPC Audit Logs" -toc: false ---- - -The documentation for this plugin can be found in its [GitHub repository](https://github.com/puppetlabs/mcollective-logstash-audit#readme). - diff --git a/website/plugin_directory/net_test.markdown b/website/plugin_directory/net_test.markdown deleted file mode 100644 index 3118ef70..00000000 --- a/website/plugin_directory/net_test.markdown +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Net Test Agent" -toc: false ---- - -The documentation for this plugin can be found in its [GitHub Repository](https://github.com/puppetlabs/mcollective-nettest-agent#readme) diff --git a/website/plugin_directory/none.markdown b/website/plugin_directory/none.markdown deleted file mode 100644 index 03bb11a9..00000000 --- a/website/plugin_directory/none.markdown +++ /dev/null @@ -1,23 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: No Security" -toc: false ---- - -This plugin is very easy to use as it provides no security there are no certificates or pre shared keys to setup. The intention is to use it in development and testing but **NOT IN PRODUCTION** - -Installation ------ - - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/security/none/). - - -Configuration ------ - -Set the following in both server and client config files: - -
-securityprovider = none
-
- diff --git a/website/plugin_directory/nrpe_agent.markdown b/website/plugin_directory/nrpe_agent.markdown deleted file mode 100644 index cd6227f3..00000000 --- a/website/plugin_directory/nrpe_agent.markdown +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: NRPE Agent" -toc: false ---- - -The documentation for this plugin can be found in its [GitHub Repository](https://github.com/puppetlabs/mcollective-nrpe-agent#readme) diff --git a/website/plugin_directory/ohai.markdown b/website/plugin_directory/ohai.markdown deleted file mode 100644 index f56588a1..00000000 --- a/website/plugin_directory/ohai.markdown +++ /dev/null @@ -1,52 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Ohai" ---- - - -## Overview - -The Ohai plugin enables mcollective to use [OpsCode Ohai](http://wiki.opscode.com/display/chef/Ohai) as a source for facts about your system. - -This plugin uses a 3000 second cache for facts, after that it will reset Ohai and regenerate all the facts, this adds a few seconds or so overhead to discovery. - -This plugin is released as Apache License v2 same as the license of Ohai. - -## Installation - - -If you are using MCollective 1.1.0 or newer you need the file `opscodeohai_facts.rb`. Otherwise, use `opscodeohai.rb`. - - * The source for the plugin is [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/facts/ohai/) - -## Configuration - - -You can set the following config options in the server.cfg - - * You have to set _factsource = opscodeohai_ in _server.cfg_ to tell it to load this plugin - -If you have MCollective 1.0.x and older: - - * plugin.facter.cache_time - how long to cache for, defaults to 300 - -If you have Mcollective 1.1.x and newer: - - * fact\_cache\_time - how long to cache for, defaults to 300 - - -## Usage - -You should now be able to do use all your ohai facts in discovery and fact reporting. - -
-$ mc-facts platform_version
-Report for fact: platform_version
-
-        5.3                                     found 3 times
-        5.4                                     found 10 times
-
-Finished processing 13 hosts in 5007.51 ms
-
- -You can also use all these facts in your usual discovery lookups etc. diff --git a/website/plugin_directory/package.markdown b/website/plugin_directory/package.markdown deleted file mode 100644 index cdba7eca..00000000 --- a/website/plugin_directory/package.markdown +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Package Agent" -toc: false ---- - -The documentation for this plugin can be found in its [GitHub Repository](https://github.com/puppetlabs/mcollective-package-agent#readme) diff --git a/website/plugin_directory/packages.markdown b/website/plugin_directory/packages.markdown deleted file mode 100644 index a69b98a8..00000000 --- a/website/plugin_directory/packages.markdown +++ /dev/null @@ -1,63 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Packages Agent" ---- - -Packages agent for MCollective - a agent to install/upgrade/downgrade -multiple packages at a time. - -When using MCollective to roll-out changes in a Continuous Delivery -environment, the included has some shortcomings, the main one being -that it does not support multiple packages in one run, which adds -overhead. - -Packages agent tackles the following requirements -1. The client does not know wether a package is already installed or not. -1. Allow install, update and downgrade. -1. Handle multiple packages in one operation. -1. Respond with a list of packages and their exact version/revision installed. -1. Re-try operations when they fail. -1. Include "yum clean expire-cache" - -Limitations ------ - -Mainly tested on Scientific Linux 6.1. - -Installation ------ - -The source is on [GitHub](https://github.com/jbraeuer/mcollective-plugins/tree/packages/agent/packages/) - -Usage ------ - -Install multiple packages at a time: - -
-% mco packages uptodate htop iotop
-Do you really want to operate on packages unfiltered? (y/n): y
-
- * [ ============================================================> ] 5 / 5
-
-ip-10-56-51-48                           = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-ip-10-56-5-253                           = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-ip-10-250-141-198                        = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-ip-10-56-46-219                          = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-ip-10-250-126-24                         = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-
-
- -Request installation of specific version (and disable y/n for automated deployments): - -
-# mco packages --batch uptodate htop/0.8.3/2.el6 iotop/0.3.2/3.el6
-
- * [ ============================================================> ] 5 / 5
-
-ip-10-56-46-219                          = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-ip-10-56-5-253                           = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-ip-10-250-126-24                         = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-ip-10-56-51-48                           = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-ip-10-250-141-198                        = OK ::: [{"name":"htop","tries":1,"version":"0.8.3","status":0,"release":"2.el6"},{"name":"iotop","tries":1,"version":"0.3.2","status":0,"release":"3.el6"}] :::
-
diff --git a/website/plugin_directory/process_management.markdown b/website/plugin_directory/process_management.markdown deleted file mode 100644 index ac9bf22b..00000000 --- a/website/plugin_directory/process_management.markdown +++ /dev/null @@ -1,102 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Process Management Agent" ---- - -An agent that can be used for process management like the Unix _pgrep_, _kill_ and _pkill_ - -**WARNING:** You should use the _kill_ and _pkill_ actions with extreme caution, you can do extensive damage to the availability of your infrastructure using these. - -Installation ------ - - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/agent/process/) - * You need to have the [sys-proctable](http://raa.ruby-lang.org/project/sys-proctable/) Gem installed - -Usage ------ - -There is a bundled _pgrep_ utility: - -
-% mc-pgrep ruby
-
- * [ ============================================================> ] 48 / 48
-
-node1.your.com
-32470 root        92.805MB  ruby /usr/sbin/mcollectived --pid=/var/run/mcollectived.pid
-
-node2.your.com
- 1997 root        40.539MB  /usr/bin/ruby /usr/sbin/puppetd --onetime
-12316 root        25.676MB  ruby /usr/sbin/mcollectived --pid=/var/run/mcollectived.pid
-
-<snip>
-
-   ---- process list stats ----
-        Matched hosts: 48
-    Matched processes: 111
-        Resident Size: 771.502MB
-         Virtual Size: 9.318GB
-
- -The process agent can return vast amounts of information, here is init on one machine: - -
-    {:pslist=>
-      [{:startcode=>134512640,
-        :kstkeip=>12850178,
-        :root=>"/",
-        :euid=>0,
-        :blocked=>0,
-        :cminflt=>3341998871,
-        :cutime=>6433066,
-        :itrealvalue=>0,
-        :tty_nr=>0,
-        :majflt=>20,
-        :wchan=>0,
-        :utime=>6,
-        :name=>"init",
-        :uid=>0,
-        :egid=>0,
-        :cmajflt=>51118,
-        :signal=>0,
-        :cmdline=>"init [3]",
-        :ppid=>0,
-        :cstime=>3222029,
-        :fd=>{"10"=>"/dev/initctl"},
-        :sigignore=>1475401980,
-        :cwd=>"/",
-        :gid=>0,
-        :vsize=>2224128,
-        :processor=>0,
-        :environ=>{"TERM"=>"linux", "HOME"=>"/"},
-        :pid=>1,
-        :nswap=>0,
-        :rt_priority=>0,
-        :flags=>4194560,
-        :nice=>0,
-        :cnswap=>0,
-        :comm=>"init",
-        :state=>"S",
-        :endcode=>134544728,
-        :username=>"root",
-        :rss=>172,
-        :startstack=>3216723968,
-        :exe=>"/sbin/init",
-        :rlim=>4294967295,
-        :starttime=>63,
-        :policy=>0,
-        :tpgid=>-1,
-        :exit_signal=>0,
-        :pgrp=>1,
-        :minflt=>699,
-        :stime=>29,
-        :priority=>15,
-        :session=>1,
-        :kstkesp=>3216722652,
-        :sigcatch=>671819267}]},
-
- -For this reason you need to be careful about large pgrep's over large infrastructures the resulting data that needs to be transfered and processed on your machine can be staggering, each node will return as much as half a MB of serialized data. - -The kill and pkill commands are not documented here and is in general not recommended to use. diff --git a/website/plugin_directory/puppet_agent.markdown b/website/plugin_directory/puppet_agent.markdown deleted file mode 100644 index b9f799e5..00000000 --- a/website/plugin_directory/puppet_agent.markdown +++ /dev/null @@ -1,9 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: AgentPuppet" -toc: false ---- - -This is a rewrite of the older Puppetd agent which supports many new features and Puppet 3. - -The documentation for this plugin is on [GitHub](https://github.com/puppetlabs/mcollective-puppet-agent#readme) diff --git a/website/plugin_directory/puppet_ca.markdown b/website/plugin_directory/puppet_ca.markdown deleted file mode 100644 index 6e7b3486..00000000 --- a/website/plugin_directory/puppet_ca.markdown +++ /dev/null @@ -1,94 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Puppet CA Agent" ---- - -This agent lets you sign, list, revoke and clean certificates on your Puppet Certificate Authorities - -Installation ------ - - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/agent/puppetca/) - - -Usage ------ - -The commands available are shown below: - -### List: - -
-% mco rpc puppetca list
-Determining the amount of hosts matching filter for 2 seconds .... 2
-
- * [ ============================================================> ] 2 / 2
-
-
-puppet1.your.net:
-         Signed:
-           ["host1.your.net",
-            "host2.your.net"]
-         Waiting CSRs:
-           ["host3.your.net"]
-
-puppet2.your.net:
-         Signed:
-           ["host4.your.net",
-            "host5.your.net"]
-         Waiting CSRs:
-           []
-
- -### Sign: - -
-% mco rpc puppetca sign certname=host3.your.net
-Determining the amount of hosts matching filter for 2 seconds .... 2
-
- * [ ============================================================> ] 2 / 2
-
-
-puppet2.your.net                  Request Aborted
-   No cert found to sign
-
-puppet1.your.net
-   Result: notice: Signed certificate request for host3.your.net
-           notice: Removing file Puppet::SSL::CertificateRequest host3.your.net at '/var/lib/puppet/ssl/ca/requests/host3.your.net.pem'
-
-
-Finished processing 2 / 2 hosts in 1207.45 ms
-
- -### Revoke: - -Note how puppetca doesn't behave too well when you ask it to revoke a certificate that doesn't exist, doesn't cause problems though - -
-% mco rpc puppetca revoke certname=host3.your.net
-Determining the amount of hosts matching filter for 2 seconds .... 2
-
- * [ ============================================================> ] 2 / 2
-
-
-puppet1.your.net
-   Result: notice: Revoked certificate with serial 156
-
-puppet2.your.net
-   Result: notice: Revoked certificate with serial # Inventory of signed certificates
-
- -### Clean - -
-% mco rpc puppetca clean certname=host3.your.net
-Determining the amount of hosts matching filter for 2 seconds .... 2
-
- * [ ============================================================> ] 2 / 2
-
-
-monitor3.your.net
-   Result: Removed signed cert: /var/lib/puppet/ssl/ca/signed/host3.your.net.pem.
-
-
-Finished processing 2 / 2 hosts in 355.97 ms
diff --git a/website/plugin_directory/puppet_commander.markdown b/website/plugin_directory/puppet_commander.markdown
deleted file mode 100644
index cdd3072b..00000000
--- a/website/plugin_directory/puppet_commander.markdown
+++ /dev/null
@@ -1,50 +0,0 @@
----
-layout: default
-title: "MCollective Plugin: Puppet Commander"
----
-
-The Puppet Commander solves the problem where at times even when using splay you will find a large amount of checkins hitting your master and overwhelming it.
-
-The basic theory is that using the [Puppet Agent plugin](puppet_agent.html) we can figure out how many machines are currently running and we can schedule runs.  We can thus ensure that at any given time we only schedule a certain amount of current runs.  This will help you with capacity planning of your masters.
-
-As a side effect it also means if you are busy managing servers and running _puppetd --test_ runs or some other scheduled runs the commander will back down and not schedule runs, leaving the resources of the master free for your interactive use.
-
-Installation
-------------
-
- * You need to have [Puppet Agent](puppet_agent.html) installed and working.
- * You should not be running the Puppet Daemons, shut those services down.
- * Get the code from [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/agent/puppetd/commander/).
- * Place the _puppetcommanderd_ script in _/usr/sbin_.
- * Place the _puppetcommander.init_ in your rc directory, often that is _/etc/init.d/puppetcommander_ and enable it.
- * Create _/etc/sysconfig/puppetcommanderd_ and set any settings like those for security plugins or MCOLLECTIVE_EXTRA_OPTS.
- * Create _/etc/puppetcommander.cfg_ from the provided template.
-
-Configuration
--------------
-
-A sample config file can be seen below:
-
-
----
-:filter: "country=/de|uk|za/"
-:interval: 30
-:concurrency: 2
-:randomize: true
-:logfile: /var/log/puppetcommander.log
-:daemonize: true
-
- -It runs my nodes in _de_, _uk_ and _za_ in 30 minutes, never more than 2 at a time. It will shuffle the nodes it discovered and log to the given log file. - -The filter option above is quite limiting, from MCollective 1.1.0 and newer to supply filters do the following in _/etc/sysconfig/puppetcommanderd_, this lets you supply a much richer set of filters than before. - -
-export MCOLLECTIVE_EXTRA_OPTS="-W country=/de|uk|za/"
-
- -And set the filter in the YAML above to: - -
-:filter: ""
-
diff --git a/website/plugin_directory/registration_metadata.markdown b/website/plugin_directory/registration_metadata.markdown deleted file mode 100644 index 6a3e6f0f..00000000 --- a/website/plugin_directory/registration_metadata.markdown +++ /dev/null @@ -1,30 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Registration Metadata" ---- - -Sends all available metadata via the registration mechanism, at present this is: - - * All facts - * All agents - * List of all classes - -Installation ------ - - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/registration/). - - -Configuration ------ - -For full details about the registration system see [Registration](https://docs.puppetlabs.com/mcollective/reference/plugins/registration.html) - -The simplest configuration for this plugin is: - -
-registerinterval = 300
-registration = Meta
-
- -You should also set up a receiver, such as [agent registration monitor](agent_registration_monitor.html) or [agent registration for MongoDB](agent_registration_mongodb.html). diff --git a/website/plugin_directory/resources_data_plugin.markdown b/website/plugin_directory/resources_data_plugin.markdown deleted file mode 100644 index ff069fb2..00000000 --- a/website/plugin_directory/resources_data_plugin.markdown +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Resources Data" -toc: false ---- - -This plugin has been incorporated into the new Puppet agent, which can be found on [GitHub](https://github.com/puppetlabs/mcollective-puppet-agent#readme). diff --git a/website/plugin_directory/services.markdown b/website/plugin_directory/services.markdown deleted file mode 100644 index 8dc143e1..00000000 --- a/website/plugin_directory/services.markdown +++ /dev/null @@ -1,7 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Services Agent" -toc: false ---- - -The documentation for this plugin can be found in its [GitHub Repository](https://github.com/puppetlabs/mcollective-service-agent#readme) diff --git a/website/plugin_directory/spamassassin.markdown b/website/plugin_directory/spamassassin.markdown deleted file mode 100644 index 9cf33b0a..00000000 --- a/website/plugin_directory/spamassassin.markdown +++ /dev/null @@ -1,105 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: SpamAssassin" ---- - - -Introduction ------------- - -An agent to handle Spam Assassin tasks such as compiling a ruleset, restarting the service, viewing the service status + compiled ruleset modified timestamp, checking for ruleset syntax errors, and executing an sa-update. Alternatively, the 'full' command will execute an update, compilation, and restart all at once. - -Installation ------------- - -The source for the plugin is [GitHub](https://github.com/mstanislav/mCollective-Agents/tree/master/spamassassin) - - -Configuration -------------- - -Below are the currently available plugin configuration options: - -
-plugin.spamassassin.compiled_ruleset = /var/lib/spamassassin/compiled/5.008/3.002005/Mail/SpamAssassin/CompiledRegexps/body_0.pm
-
- -Usage ------ -### Status - -
-# mc-spamassassin -W "purpose=asav" status
-asav01.example.com                      RUNNING, COMPILED RULESET MON JAN 17 15:14:03 -0600 2011
-asav02.example.com                      RUNNING, COMPILED RULESET MON JAN 17 15:18:28 -0600 2011
-asav03.example.com                	    RUNNING, COMPILED RULESET MON JAN 17 15:20:53 -0600 2011
-asav04.example.com                	    RUNNING, COMPILED RULESET MON JAN 17 15:27:06 -0600 2011
-asav05.example.com                	    RUNNING, COMPILED RULESET MON JAN 17 15:24:07 -0600 2011
-
-Finished processing 5 / 5 hosts in 380.58 ms
-
- -### Update - -
-# mc-spamassassin -W "purpose=asav" update
-asav02.example.com                       NO UPDATES FOUND
-asav03.example.com                       NO UPDATES FOUND
-asav05.example.com                	     NO UPDATES FOUND
-asav04.example.com                	     NO UPDATES FOUND
-asav01.example.com                       NO UPDATES FOUND
-
-Finished processing 5 / 5 hosts in 956.66 ms
-
- -### Compile - -
-# mc-spamassassin -W "purpose=asav" compile
-asav05.example.com                	     OK
-asav03.example.com                       OK
-asav02.example.com                       OK
-asav04.example.com                	     OK
-asav01.example.com                       OK
-
-Finished processing 5 / 5 hosts in 61450.94 ms
-
- -### Lint - -
-# mc-spamassassin -W "purpose=asav" lint
-asav05.example.com                	     3 SYNTAX ERRORS
-asav02.example.com                       SYNTAX OK
-asav04.example.com                	     2 SYNTAX ERRORS
-asav03.example.com                       SYNTAX OK
-asav01.example.com                       SYNTAX OK
-
-Finished processing 5 / 5 hosts in 5004.15 ms
-
- -### Restart - -
-# mc-spamassassin -W "purpose=asav" restart
-asav05.example.com                	     OK
-asav04.example.com                	     OK
-asav02.example.com                       OK
-asav03.example.com                       OK
-asav01.example.com                       OK
-
-Finished processing 5 / 5 hosts in 5134.55 ms
-
- -### Full - -
-# mc-spamassassin -W "purpose=asav" full
-asav05.example.com                	     NO UPDATES FOUND, COMPILATION OK, RESTART OK
-asav02.example.com                       NO UPDATES FOUND, COMPILATION OK, RESTART OK
-asav04.example.com                	     NO UPDATES FOUND, COMPILATION OK, RESTART OK
-asav03.example.com                       NO UPDATES FOUND, COMPILATION OK, RESTART OK
-asav01.example.com                       NO UPDATES FOUND, COMPILATION OK, RESTART OK
-
-Finished processing 5 / 5 hosts in 66339.50 ms
-
diff --git a/website/plugin_directory/stomp_util.markdown b/website/plugin_directory/stomp_util.markdown deleted file mode 100644 index 09230fc9..00000000 --- a/website/plugin_directory/stomp_util.markdown +++ /dev/null @@ -1,79 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: STOMP Utilities" ---- - -*NOTE:* This plugin will be removed when MCollective 2.2.x comes to an end due to the deprecation of the STOMP connector. - -Helpers and utilities for the MCollective STOMP connector - -Installation ------ - -The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/agent/stomputil/) - -Usage ------ - -### Connection Information - -The idea is that if you have a network with failover STOMP servers you might need some visibility about what is connected where, this agent and bundled utility will help you with that. - -
-% mco rpc stomputil peer_info
-Determining the amount of hosts matching filter for 2 seconds .... 1
-
- * [ ============================================================> ] 1 / 1
-
-
-node1.your.net
-       Host: stomp1.your.net
-    Address: 192.168.1.10
-   Protocol: AF_INET
-       Port: 6163
-
-Finished processing 1 / 1 hosts in 71.25 ms
-
- -You can also view all the nodes using the peer map utility. - -
-$ mc-peermap country
-stomp1.your.net -+ 22 nodes with 16.08ms average ping [de]
-                 |-node1.your.net
-                 <snip>
-
-stomp3.your.net -+ 19 nodes with 123.07ms average ping [uk]
-                 |-node10.your.net
-                 <snip>
-
-
-stomp2.your.net -+ 7 nodes with 363.30ms average ping [us]
-                 |-node20.your.net
-                 <snip>
-
-
- -Notice that I specified _country_ on the command line this causes the fact country for each STOMP server to be displayed in the output. - -### Reconnect: - -If you determined with the command above that you have nodes you'd rather reconnect to their primary STOMP server use this to disconnect and reconnect to the middleware, recreating all subscriptions and reloading all agents. - -**NOTE:** You do not want to run this against all your machines at once, take them in batches. - -
-% mco rpc -I your.node.com stomputil reconnect
-Determining the amount of hosts matching filter for 2 seconds .... 1
-
- * [ ============================================================> ] 1 / 1
-
-
-nod1.your.net
-   Restarted: 1
-
-
-Finished processing 1 / 1 hosts in 591.50 ms
-
- - diff --git a/website/plugin_directory/sysctl_data.markdown b/website/plugin_directory/sysctl_data.markdown deleted file mode 100644 index 876b5587..00000000 --- a/website/plugin_directory/sysctl_data.markdown +++ /dev/null @@ -1,21 +0,0 @@ ---- -layout: default -title: "MCollective Plugin: Sysctl Data" -toc: false ---- - - -This plugin can retrive a value from a Linux sysctl variable to be used in agents and discovery. - -Sample usage to select all machines where ipv4 forwarding is enabled: - -
-$ mco find -S "sysctl('net.ipv4.conf.all.forwarding').value=1"
-
- - -Installation ------- - - * The source is on [GitHub](https://github.com/puppetlabs/mcollective-plugins/tree/master/data/sysctl/). - diff --git a/website/reference/basic/basic_agent_and_client.md b/website/reference/basic/basic_agent_and_client.md deleted file mode 100644 index 4698a9ba..00000000 --- a/website/reference/basic/basic_agent_and_client.md +++ /dev/null @@ -1,262 +0,0 @@ ---- -layout: default -title: Basic Agents and Clients ---- -[SimpleRPCIntroduction]: /mcollective/simplerpc/ - -## Deprecation Warning - -You must use [SimpleRPC][SimpleRPCIntroduction] for writing agents and clients. SimpleRPC is a framework that wraps the core MCollective client and agent structures in a lot of helpful convention and tools. - -**The approach to developing agents and clients documented below is not supported post version 2.0.0 and the functionality will be removed for the next major release.** - -## Overview - -Writing agents for mcollective is simple, we'll write a simple _echo_ agent as well as a cli tool to communicate with it that supports discovery, filtering and more. - -The agent will send back everything that got sent to it, not overly useful but enough to demonstrate the concepts. - -## The Agent - -Agents go into a directory configured in the _server.cfg_ using the _libdir_ directive. You should have _mcollective/agent_ directory under that, restart the daemon when you've put it there. - -Create a file echo.rb with the following, I'll walk through each part: - -{% highlight ruby linenos %} -module MCollective - module Agent - # Simple echo agent - class Echo - attr_reader :timeout, :meta - - def initialize - @timeout = 5 - @meta = {:license => "Apache License, Version 2", - :author => "R.I.Pienaar ", - :version => "1.0"} - end - - def handlemsg(msg, stomp) - msg[:body] - end - - def help - <<-EOH - Echo Agent - ========== - - Simple agent that just sends the body of any request back - EOH - end - end - end -end -{% endhighlight %} - -Once you have it running you can test your agent works as below, we'll send the word _hello_ to the agent and we'll see if we get it back: - -{% highlight console %} -% /usr/sbin/mc-call-agent --config etc/client.cfg --agent echo --arg hello -Determining the amount of hosts matching filter for 2 seconds .... 1 - -devel.your.com> -"hello" - ----- stomp call summary ---- - Nodes: 1 / 1 - Start Time: Tue Nov 03 23:18:40 +0000 2009 - Discovery Time: 2001.65ms - Agent Time: 44.17ms - Total Time: 2045.82ms -{% endhighlight %} - - -### Agent name -Each agent should be wrapped in a module and class as below, this will create an agent called _echo_ and should live in a file called _agents/echo.rb_. - -{% highlight ruby %} -module MCollective - module Agent - class Echo - end - end -end -{% endhighlight %} - -### Initialization -Every agent needs to specify a timeout and meta data. The timeout gets used by the app server to kill off agents that is taking too long to finish. - -Meta data contains some information about the licence, author and version of the agent. Right now the information is free-form but I suggest supplying at the very least the details below as we'll start enforcing the existence of it in future. - -{% highlight ruby %} - attr_reader :timeout, :meta - - def initialize - @timeout = 1 - @meta = {:license => "Apache License, Version 2", - :author => "R.I.Pienaar ", - :version => "1.0"} - end -{% endhighlight %} - -### Handling incoming messages -You do not need to be concerned with filtering, authentication, authorization etc when writing agents - the app server takes care of all of that for you. - -Whenever a message for your agent pass all the checks it will be passed to you via the _handlemsg_ method. - -{% highlight ruby %} - def handlemsg(msg, stomp) - msg[:body] - end -{% endhighlight %} - -The msg will be a hash with several keys giving you information about sender, hashes, time it was sent and so forth, in our case we just want to know about the body and we're sending it right back as a return value. - -### Online Help -We keep help information, not used right now but future version of the code will have a simple way to get help for each agent, the intent is that inputs, outputs and behavior to all agents would be described in this. - -{% highlight ruby %} - def help - <<-EOH - Echo Agent - ========== - - Simple agent that just sends the body of any request back - EOH - end -{% endhighlight %} - -## More complex agents -As you write more complex agents and clients you might find the need to have a few separate files make up your agent, you can drop these files into a directory named _util_ under the plugins (that is, at the same level of the agent folder, so as _/usr/libexec/mcollective/mcollective/util_ at the time of writing). - -Create the _util_ folder if needed. - -The classes should be _MCollective::Util::Yourclass_ and you should use the following construct to require them from disk: - -{% highlight ruby %} -MCollective::Util.loadclass("MCollective::Util::Yourclass") -{% endhighlight %} - -Create _util/yourclass.rb_ with this content : - -{% highlight ruby %} -module MCollective - module Util - class Yourclass - # The class definition here - end - end -end -{% endhighlight %} - -_loadclass_ on _Yourclass_ will automatically search for a _yourclass.rb_ file (lowercase). - -At present simply requiring them will work and we'll hope to maintain that but to be 100% future safe use this method. - - -It also loads modules in exactly the same way. - -## The Client -We provide a client library that abstracts away a lot of the work in writing simple attractive cli frontends to your agents that supports discovery, filtering, generates help etc. The client lib is functional but we will improve/refactor the options parsing a bit in future. - -{% highlight ruby linenos %} -#!/usr/bin/ruby - -require 'mcollective' - -oparser = MCollective::Optionparser.new({}, "filter") - -options = oparser.parse{|parser, options| - parser.define_head "Tester for the echo agent" - parser.banner = "Usage: mc-echo [options] msg" -} - -if ARGV.length > 0 - msg = ARGV.shift -else - puts("Please specify a message to send") - exit 1 -end - -begin - options[:filter]["agent"] = "echo" - - client = MCollective::Client.new(options[:config]) - - stats = client.discovered_req(msg, "echo", options) do |resp| - next if resp == nil - - printf("%30s> %s\n", resp[:senderid], resp[:body]) - end -rescue Exception => e - puts("Failed to contact any agents: #{e}") - exit 1 -end - -client.display_stats(stats, options) -{% endhighlight %} - -We can test it works as below: - -{% highlight console %} -% ./mc-echo --config etc/client.cfg "hello world" -Determining the amount of hosts matching filter for 2 seconds .... 1 - - devel.your.com> hello world - -Finished processing 1 / 1 hosts in 45.02 ms -{% endhighlight %} - -Verbose statistics: - -{% highlight console %} -% ./mc-echo --config etc/client.cfg "hello world" -v -Determining the amount of hosts matching filter for 2 seconds .... 1 - - devel.your.com> hello world - ----- stomp call summary ---- - Nodes: 1 / 1 - Start Time: Tue Nov 03 23:28:34 +0000 2009 - Discovery Time: 2002.27ms - Agent Time: 45.84ms - Total Time: 2048.11ms -{% endhighlight %} - -Discovery and filtering: - -{% highlight console %} -% ./mc-echo --config etc/client.cfg "hello world" --with-fact country=au - -Failed to contact any agents: No matching clients found - -% ./mc-echo --config etc/client.cfg "hello world" --with-fact country=uk -Determining the amount of hosts matching filter for 2 seconds .... 1 - - devel.your.com> hello world - -Finished processing 1 / 1 hosts in 38.77 ms -{% endhighlight %} - -Standard Help: - -{% highlight console %} -% ./mc-echo --help -Usage: mc-echo [options] msg -Tester for the echo agent - -Common Options - -c, --config FILE Load configuratuion from file rather than default - --dt SECONDS Timeout for doing discovery - --discovery-timeout - -t, --timeout SECONDS Timeout for calling remote agents - -q, --quiet Do not be verbose - -v, --verbose Be verbose - -h, --help Display this screen - -Host Filters - --wf, --with-fact fact=val Match hosts with a certain fact - --wc, --with-class CLASS Match hosts with a certain configuration management class - --wa, --with-agent AGENT Match hosts with a certain agent - --wi, --with-identity IDENT Match hosts with a certain configured identity -{% endhighlight %} diff --git a/website/reference/basic/basic_cli_usage.md b/website/reference/basic/basic_cli_usage.md deleted file mode 100644 index fec90466..00000000 --- a/website/reference/basic/basic_cli_usage.md +++ /dev/null @@ -1,531 +0,0 @@ ---- -layout: default -title: Using MCollective Command Line Applications ---- - -MCollective is designed first and foremost for the CLI. You will mostly -interact with a single executable called *mco* which has a number of -sub-commands, arguments and flags. - -> **Note:** In [Puppet Enterprise](https://docs.puppet.com/pe/), the `peadmin` user is the *only* user -who can execute `mco` commands. To execute commands as the `peadmin` user, see -[the Puppet Enterprise documentation](https://docs.puppet.com/pe/latest/orchestration_invoke_cli.html#logging-in-as-peadmin). - -## Basic Usage of the *mco* Command - -A simple example of a *mco* command can be seen below: - -{% highlight console %} -% mco ping -dev8 time=126.19 ms -dev6 time=132.79 ms -dev10 time=133.57 ms -. -. - ----- ping statistics ---- -25 replies max: 305.58 min: 57.50 avg: 113.16 -{% endhighlight %} - -In this example the *ping* sub-command is referred to as an -*application*. Mcollective provides many applications, for a list of -them, type *mco help*. You can also create your own application to plug -into the framework. The *help* sub-command will show you something like -this: - - -{% highlight console %} -% mco help -The Marionette Collective version 2.0.0 - - controller Control the mcollective daemon - facts Reports on usage for a specific fact - find Find hosts matching criteria - help Application list and help - inventory General reporting tool for nodes, collectives and subcollectives - ping Ping all nodes - plugin MCollective Plugin Application - rpc Generic RPC agent client application -{% endhighlight %} - -You can request help for a specific application using either *mco help -application* or *mco application ---help*. Shown below is part of the -help for the *rpc* application: - -{% highlight console %} -% mco help rpc -Generic RPC agent client application - -Usage: mco rpc [options] [filters] --agent --action [--argument --argument ...] -Usage: mco rpc [options] [filters] [ ...] - - --no-results, --nr Do not process results, just send request - -a, --agent AGENT Agent to call - --action ACTION Action to call - --arg, --argument ARGUMENT Arguments to pass to agent - - --np, --no-progress Do not show the progress bar - -1, --one Send request to only one discovered nodes - --batch SIZE Do requests in batches - --batch-sleep SECONDS Sleep time between batches - --limit-nodes, --ln COUNT Send request to only a subset of nodes, can be a percentage - -j, --json Produce JSON output - -c, --config FILE Load configuratuion from file rather than default - -v, --verbose Be verbose - -h, --help Display this screen - -Common Options - -T, --target COLLECTIVE Target messages to a specific sub collective - --dt, --discovery-timeout SECONDS - Timeout for doing discovery - -t, --timeout SECONDS Timeout for calling remote agents - -q, --quiet Do not be verbose - --ttl TTL Set the message validity period - --reply-to TARGET Set a custom target for replies - -Host Filters - -W, --with FILTER Combined classes and facts filter - -S, --select FILTER Compound filter combining facts and classes - -F, --wf, --with-fact fact=val Match hosts with a certain fact - -C, --wc, --with-class CLASS Match hosts with a certain config management class - -A, --wa, --with-agent AGENT Match hosts with a certain agent - -I, --wi, --with-identity IDENT Match hosts with a certain configured identity - -The Marionette Collective 2.0.0 -{% endhighlight %} - -The *help* first shows a basic overview of the command line syntax -followed by options specific to this command. Following that you will -see some *Common Options* and *Host Filters* that generally apply to -most applications. - -## Making RPC Requests - -### Overview of a Request - -The *rpc* application is the main application used to make requests to -your servers. It is capable of interacting with any standard Remote -Procedure Call (RPC) agent. Below is an example that shows an attempt to -start a webserver on several machines: - -{% highlight console %} -% mco rpc service start service=httpd -Determining the amount of hosts matching filter for 2 seconds .... 10 - - * [ ============================================================> ] 10 / 10 - -dev4 Request Aborted - Could not start Service[httpd]: Execution of '/sbin/service httpd start' returned 1: - - -Finished processing 10 / 10 hosts in 1323.61 ms -{% endhighlight %} - -The order of events in this process are: - - * Perform discovery against the network and discover 10 servers - * Send the request and then show a progress bar of the replies - * Show any results that were out of the ordinary - * Show some statistics - -Mcollective client applications aim to only provide the most relevant -information. In this case, the application is not showing verbose -information about the nine *OK* results, since the most important issue -is the one *Failure*. Keep this in mind when viewing the results of -commands. - -### Anatomy of a Request - -MCollective agents are broken up into actions and each action can take -input arguments. - -{% highlight console %} -% mco rpc service stop service=httpd -{% endhighlight %} - -This shows the basic make-up of an RPC command. In this case we are: - - * using the *rpc* application - a generic application that can interact with any agent - * directing our request to machines with the *service* agent - * sending a request to the *stop* action of the service agent - * supplying a value, *httpd*, to the *service* argument of the *stop* action - -The same command has a longer form as well: - -{% highlight console %} -% mco rpc --agent service --action stop --argument service=httpd -{% endhighlight %} - -These two commands are functionally identical. - -### Discovering Available *Agents* - -The above command showed you how to interact with the *service* agent, -but how can you find out that this agent even exists? On a correctly -installed MCollective system you can use the *plugin* application to get -a list: - -{% highlight console %} -% mco plugin doc -Please specify a plugin. Available plugins are: - -Agents: - package Install and uninstall software packages - puppetd Run puppet agent, get its status, and enable/disable it - rpcutil General helpful actions that expose stats and internals to SimpleRPC clients - service Agent to manage services -{% endhighlight %} - -The first part of this list shows all the agents this computer is aware -of. In order to show up on this list, an agent must have a *DDL* file -and be installed locally. - -To find out the *actions*, *inputs* and *outputs* for a specific agent -use the plugin application again: - -{% highlight console %} -% mco plugin doc service -SimpleRPC Service Agent -======================= - -Agent to manage services - - Author: R.I.Pienaar - Version: 1.2 - License: GPLv2 - Timeout: 60 - Home Page: http://mcollective-plugins.googlecode.com/ - - - -ACTIONS: -======== - restart, start, status, stop - - status action: - -------------- - Gets the status of a service - - INPUT: - service: - Description: The service to get the status for - Prompt: Service Name - Type: string - Validation: ^[a-zA-Z\-_\d]+$ - Length: 30 - - - OUTPUT: - status: - Description: The status of service - Display As: Service Status -{% endhighlight %} - -This shows a truncated example of the auto-generated help for the -*service* agent. First shown is metadata such as version, author and -license. This is followed by the list of actions available, in this case -the *restart*, *start*, *status* and *stop* actions. - -Further information is shown about each action. For example, you can see -that the *status* action requires an input called *service* which is a -string, has a maximum length of 30, etc. You can also see you will -receive one output called *status* - -With this information, you can request the status for a specific -service: - -{% highlight console %} -% mco rpc service status service=httpd -Determining the amount of hosts matching filter for 2 seconds .... 10 - - * [ ============================================================> ] 10 / 10 - - -dev1 - Service Status: stopped - -dev4 - Service Status: stopped - -. -. -. - -Finished processing 10 / 10 hosts in 326.01 ms -{% endhighlight %} - -Unlike the previous example, in this case specific information is -returned on the success of the action. This is because this specific -action is meant to retrieve information and so mcollective assumes you -would like to see complete, thorough data regardless of success or -failure. - -Note that this output displays *Service Status* as shown in the *mco -plugin doc service* help page. Any time you need more information about -a display name, the doc for the associated agent will have a -*Description* section for every input and output. - -## Selecting Request Targets Using *Filters* - -### Basic Filters - -A key capability of mcollective is fast discovery of network resources. -Discovery rules are written using *filters*. For example: - -{% highlight console %} -% mco rpc service status service=httpd -S "environment=development or customer=acme" -{% endhighlight %} - -This shows a filter rule that limits the RPC request to being run on -machines that are either in the Puppet environment *development* or -belong to the Customer *acme*. - -Filtering can be based on *facts*, the presence of a *Configuration -Management Class* on the node, the node's *Identity*, or installed -*Agents* on the node. - -Here are a number of examples of this with short descriptions of each -filter: - -{% highlight console %} -# all machines with the service agent -% mco ping -A service -% mco ping --with-agent service - -# all machines with the apache class on them -% mco ping -C apache -% mco ping --with-class apache - -# all machines with a class that match the regular expression -% mco ping -C /service/ - -# all machines in the UK -% mco ping -F country=uk -% mco ping --with-fact country=uk - -# all machines in either UK or USA -% mco ping -F "country=/uk|us/" - -# just the machines called dev1 or dev2 -% mco ping -I dev1 -I dev2 - -# all machines in the domain foo.com -% mco ping -I /foo.com$/ -{% endhighlight %} - -As you can see, you can filter by Agent, Class and/or Fact, and you can -use regular expressions almost anywhere. You can also combine filters -additively in a command so that all the criteria have to be matched. - -Note: You can use a shortcut to combine Class and Fact filters: - -{% highlight console %} -# all machines with classes matching /apache/ in the UK -% mco ping -W "/apache/ location=uk" -{% endhighlight %} - -### Complex *Compound* or *Select* Queries - -While the above examples are easy to enter, they are limited in that -they can only combine filters additively. If you want to create searches -with more complex boolean logic use the *-S* switch. For example: - -{% highlight console %} -% mco ping -S "((customer=acme and environment=staging) or environment=development) and /apache/" -{% endhighlight %} - -The above example shows a scenario where the development environment is -usually labeled *development* but one customer has chosen to use -*staging*. You want to find all machines in those customer's -environments that match the class *apache*. This search would be -impossible using the previously shown methods, but the above command -uses *-S* to allow the use of boolean operators such as *and* and *or* -so you can easily build the logic of the search. - -The *-S* switch also allows for negative matches using *not* or *!*: - -{% highlight console %} -% mco ping -S "environment=development and !customer=acme" -% mco ping -S "environment=development and not customer=acme" -{% endhighlight %} - -### Filtering Using Data Plugins - -As of version 2.1.0, custom data plugins can also be used to create -complex filters: - -{% highlight console %} -% mco ping -S "fstat('/etc/hosts').md5=/baa3772104/ and environment=development" -{% endhighlight %} - -This will search for the md5 hash of a specific file with matches -restricted to the *development* environment. Note that as before, -regular expressions can also be used. - -As with agents, you can also discover which plugins are available for -use: - -{% highlight console %} -% mco plugin doc - -Please specify a plugin. Available plugins are: - -Agents: - . - . - -Data Queries: - agent Meta data about installed MColletive Agents - augeas_match Allows agents and discovery to do Augeas match lookups - fstat Retrieve file stat data for a given file - resource Information about Puppet managed resources - sysctl Retrieve values for a given sysctl -{% endhighlight %} - -For information on the input these plugins take and output they provide -use the *mco plugin doc fstat* command. - -Currently, each data function can only accept one input while matches -are restricted to a single output field per invocation. - -## Chaining RPC Requests - -The *rpc* application can chain commands one after the other. The -example below uses the *package* agent to find machines with a specific -version of mcollective and then schedules Puppet runs on those machines: - -{% highlight console %} -% mco rpc package status package=mcollective -j|jgrep "data.properties.ensure=2.0.0-6.el6" |mco rpc puppetd runonce -{% endhighlight %} - -Mcollective results can also be filtered using the opensource gem, -jgrep. Mcollective data output is fully compatible with jgrep. - -## Using with PuppetDB - -Recent versions of PuppetDB has a built in query language called -Puppet Query Language that you use via the `puppet query` command. - -Much like the above example of chaining RPC requests MCollective supports -reading results from Puppet Query: - -{% highlight console %} -% puppet query "inventory { facts.os.name = 'CentOS' }"| mco rpc puppetd runonce -{% endhighlight %} - -This will run Puppet on all CentOS machines - -## Seeing the Raw Data - -By default the *rpc* application will try to show human-readable data. -To see the actual raw data, add the *-v* flag to disable the display -helpers: - -{% highlight console %} -% mco rpc nrpe runcommand command=check_load -I dev1 -v -. -. -dev1 : OK - {:exitcode=>0, :output=>"OK - load average: 0.00, 0.00, 0.00", :perfdata=> "load1=0.000;1.500;2.000;0; load5=0.000;1.500;2.000;0; load15=0.000;1.500;2.000;0;"} -{% endhighlight %} - - -This data can also be returned in JSON format: - -{% highlight console %} -% mco rpc nrpe runcommand command=check_load -I dev1 -j -[ - { - "action": "runcommand", - "agent": "nrpe", - "data": { - "exitcode": 0, - "output": "OK - load average: 0.00, 0.00, 0.00", - "perfdata": "load1=0.000;1.500;2.000;0; load5=0.000;1.500;2.000;0; load15=0.000;1.500;2.000;0;" - }, - "statuscode": 0, - "statusmsg": "OK", - "sender": "dev1" - } -] -{% endhighlight %} - -## Error Messaging - -When an application encounters an error, it returns an explanatory -string: - -{% highlight console %} -% mco rpc rpcutil foo -rpc failed to run: Attempted to call action foo for rpcutil but it's not declared in the DDL (MCollective::DDLValidationError) -{% endhighlight %} - -By default only an abbreviated error string is shown that provides some -insight into the nature of the problem. For more details, add the *-v* -flag to show a full stack trace: - -{% highlight console %} -% mco rpc rpcutil foo -v -rpc failed to run: Attempted to call action foo for rpcutil but it's not declared in the DDL (MCollective::DDLValidationError) - from /usr/lib/ruby/site_ruby/1.8/mcollective/ddl.rb:303:in `validate_rpc_request' - from /usr/lib/ruby/site_ruby/1.8/mcollective/rpc/client.rb:218:in `method_missing' - from /home/rip/.mcollective.d/lib/mcollective/application/rpc.rb:133:in `send' - from /home/rip/.mcollective.d/lib/mcollective/application/rpc.rb:133:in `main' - from /usr/lib/ruby/site_ruby/1.8/mcollective/application.rb:283:in `run' - from /usr/lib/ruby/site_ruby/1.8/mcollective/applications.rb:23:in `run' - from /usr/bin/mco:20 -{% endhighlight %} - -## Exit codes - -When _mco_ finishes, it generates an exit code. The returned exit code depends on the nature of the issue: - -- 0: If nodes were discovered and all passed. -- 0: If no discovery was performed, at least 1 response was received, and all responses were OK. -- 1: If no nodes were discovered, or if mco encountered an issue not listed here. -- 2: If nodes were discovered but some RPC requests failed. -- 3: If nodes were discovered, but no responses were received. -- 4: If no discovery was performed, and no responses were received. - -## Custom Applications - -The *rpc* application should suit most needs. However, sometimes the -data being returned calls for customization such as custom aggregation, -summarising or complete custom display. - -In such cases, a custom application may be useful For example, the -*package* application provides concluding summaries and provides some -basic safe guards for its use. The agent also provides the commonly -required data. Typical *package* output looks like this: - -{% highlight console %} -% mco package status kernel -Do you really want to operate on packages unfiltered? (y/n): y - - * [ ============================================================> ] 25 / 25 - - - dev5 version = kernel-2.6.32-220.7.1.el6 - dev9 version = kernel-2.6.32-220.2.1.el6 - . - . - ----- package agent summary ---- - Nodes: 25 / 25 - Versions: 9 * 2.6.32-220.2.1.el6, 9 * 2.6.32-220.4.1.el6, 7 * 2.6.32-220.el6 - Elapsed Time: 3.95 s -{% endhighlight %} - - -Notice how this application recognises that you are acting on all -possible machines, an action which might have a big impact on your YUM -servers. Consequently, *package* prompts for confirmation and, at the -end of processing, displays a brief summary of the network status. - -While the behaviors of custom applications are not always consistent -with each other, in general they accept the standard discovery flags. -For details of which flags are accepted in a given application, use the -*mco help appname* command. - -To discover which custom applications are available, run *mco* or *mco -help*. diff --git a/website/reference/basic/configuration.md b/website/reference/basic/configuration.md deleted file mode 100644 index 85ca0d31..00000000 --- a/website/reference/basic/configuration.md +++ /dev/null @@ -1,136 +0,0 @@ ---- -layout: default -title: Configuration Guide ---- - -[SSLSecurity]: /mcollective/reference/plugins/security_ssl.html -[AESSecurity]: /mcollective/reference/plugins/security_aes.html -[Registration]: /mcollective/reference/plugins/registration.html -[Auditing]: /mcollective/simplerpc/auditing.html -[Authorization]: /mcollective/simplerpc/authorization.html -[Subcollectives]: /mcollective/reference/basic/subcollectives.html -[client_config]: /mcollective/configure/client.html -[server_config]: /mcollective/configure/server.html - -> **Note:** For detailed information on MCollective's client and server daemon settings, see [Client Configuration Reference][client_config] and [Server Configuration Reference][server_config], respectively. - -You can configure the MCollective server daemon and client by setting options in their configuration files. Plugins can also add their own options. - -## Configuration Files - -MCollective uses two configuration files, one for the client and one for the server. - -If you don't specify a client configuration file when invoking MCollective, it looks for one in these locations, in the listed order: - -1. `~/.mcollective` -1. `/etc/puppetlabs/mcollective/client.cfg` -1. `/etc/mcollective/client.cfg` - -The MCollective server daemon looks in these locations, also in order: - -1. `/etc/puppetlabs/agent/mcollective/server.cfg` -1. `/etc/mcollective/server.cfg` - -The configuration file formats use a simple key-value syntax: - -~~~ ini -key = value -~~~ - -## Common Options - -|Key|Sample|Description| -|---|------|-----------| -|collectives|mcollective,subcollective|A list of [Subcollectives][] to join - 1.1.3 and newer only| -|main_collective|mcollective|The main collective to target - 1.1.3 and newer only| -|logfile|/var/log/mcollective.log|Where to log| -|loglevel|debug|Can be info, warn, debug, fatal, error| -|identity|dev1.your.com|Identifier for this node, does not need to be unique, defaults to hostname if unset and must match _/\w\.\-/_ if set| -|keeplogs|5|The amount of logs to keep| -|max_log_size|2097152|Max size in bytes for log files before rotation happens| -|libdir|/usr/libexec/mcollective:/site/mcollective|Where to look for plugins| -|connector|activemq|Which _connector_ plugin to use for communication| -|securityprovider|Psk|Which security model to use, see [SSL Security Plugin][SSLSecurity] and [AES Security Plugin][AESSecurity] for details on others| -|logger_type|file|Valid logger types, currently file, syslog or console| -|ssl_cipher|aes-256-cbc|This sets the cipher in use by the SSL code, see _man enc_ for a list supported by OpenSSL| -|direct_addressing|n|Enable or disable directed requests| -|direct_addressing_threshold|10|When direct requests are enabled, send direct messages for less than or equal to this many hosts| -|ttl|60|Sets the default TTL for requests - 1.3.2 and newer only| -|logfacility|When using the syslog logger sets the facility, defaults to user| -|default_discovery_method|The default method to use for discovery - _mc_ by default| -|default_discovery_options|Options to pass to the discovery plugin, empty by default| - -## Server Configuration - -The server configuration file should be readable by the root user _only._ - -|Key|Sample|Description| -|---|------|-----------| -|daemonize|1|Runs the server in the background| -|factsource|Facter|Which fact plugin to use| -|registration|Agentlist|[Registration][] plugin to use| -|registerinterval|120|How many seconds to sleep between registration messages, setting this to zero disables registration| -|registration_collective|development|Which sub-collective to send registration messages to| -|classesfile|/var/lib/puppet/classes.txt|Where to find a list of classes configured by your configuration management system| -|rpcaudit|1|Enables [SimpleRPC Auditing][Auditing]| -|rpcauditprovider|Logfile|Enables auditing using _MCollective::Audit::Logfile_| -|rpcauthorization|1|Enables [SimpleRPC Authorization][Authorization] globally| -|rpcauthprovider|action_policy|Use the _MCollective::Util::ActionPolicy_ plugin to manage authorization| -|rpclimitmethod|The method used for --limit-results. Can be either _first_ or _random_| -|fact_cache_time|300|How long to cache fact results for before refreshing from source| - -## Client Configuration - -The client configuration file should be globally readable. - -> **Security Note:** _Don't_ put pre-shared keys or client SSL certificates in a world-readable file. See [Client Setup](#client-setup) for details on how to provide those values for each user. - -|Key|Sample|Description| -|---|------|-----------| -|color|0|Disables the use of color in RPC results| -|connection_timeout|3|Sets the timeout for server communication. Default: none| -|discovery_timeout|2|Sets the timeout for discovering nodes. Default: 2| -|default_batch_size|100|Sets the default batch size which would be used when when not supplied in the API or CLI. Default 0| -|default_batch_sleep_time|30|Sets the default batch sleep time which would be used when when not supplied in the API or CLI. Default 1| - -## Plugin Configuration - -You can add configuration options for plugins you create, using this syntax: - -~~~ ini -plugin.pluginname.option = value -~~~ - -Describe any options in the plugin's documentation. - -Common plugin options include: - -|Key|Sample|Description| -|---|------|-----------| -|plugin.yaml|/etc/puppetlabs/agent/mcollective/facts.yaml:/other/facts.yaml|Where the YAML fact source finds facts; multiples are merged| -|plugin.psk|123456789|The pre-shared key (PSK) to use for the PSK security provider| -|plugin.psk.callertype|group|What to base the callerid on for the PSK plugin: uid, gid, user, group, or identity| - -## Client Setup - -Do not set the host, user, password, and pre-shared key in the client configuration file, since these files are generally world-readable (unlike the server configuration file, which should be readable by the root user only). - -> **Note:** You can make this clearer by explicitly setting these options to 'unset' in the client configuration file, which prevents MCollective from working unless something overrides those settings. - -You can set per-user environment variables to supply these values: - -~~~ bash -export STOMP_USER=user -export STOMP_PASSWORD=password -export MCOLLECTIVE_PSK=123456789 -~~~ - -You can also set options that MCollective always applies by using the `MCOLLECTIVE_EXTRA_OPTS` environment variable: - -~~~ bash -export MCOLLECTIVE_EXTRA_OPTS="--dt 5 --timeout 3 --config /home/you/mcollective.cfg" -~~~ - -The client library uses these variables, allowing you to give each administrative user their own username and privileges. - -You can also configure clients in a user's `~/.mcollective` file as an alternative to the method above, but that file must then be a complete client configuration file; MCollective will not look for and apply other client configuration files after finding one at `~/.mcollective`. diff --git a/website/reference/basic/daemon.md b/website/reference/basic/daemon.md deleted file mode 100644 index b74a6654..00000000 --- a/website/reference/basic/daemon.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -layout: default -title: Controlling the Daemon ---- - -The main daemon that runs on nodes keeps internal stats and supports reloading of agents and changing -logging level without restarting. - -If you want to reload all the agents without restarting the daemon you can just send it signal *USR1* -and it will reload its agents. - -You can send *USR2* to cycle the log level through DEBUG to FATAL and back again, just keep sending -the signal and look at the logs. - -You can send *WINCH* to flush and reopen logfiles, for logrotation purposes. - -Reloading agents work in most cases though we recommend a full daemon restart in production use -due to the nature of the ruby class loading system. If you are changing agent contents and relying -on the reload behavior you might end up with agents not being in a consistent state. - -## Obtaining daemon statistics - -The daemon keeps a number of statistics about its operation, you can view these using the _inventory_ -application: - -{% highlight console %} -% mco inventory example.com - Server Statistics: - Version: 2.2.0 - Start Time: Mon Sep 24 17:37:28 +0100 2012 - Config File: /etc/mcollective/server.cfg - Collectives: mcollective, fr_collective - Main Collective: mcollective - Process ID: 24473 - Total Messages: 52339 - Messages Passed Filters: 44118 - Messages Filtered: 8221 - Expired Messages: 0 - Replies Sent: 29850 - Total Processor Time: 527.06 seconds - System Time: 349.32 seconds - -. -. -. -{% endhighlight %} - -The statistics mean: - -|Statistic |Meaning | -|------------|-------------------------------------------| -|Start Time |Local time on the node when the daemon started| -|Collectives |All known collectives this agent responds on| -|Main Collective |The primary collective| -|Process ID |The process ID of the mcollectived| -|Total Messages |Total messages received from the middleware| -|Messages Passed Filters|Amount of messages that was determined to be applicable to this node based on filters| -|Messages Filtered |Messages that did not apply to this node| -|Expired Messages |Received messages that had expired their TTL values| -|Replies Sent |Not all received messages result in replies, this counts the actual replies sent| -|Total Processor Time |Processor time including user and system time consumed since start| -|System Time |System Processor time only| - -You can get the raw values using the *rpcutil* agent using the *daemon_stats* action. - -{% highlight console %} -% mco rpc rpcutil daemon_stats -Discovering hosts using the mongo method .... 26 - - * [ ============================================================> ] 26 / 26 - -. -. -. - -example.com - Agents: ["stomputil", - "nrpe", - "package", - "rpcutil", - "rndc", - "urltest", - "iptables", - "puppetd", - "discovery", - "service", - "eximng", - "filemgr", - "process"] - Config File: /etc/mcollective/server.cfg - Failed Filter: 168432 - Passed Filter: 91231 - PID: 1418 - Replies: 91127 - Start Time: 1347545937 - Threads: ["#", - "#", - "#", - "#"] - Times: {:cutime=>1111.13, :utime=>3539.21, :cstime=>1243.64, :stime=>5045.21} - Total Messages: 259842 - TTL Expired: 179 - Failed Security: 0 - Security Validated: 259842 - Version: 2.2.0 - - -Summary of Agents: - - package = 26 - process = 26 - discovery = 26 - service = 26 - puppetd = 26 - filemgr = 26 - nrpe = 26 - rpcutil = 26 - stomputil = 26 - iptables = 11 - urltest = 7 - libvirt = 4 - eximng = 4 - registration = 3 - rndc = 3 - angelianotify = 2 - -Summary of Version: - - 2.2.0 = 26 - -Finished processing 26 / 26 hosts in 289.20 ms -{% endhighlight %} - -## Obtaining running configuration settings - -All configuration settings of any mcollective daemon can be retrieved using the *get_config_item* -action of the *rpcutil* agent: - -{% highlight console %} -% mco rpc rpcutil get_config_item item=collectives -I example.com -Discovering hosts using the mongo method .... 1 - - * [ ============================================================> ] 1 / 1 - - -example.com: - Property: collectives - Value: ["mcollective", "fr_collective"] - - -Summary of Value: - - mcollective = 1 - fr_collective = 1 - - -Finished processing 1 / 1 hosts in 59.05 ms -{% endhighlight %} diff --git a/website/reference/basic/gettingstarted.md b/website/reference/basic/gettingstarted.md deleted file mode 100644 index 43cba931..00000000 --- a/website/reference/basic/gettingstarted.md +++ /dev/null @@ -1,35 +0,0 @@ ---- -layout: default -title: Getting Started ---- -[Screencasts]: /mcollective/screencasts.html -[ActiveMQ]: http://activemq.apache.org/ -[RabbitMQ]: http://www.rabbitmq.com/ -[EC2Demo]: /mcollective/ec2demo.html -[Stomp]: http://stomp.codehaus.org/Ruby+Client -[DepRPMs]: https://yum.puppetlabs.com/ -[DebianBug]: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562954 -[SecurityWithActiveMQ]: /mcollective/reference/integration/activemq_security.html -[ActiveMQClustering]: http://www.devco.net/archives/2009/11/10/activemq_clustering.php -[ActiveMQSamples]: http://github.com/puppetlabs/marionette-collective/tree/master/ext/activemq/examples/ -[ConfigurationReference]: /mcollective/reference/basic/configuration.html -[Terminology]: /mcollective/terminology.html -[SimpleRPCIntroduction]: /mcollective/simplerpc/ -[ControllingTheDaemon]: /mcollective/reference/basic/daemon.html -[SSLSecurityPlugin]: /mcollective/reference/plugins/security_ssl.html -[AESSecurityPlugin]: /mcollective/reference/plugins/security_aes.html -[ConnectorActiveMQ]: /mcollective/reference/plugins/connector_activemq.html -[ConnectorRabbitMQ]: /mcollective/reference/plugins/connector_rabbitmq.html -[MessageFlowCast]: /mcollective/screencasts.html#message_flow -[Plugins]: https://docs.puppetlabs.com/mcollective/plugin_directory/ -[RedHatGuide]: gettingstarted_redhat.html -[DebianGuide]: gettingstarted_debian.html -[server_config]: /mcollective/configure/server.html - -> ## This Page is Deprecated -> -> We've revamped our getting started documentation! Instead of this page, use one of the following: -> -> * [Demo MCollective with Vagrant](/mcollective/deploy/demo.html) -> * [Deploy MCollective (Standard Deployment)](/mcollective/deploy/standard.html) - diff --git a/website/reference/basic/gettingstarted_debian.md b/website/reference/basic/gettingstarted_debian.md deleted file mode 100644 index 7c52307c..00000000 --- a/website/reference/basic/gettingstarted_debian.md +++ /dev/null @@ -1,259 +0,0 @@ ---- -layout: default -title: Getting Started ---- -[Screencasts]: /mcollective/screencasts.html -[ActiveMQ]: http://activemq.apache.org/ -[ActiveMQ Getting Started]: http://activemq.apache.org/getting-started.html -[EC2Demo]: /mcollective/ec2demo.html -[Stomp]: http://stomp.codehaus.org/Ruby+Client -[DepRPMs]: https://yum.puppetlabs.com/ -[DebianBug]: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562954 -[SecurityWithActiveMQ]: /mcollective/reference/integration/activemq_security.html -[ActiveMQClustering]: /mcollective/reference/integration/activemq_clusters.html -[ActiveMQSamples]: http://github.com/puppetlabs/marionette-collective/tree/master/ext/activemq/examples/ -[ActiveMQSingleBrokerSample]: http://github.com/puppetlabs/marionette-collective/raw/master/ext/activemq/examples/single-broker/activemq.xml -[ConfigurationReference]: /mcollective/reference/basic/configuration.html -[Terminology]: /mcollective/terminology.html -[SimpleRPCIntroduction]: /mcollective/simplerpc/ -[ControllingTheDaemon]: /mcollective/reference/basic/daemon.html -[SSLSecurityPlugin]: /mcollective/reference/plugins/security_ssl.html -[AESSecurityPlugin]: /mcollective/reference/plugins/security_aes.html -[ConnectorActiveMQ]: /mcollective/reference/plugins/connector_activemq.html -[ConnectorRabbitMQ]: /mcollective/reference/plugins/connector_rabbitmq.html -[MessageFlowCast]: /mcollective/screencasts.html#message_flow -[Plugins]: https://docs.puppetlabs.com/mcollective/plugin_directory/ -[MCDownloads]: http://www.puppetlabs.com/downloads/mcollective/ -[RubyGems]: http://packages.debian.org/search?suite=default§ion=all&arch=any&searchon=names&keywords=rubygems -[server_config]: /mcollective/configure/server.html - - -> ## This Page is Deprecated -> -> We've revamped our getting started documentation! Instead of this page, use one of the following: -> -> * [Demo MCollective with Vagrant](/mcollective/deploy/demo.html) -> * [Deploy MCollective (Standard Deployment)](/mcollective/deploy/standard.html) - - -Getting started using Debian based distribution like Debian squeeze and Ubuntu is easy as DEBs are available for most the required components. This guide walks you through the process. - -## Requirements -We try to keep the requirements on external Gems to a minimum, you only need: - - * A Stomp server, tested against [ActiveMQ][] - * Ruby - * RubyGems - * [Ruby Stomp Client][Stomp] - -## Packages - -We strongly recommend you set up a local Apt repository that will host all the packages on your LAN, you can get the prerequisite packages here: - - * [ActiveMQ][] - * Java - OpenJDK that is included with your distribution - * Ruby - included with your distribution - * [RubyGems][] - * Stomp Ruby Gem - * [MCollective][MCDownloads] - mcollective-2.2.x-1_all.deb, mcollective-common-2.2.x-1_all.deb, mcollective-client-2.2.x-1_all.deb - -The rest of this guide will assume you set up a Apt repository. Puppet Labs hosts a Apt repository with all these dependencies at _apt.puppetlabs.com_. - -## ActiveMQ - -ActiveMQ is currently the most used and tested middleware for use with MCollective. - -You need at least one ActiveMQ server on your network, all the nodes you wish to manage will connect to the central ActiveMQ server. -Later on you can [cluster the ActiveMQ servers for availability and scale][ActiveMQClustering]. - -### Install - -On the server that you chose to configure as the ActiveMQ server: - -{% highlight console %} -% apt-get install openjdk-6-jre -{% endhighlight %} - -ActiveMQ installation instructions can be found [here][ActiveMQ Getting Started]. - -### Configuring - -[The ActiveMQ config reference][activemq_config] describes all of the ActiveMQ settings that MCollective cares about. For best use, skim the sections you care about while comparing it to an example activemq.xml file. - -[activemq_config]: /mcollective/deploy/middleware/activemq.html - -We recommend that new users: - -* Start with the [single-broker example config][ActiveMQSingleBrokerSample]. -* Change the [user account passwords](/mcollective/deploy/middleware/activemq.html#authentication-users-and-groups). -* [Set up TLS](/mcollective/deploy/middleware/activemq.html#tls-credentials) and [use a TLS Stomp transport connector](/mcollective/deploy/middleware/activemq.html#transport-connectors). - -Other example config files are also available from [GitHub][ActiveMQSamples]. - -### Starting - -Start the ActiveMQ service: - -{% highlight console %} - # /etc/init.d/activemq start -{% endhighlight %} - -You should see it running in the process list: - -{% highlight console %} - # ps auxw|grep java - activemq 3012 0.1 14.5 1155112 152180 ? Sl Dec28 2:02 java -Dactivemq.home=/usr/share/activemq -Dactivemq.base=/usr/share/activemq -Dcom.sun.management.jmxremote -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Xmx512m -Djava.library.path=/usr/lib:/usr/lib64 -classpath /usr/share/java/tanukiwrapper.jar:/usr/share/activemq/bin/run.jar -Dwrapper.key=eg4_VvENzCmvtAKg -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=3000 -Dwrapper.version=3.2.3 -Dwrapper.native_library=wrapper -Dwrapper.service=TRUE -Dwrapper.cpu.timeout=10 -Dwrapper.jvmid=1 org.tanukisoftware.wrapper.WrapperSimpleApp org.apache.activemq.console.Main start -{% endhighlight %} - -You should also see it listening on port 61613 or 61614 in your network stack, depending on whether you turned on TLS. - -You should open port 61613 or 61614 for all your nodes to connect to. - -## Marionette Collective - -There are a few packages supplied and you will have potentially two type of server: - - * Nodes that you wish to manage using mcollective need the mcollective and mcollective-common packages - * Nodes that you wish to use to initiate requests from also known as clients need mcollective-client and mcollective-common packages - -A machine can be both at once, in which case you need to install all 3 packages. We'll work on the assumption here that you wish to both manage your machine and use it as a client by installing all 3 packages on our initial node. - -### Installation - -{% highlight console %} - # apt-get install mcollective mcollective-client mcollective-common - # gem install stomp -{% endhighlight %} - - -## Configuring -You'll need to tweak some configs in */etc/mcollective/client.cfg*, a full reference of config settings can be -found [here][ConfigurationReference]: - -We're assuming you called the machine running ActiveMQ *stomp.example.net*; please change as appropriate. Also note that the port should be 61614 if you turned on TLS. - -{% highlight ini %} - # main config - libdir = /usr/libexec/mcollective - logfile = /dev/null - loglevel = error - - # connector plugin config - connector = activemq - plugin.activemq.pool.size = 1 - plugin.activemq.pool.1.host = stomp.example.net - plugin.activemq.pool.1.port = 61613 - plugin.activemq.pool.1.user = mcollective - plugin.activemq.pool.1.password = marionette - - # security plugin config - securityprovider = psk - plugin.psk = abcdefghj -{% endhighlight %} - -You should also create _/etc/mcollective/server.cfg_ here's a sample, a full reference of config settings can be found on the [Server Configuration Reference][server_config]: - -{% highlight ini %} - # main config - libdir = /usr/libexec/mcollective - logfile = /var/log/mcollective.log - daemonize = 1 - loglevel = info - - # connector plugin config - connector = activemq - plugin.activemq.pool.size = 1 - plugin.activemq.pool.1.host = stomp.example.net - plugin.activemq.pool.1.port = 61613 - plugin.activemq.pool.1.user = mcollective - plugin.activemq.pool.1.password = marionette - - - # facts - factsource = yaml - plugin.yaml = /etc/mcollective/facts.yaml - - # security plugin config - securityprovider = psk - plugin.psk = abcdefghj -{% endhighlight %} - -Replace the *plugin.psk* in both these files with a Pre-Shared Key of your own. - -### Create Facts -By default - and for this setup - we'll use a simple YAML file for a fact source, later on you can use Puppet Labs Facter or something else. - -Create */etc/mcollective/facts.yaml* along these lines: - -{% highlight yaml %} - --- - location: devel - country: uk -{% endhighlight %} - -### Start the Server - -The packages include standard init script, just start the server: - -{% highlight console %} - # /etc/init.d/mcollective restart -{% endhighlight %} - -You should see in the log file somethig like: - -{% highlight console %} - # tail /var/log/mcollective.log - I, [2010-12-29T11:15:32.321744 #11479] INFO -- : mcollectived:33 The Marionette Collective 1.1.0 started logging at info level -{% endhighlight %} - -### Test connectivity - -If all is fine and you see this log message you can test with the client code: - -{% highlight console %} -% mco ping -your.domain.com time=74.41 ms - ----- ping statistics ---- -1 replies max: 74.41 min: 74.41 avg: 74.41 -{% endhighlight %} - -This sends out a simple 'hello' packet to all the machines, as we only installed one you should have just one reply. - -If you install the _mcollective_ and _mcollective-common_ packages along wit the facts and server.cfg you should see more nodes show up here. - -You can explore other aspects of your machines: - -{% highlight console %} -% mco find --with-fact country=uk -your.domain.com -{% endhighlight %} - -This searches all systems currently active for ones with a fact *country=uk*, it got the data from the yaml file you made earlier. - -If you use confiuration management tools like puppet and the nodes are setup with classes with *classes.txt* in */var/lib/puppet* then you -can search for nodes with a specific class on them - the locations will configurable soon: - -{% highlight console %} -% mco find --with-class common::linux -your.domain.com -{% endhighlight %} - -The filter commands are important they will be the main tool you use to target only parts of your infrastructure with calls to agents. - -See the `--help` option to the various `mco *` commands for available options. You can now look at some of the available plugins and -play around, you might need to run the server process as root if you want to play with services etc. - -### Plugins -We provide limited default plugins, you can look on our sister project [MCollective Plugins][Plugins] where you will -find various plugins to manage packages, services etc. - -### Further Reading -From here you should look at the rest of the wiki pages some key pages are: - - * [Screencasts][] - Get a hands-on look at what is possible - * [Terminology][] - * [Introduction to Simple RPC][SimpleRPCIntroduction] - a simple to use framework for writing clients and agents - * [ControllingTheDaemon][] - Controlling a running daemon - * [AESSecurityPlugin][] - Using AES+RSA for secure message encryption and authentication of clients - * [SSLSecurityPlugin][] - Using SSL for secure message signing and authentication of clients diff --git a/website/reference/basic/gettingstarted_redhat.md b/website/reference/basic/gettingstarted_redhat.md deleted file mode 100644 index 7d592c87..00000000 --- a/website/reference/basic/gettingstarted_redhat.md +++ /dev/null @@ -1,255 +0,0 @@ ---- -layout: default -title: Getting Started ---- -[Screencasts]: /mcollective/screencasts.html -[ActiveMQ]: http://activemq.apache.org/ -[EC2Demo]: /mcollective/ec2demo.html -[Stomp]: http://stomp.codehaus.org/Ruby+Client -[DepRPMs]: https://yum.puppetlabs.com/ -[DebianBug]: http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=562954 -[SecurityWithActiveMQ]: /mcollective/reference/integration/activemq_security.html -[ActiveMQClustering]: /mcollective/reference/integration/activemq_clusters.html -[ActiveMQSamples]: http://github.com/puppetlabs/marionette-collective/tree/master/ext/activemq/examples/ -[ActiveMQSingleBrokerSample]: http://github.com/puppetlabs/marionette-collective/raw/master/ext/activemq/examples/single-broker/activemq.xml -[ConfigurationReference]: /mcollective/reference/basic/configuration.html -[Terminology]: /mcollective/terminology.html -[SimpleRPCIntroduction]: /mcollective/simplerpc/ -[ControllingTheDaemon]: /mcollective/reference/basic/daemon.html -[SSLSecurityPlugin]: /mcollective/reference/plugins/security_ssl.html -[AESSecurityPlugin]: /mcollective/reference/plugins/security_aes.html -[ConnectorActiveMQ]: /mcollective/reference/plugins/connector_activemq.html -[ConnectorRabbitMQ]: /mcollective/reference/plugins/connector_rabbitmq.html -[MessageFlowCast]: /mcollective/screencasts.html#message_flow -[Plugins]: https://docs.puppetlabs.com/mcollective/plugin_directory/ -[MCDownloads]: http://www.puppetlabs.com/downloads/mcollective/ -[EPEL]: http://fedoraproject.org/wiki/EPEL -[server_config]: /mcollective/configure/server.html - - -> ## This Page is Deprecated -> -> We've revamped our getting started documentation! Instead of this page, use one of the following: -> -> * [Demo MCollective with Vagrant](/mcollective/deploy/demo.html) -> * [Deploy MCollective (Standard Deployment)](/mcollective/deploy/standard.html) - - - -Getting started using Red Hat based distribution like Red Hat Enterprise Linux and CentOS is easy as RPMs are available for all the required components. This guide walks you through the process. - -## Requirements -We try to keep the requirements on external Gems to a minimum, you only need: - - * A Stomp server, tested against [ActiveMQ][] - * Ruby - * Rubygems - * [Ruby Stomp Client][Stomp] - -## Packages - -We strongly recommend you set up a local Yum repository that will host all the packages on your LAN, you can get the prerequisite packages here: - - * [ActiveMQ][MCDownloads] - `activemq-5.4.0-2.el5.noarch.rpm`, `activemq-info-provider-5.4.0-2.el5.noarch.rpm`, `tanukiwrapper-3.2.3-1jpp.*.rpm` - * Java - OpenJDK that is included with your distribution - * Ruby - included with your distribution - * RubyGems - [EPEL][] - * Stomp Ruby Gem - [EPEL][] - * [MCollective][MCDownloads] - `mcollective-2.2.x-1.el5.noarch.rpm`, `mcollective-common-2.2.x-1.el5.noarch.rpm`, `mcollective-client-2.2.x-1.el5.noarch.rpm` - -The rest of this guide will assume you set up a Yum repository. Puppet Labs hosts a Yum repository with all these dependencies at _yum.puppetlabs.com_. - -## ActiveMQ -ActiveMQ is currently the most used and tested middleware for use with MCollective. - -You need at least one ActiveMQ server on your network, all the nodes you wish to manage will connect to the central ActiveMQ server. -Later on your can [cluster the ActiveMQ servers for availability and scale][ActiveMQClustering]. - -### Install - -On the server that you chose to configure as the ActiveMQ server: - -{% highlight console %} -% yum install java-1.6.0-openjdk activemq -{% endhighlight %} - -### Configuring - -[The ActiveMQ config reference][activemq_config] describes all of the ActiveMQ settings that MCollective cares about. For best use, skim the sections you care about while comparing it to an example activemq.xml file. - -[activemq_config]: /mcollective/deploy/middleware/activemq.html - -We recommend that new users: - -* Start with the [single-broker example config][ActiveMQSingleBrokerSample]. -* Change the [user account passwords](/mcollective/deploy/middleware/activemq.html#authentication-users-and-groups). -* [Set up TLS](/mcollective/deploy/middleware/activemq.html#tls-credentials) and [use a TLS Stomp transport connector](/mcollective/deploy/middleware/activemq.html#transport-connectors). - -Other example config files are also available from [GitHub][ActiveMQSamples]. - - -### Starting - -Start the ActiveMQ service: - -{% highlight console %} - # /etc/init.d/activemq start -{% endhighlight %} - -You should see it running in the process list: - -{% highlight console %} - # ps auxw|grep java - activemq 3012 0.1 14.5 1155112 152180 ? Sl Dec28 2:02 java -Dactivemq.home=/usr/share/activemq -Dactivemq.base=/usr/share/activemq -Dcom.sun.management.jmxremote -Dorg.apache.activemq.UseDedicatedTaskRunner=true -Xmx512m -Djava.library.path=/usr/lib:/usr/lib64 -classpath /usr/share/java/tanukiwrapper.jar:/usr/share/activemq/bin/run.jar -Dwrapper.key=eg4_VvENzCmvtAKg -Dwrapper.port=32000 -Dwrapper.jvm.port.min=31000 -Dwrapper.jvm.port.max=31999 -Dwrapper.pid=3000 -Dwrapper.version=3.2.3 -Dwrapper.native_library=wrapper -Dwrapper.service=TRUE -Dwrapper.cpu.timeout=10 -Dwrapper.jvmid=1 org.tanukisoftware.wrapper.WrapperSimpleApp org.apache.activemq.console.Main start -{% endhighlight %} - -You should also see it listening on port 61613 in your network stack - -You should open port 61613 for all your nodes to connect to. - -## Marionette Collective - -There are a few packages supplied and you will have potentially two type of server: - - * Nodes that you wish to manage using mcollective need the mcollective and mcollective-common packages - * Nodes that you wish to use to initiate requests from also known as clients need mcollective-client and mcollective-common packages - -A machine can be both at once, in which case you need to install all 3 packages. We'll work on the assumption here that you wish to both manage your machine and use it as a client by installing all 3 packages on our initial node. - -### Installation - -{% highlight console %} - # yum install mcollective mcollective-client mcollective-common rubygem-stomp -{% endhighlight %} - - -## Configuring -You'll need to tweak some configs in */etc/mcollective/client.cfg*, a full reference of config settings can be -found [here][ConfigurationReference]: - -We're assuming you called the machine running ActiveMQ *stomp.example.net* please change as appropriate - -{% highlight ini %} - # main config - libdir = /usr/libexec/mcollective - logfile = /dev/null - loglevel = error - - # connector plugin config - connector = activemq - plugin.activemq.pool.size = 1 - plugin.activemq.pool.1.host = stomp.example.net - plugin.activemq.pool.1.port = 61613 - plugin.activemq.pool.1.user = mcollective - plugin.activemq.pool.1.password = marionette - - # security plugin config - securityprovider = psk - plugin.psk = abcdefghj -{% endhighlight %} - -You should also create _/etc/mcollective/server.cfg_ here's a sample, a full reference of config settings can be found on the [Server Configuration Reference][server_config]: - -{% highlight ini %} - # main config - libdir = /usr/libexec/mcollective - logfile = /var/log/mcollective.log - daemonize = 1 - loglevel = info - - # connector plugin config - connector = activemq - plugin.activemq.pool.size = 1 - plugin.activemq.pool.1.host = stomp.example.net - plugin.activemq.pool.1.port = 61613 - plugin.activemq.pool.1.user = mcollective - plugin.activemq.pool.1.password = marionette - - # facts - factsource = yaml - plugin.yaml = /etc/mcollective/facts.yaml - - # security plugin config - securityprovider = psk - plugin.psk = abcdefghj -{% endhighlight %} - -Replace the *plugin.psk* in both these files with a Pre-Shared Key of your own. - -### Create Facts -By default - and for this setup - we'll use a simple YAML file for a fact source, later on you can use Puppet Labs Facter or something else. - -Create */etc/mcollective/facts.yaml* along these lines: - -{% highlight yaml %} - --- - location: devel - country: uk -{% endhighlight %} - -### Start the Server - -The packages include standard init script, just start the server: - -{% highlight console %} - # /etc/init.d/mcollective restart -{% endhighlight %} - -You should see in the log file somethig like: - -{% highlight console %} - # tail /var/log/mcollective.log - I, [2010-12-29T11:15:32.321744 #11479] INFO -- : mcollectived:33 The Marionette Collective 1.1.0 started logging at info level -{% endhighlight %} - -### Test connectivity - -If all is fine and you see this log message you can test with the client code: - -{% highlight console %} -% mco ping -your.domain.com time=74.41 ms - ----- ping statistics ---- -1 replies max: 74.41 min: 74.41 avg: 74.41 -{% endhighlight %} - -This sends out a simple 'hello' packet to all the machines, as we only installed one you should have just one reply. - -If you install the _mcollective_ and _mcollective-common_ packages along wit the facts and server.cfg you should see more nodes show up here. - -You can explore other aspects of your machines: - -{% highlight console %} -% mco find --with-fact country=uk -your.domain.com -{% endhighlight %} - -This searches all systems currently active for ones with a fact *country=uk*, it got the data from the yaml file you made earlier. - -If you use confiuration management tools like puppet and the nodes are setup with classes with *classes.txt* in */var/lib/puppet* then you -can search for nodes with a specific class on them - the locations will configurable soon: - -{% highlight console %} -% mco find --with-class common::linux -your.domain.com -{% endhighlight %} - -The filter commands are important they will be the main tool you use to target only parts of your infrastructure with calls to agents. - -See the `--help` option to the various `mco *` commands for available options. You can now look at some of the available plugins and -play around, you might need to run the server process as root if you want to play with services etc. - -### Plugins -We provide limited default plugins, you can look on our sister project [MCollective Plugins][Plugins] where you will -find various plugins to manage packages, services etc. - -### Further Reading -From here you should look at the rest of the wiki pages some key pages are: - - * [Screencasts][] - Get a hands-on look at what is possible - * [Terminology][] - * [Introduction to Simple RPC][SimpleRPCIntroduction] - a simple to use framework for writing clients and agents - * [ControllingTheDaemon][] - Controlling a running daemon - * [AESSecurityPlugin][] - Using AES+RSA for secure message encryption and authentication of clients - * [SSLSecurityPlugin][] - Using SSL for secure message signing and authentication of clients diff --git a/website/reference/basic/messageflow.md b/website/reference/basic/messageflow.md deleted file mode 100644 index 722dcbb5..00000000 --- a/website/reference/basic/messageflow.md +++ /dev/null @@ -1,28 +0,0 @@ ---- -layout: default -title: Message Flow -toc: false ---- -[MessageFormat]: /mcollective/reference/basic/messageformat.html -[ActiveMQClusters]: /mcollective/reference/integration/activemq_clusters.html -[SecurityWithActiveMQ]: /mcollective/reference/integration/activemq_security.html -[ScreenCast]: /mcollective/screencasts.html#message-flow-terminology-and-components - -The diagram below shows basic message flow on a MCollective system. There is also a [screencast][ScreenCast] that shows this process, recommend you watch that. - -The key thing to take away from this diagram is the broadcast paradigm that is in use, one message only leaves the client and gets broadcast to all nodes. We'll walk you through each point below. - -![Message Flow](/mcollective/images/message-flow-diagram.png) - -|Step|Description| -|----|-----------| -|A|A single messages gets sent from the workstation of the administrator to the middleware. The message has a filter attached saying only machines with the fact _cluster=c_ should perform an action.| -|B|The middleware network broadcasts the message to all nodes. The middleware network can be a [cluster of multiple servers in multiple locations, networks and data centers][ActiveMQClusters].| -|C|Every node gets the message and validates the filter| -|D|Only machines in _cluster=c_ act on the message and sends a reply, depending on your middleware only the workstation will get the reply.| - -For further information see: - - * [Messages and their contents][MessageFormat] - * [Clustering ActiveMQ brokers][ActiveMQClusters] - * [Security, authentication and authorization][SecurityWithActiveMQ] diff --git a/website/reference/basic/messageformat.md b/website/reference/basic/messageformat.md deleted file mode 100644 index 72e509cb..00000000 --- a/website/reference/basic/messageformat.md +++ /dev/null @@ -1,160 +0,0 @@ ---- -layout: default -title: Message Format ---- -[SecurityPlugins]: http://github.com/puppetlabs/marionette-collective/tree/master/lib/mcollective/security/ -[SimpleRPCIntroduction]: /mcollective/simplerpc/ -[MessageFlow]: messageflow.html -[ScreenCast]: /mcollective/screencasts.html#message_flow - -The messages that gets put on the middleware attempts to contain everything that mcollective needs to function, avoiding where possible special features in the Middle Ware. This will hopefully make it easier to create Connector plugins for other middleware. - -At present the task of encoding and decoding messages lies with the `MCollective::Security::*` classes, see the provided [security plugins][SecurityPlugins] as a examples. - -Abstracting the encoding away from the security plugins is a goal for future refactorings, till then each security plugin will need to at least conform to the following structure. - -In general this is all hidden from the developers, especially if you use [Simple RPC][SimpleRPCIntroduction]. If you want to implement your own security or serialization you will need to know exactly how all of this sticks together. - -There is also a [screencast][ScreenCast] that shows this process and message format, recommend you watch that. - -## Message Flow -For details of the flow of messages and how requests / replies travel around the network see the [MessageFlow][] page. - -## History - -|Date|Description|Ticket| -|----|-----------|------| -|2011/04/23|Add _agent_ and _collective_ to the request hashes|7113| - -### Requests sent to agents -A sample request that gets sent to the connector can be seen here, each component is described below: - -{% highlight ruby %} -{:filter => - {"cf_class" => ["common::linux"], - "fact" => [{:fact=>"country", :value=>"uk"}], - "agent" => ["package"]}, - :senderid => "devel.your.com", - :msgtarget => "/topic/mcollective.discovery.command", - :agent: => 'discovery', - :collective' => 'mcollective', - :body => body, - :hash => "2d437f2904980ac32d4ebb7ca1fd740b", - :msgtime => 1258406924, - :ttl => 60, - :requestid => "0b54253cb5d04eb8b26ea75bbf468cbc"} -{% endhighlight %} - -Once this request is created the security plugin will serialize it and sent it to the connector, in the case of the PSK security plugin this is done using Marshal. - -#### :filter -The filter will be evaluated by each node, if it passes the node will dispatch the message to an agent. - -You can see all these types of filter in action in the _MCollection::Optionparser_ class. - -Each filter is an array and you can have multiple filters per type which will be applied with an _AND_ -Valid filter types are: - -##### CF Class - -This will look in a list of classes/recipes/cookbooks/roles applied by your -configuration management system and match based on that - -{% highlight ruby %} -filter["cf_class"] = ["common::linux"] -{% endhighlight %} - -##### MCollective Agent - -This will look through the list of known agents and match against that. - -{% highlight ruby %} -filter["agent"] = ["package"] -{% endhighlight %} - -##### Facts - -Since facts are key => value pairs this is a bit more complex than normal as you need to build a nested Hash. - -{% highlight ruby %} -filter["fact"] = [{:fact => "country", :value => "uk"}] -{% endhighlight %} - -Regular expression matches are: - -{% highlight ruby %} -filter["fact"] = [{:fact => "country", :value => "/uk/"}] -{% endhighlight %} - -As of version 1.1.0 this has been extended to support more comparison operators: - -{% highlight ruby %} -filter["fact"] = [{:fact => "country", :value => "uk", :operator => "=="}] -{% endhighlight %} - -Valid operators are: ==, =~, <=, =>, >=, =<, > < and != - -As regular expressions are now done using their own operator backwards compatability -is lost and in mixed version environment 1.1.x clients doing regex matches on facts -will be treated as equality on 1.0.x and older clients. - -##### Identity - -The identity is the configured identity in the server config file, many hosts can have the same identity it's just another level of filter doesn't really mean much like a hostname that would need to be unique. - -{% highlight ruby %} -filter["identity"] = ["foo.bar.com"] -{% endhighlight %} - -#### :senderid - -The value of _identity_ in the configuration file. - -#### :msgtarget - -The Middleware topic or channel the message is being sent to. This is not being used since later 1.1.x and since 1.3.1 it is not included in messages anymore. - -#### :body - -The contents of the body will vary by what ever the security provider choose to impliment, the PSK security provider simply Marshal encodes the body into a serialized format ready for transmission. - -This ensures that variable types etc remain in tact end to end. Other security providers might use JSON etc, the decoding of this is also handled by the security provider so its totally up to the provider to decide. - -In the case of [Simple RPC][SimpleRPCIntroduction] the entire RPC request and replies will be in the body of the messages, it's effectively a layer on top of the basic message flow. - -#### :hash - -This is an example of something specific to the security provider, this is used only by the PSK provider so it's optional and specific to the PSK provider - -#### :msgtime - -The unix timestamp that the message was sent at. - -#### :ttl - -Each request has a TTL, messages older than this gets discarded. Added in version 1.3.1 - -#### :requestid - -This is a unique id for each message that gets sent, replies will have the same id attached to them for validation. - -### Replies from Agents -Replies are very similar to requests, I'll show a reply below and only highlight the differences between requests. - -{% highlight ruby %} -{:senderid => "devel.your.com", - :senderagent => "package", - :msgtarget => "/topic/mcollective.package.reply", - :body => body, - :hash => "2d437f2904980ac32d4ebb7ca1fd740b", - :msgtime => 1258406924, - :requestid => "0b54253cb5d04eb8b26ea75bbf468cbc"} -{% endhighlight %} - -Once this reply is created the security plugin will serialize it and sent it to the connector, in the case of the PSK security plugin this is done using serialization tools like Marshal or YAML depending on Security Plugin. - -#### :senderagent -This is the agent name that sent the reply - -#### :requestid -The id that was contained in the request we are replying to. Agents do not generally tend to generate messages - they only reply - so this should always be provided. diff --git a/website/reference/basic/subcollectives.md b/website/reference/basic/subcollectives.md deleted file mode 100644 index 97788986..00000000 --- a/website/reference/basic/subcollectives.md +++ /dev/null @@ -1,201 +0,0 @@ ---- -layout: default -title: Subcollectives ---- - -[ActiveMQClustering]: /mcollective/reference/integration/activemq_clusters.html -[activemq_authorization]: /mcollective/deploy/middleware/activemq.html#authorization-group-permissions -[activemq_detailed]: /mcollective/deploy/middleware/activemq.html#detailed-restrictions -[activemq_subcollectives]: /mcollective/deploy/middleware/activemq.html#detailed-restrictions-with-multiple-subcollectives -[activemq_filtering]: /mcollective/deploy/middleware/activemq.html#destination-filtering -[activemq_authentication]: /mcollective/deploy/middleware/activemq.html#authentication-users-and-groups - -## Overview - -By default all servers are part of a single broadcast domain, if you have an -agent on all machines in your network and you send a message directed to -machines with that agent they will all get it regardless of filters. - -This works well for the common use case but can present problems in the -following scenarios: - - * You have a very big and busy network. With thousands of machines responding - to requests every 10 seconds very large numbers of messages will be created - and you might want to partition the traffic. - * You have multiple data centers on different continents, the latency and - volume of traffic will be too big. You'd want to duplicate monitoring and - management automations in each datacenter but as it's all one broadcast - domain you still see large amount of traffic on the slow link. - * You can't just run multiple seperate installs because you still wish to - retain the central management feature of MCollective. - * Securing a flat network can be problematic. SimpleRPC has a security - framework that is aware of users and network topology but the core network - doesnt. - -We've introduced the concept of sub collectives that lets you define broadcast -domains and configure a mcollective server to belong to one or many of these domains. - -## Partitioning Approaches - -Determining how to partition your nework can be a very complex subject and -requires an understanding of your message flow, where requestors sit and also -the topology of your middleware clusters. - -Most middleware solutions will only send traffic where they know there exist an -interest in this traffic. Therefore if you had an agent on only 10 of 1000 -machines only those 10 machines will receive the associated traffic. This is an -important distinction to keep in mind. - -![ActiveMQ Cluster](../../images/subcollectives-multiple-middleware.png) - -We'll be working with a small 52 node collective that you can see above, the -collective has machines in many data centers spread over 4 countries. There are -3 ActiveMQ servers connected in a mesh. - -Along with each ActiveMQ node is also a Puppet Master, Nagios instance and other -shared infrastructure components. - -An ideal setup for this network would be: - - * MCollective NRPE and Puppetd Agent on each of 52 servers - * Puppet Commander on each of the 3 ActiveMQ locations - * Nagios in each of the locations monitoring the machines in its region - * Regional traffic will be isolated and contained to the region as much as - possible - * Systems Administrators and Registration data retain the ability to target the - whole collective - -The problem with a single flat collective is that each of the 3 Puppet -Commanders will get a copy of all the traffic, even traffic they did not request -they will simply ignore the wrong traffic. The links between Europe and US will -see a large number of messages traveling over them. In a small 52 node traffic -this is managable but if each of the 4 locations had thousands of nodes the -situation will rapidly become untenable. - -It seems natural then to create a number of broadcast domains - subcollectives: - - * A global collective that each machines belongs to - * UK, DE, ZA and US collectives that contains just machines in those regions - * An EU collective that has UK, DE and ZA machines - -Visually this arrangement might look like the diagram below: - -![Subcollectives](../../images/subcollectives-collectives.png) - -Notice how subcollectives can span broker boundaries - our EU collective has nodes -that would connect to both the UK and DE brokers. - -We can now configure our Nagios and Puppet Commanders to communicate only to the -sub collectives and the traffic for these collectives will be contained -regionally. - -The graph below shows the impact of doing this, this is the US ActiveMQ instance -showing traffic before partitioning and after. You can see even on a small -network this can have a big impact. - -![Subcollectives](../../images/subcollectives-impact.png) - -## Configuring MCollective - -Configuring the partitioned collective above is fairly simple. We'll look at -one of the DE nodes for reference: - -{% highlight ini %} -collectives = mcollective,de_collective,eu_collective -main_collective = mcollective -{% endhighlight %} - -The _collectives_ directive tells the node all the collectives it should belong -to and the `main_collective` instructs Registration where to direct messages -to. - -## Testing - -Testing that it works is pretty simple, first we need a _client.cfg_ that -configures your client to talk to all the sub collectives: - -{% highlight ini %} -collectives = mcollective,uk_collective,us_collective,de_collective,eu_collective,us_collective,za_collective -main_collective = mcollective -{% endhighlight %} - -You can now test with _mco ping_: - -{% highlight console %} -$ mco ping -T us_collective -host1.us.my.net time=200.67 ms -host2.us.my.net time=241.30 ms -host3.us.my.net time=245.24 ms -host4.us.my.net time=275.42 ms -host5.us.my.net time=279.90 ms -host6.us.my.net time=283.61 ms -host7.us.my.net time=370.56 ms - - ----- ping statistics ---- -7 replies max: 370.56 min: 200.67 avg: 270.96 -{% endhighlight %} - -By specifying other collectives in the -T argument you should see the sub -collectives and if you do not specify anything you should see all machines. - -Clients don't need to know about all collectives, only the ones they intend -to communicate with. - -You can discover the list of known collectives and how many nodes are in each -using the _inventory_ application: - -{% highlight console %} -$ mco inventory --list-collectives - - * [ ==================================== ] 52 / 52 - - Collective Nodes - ========== ===== - za_collective 2 - us_collective 7 - uk_collective 19 - de_collective 24 - eu_collective 45 - mcollective 52 - - Total nodes: 52 - -{% endhighlight %} - -## Partitioning for Security - -Another possible advantage from subcollectives is security. While the SimpleRPC -framework has a security model that is aware of the topology the core network -layer does not. Even if you only give someone access to run SimpleRPC requests -against some machines they can still use _mco ping_ to discover other nodes on -your network. - -By creating a subcollective of just their nodes and restricting them on the -middleware level to just that collective you can effectively and completely -create a secure isolated zone that overlays your exiting network. - -These restrictions have to be configured on the middleware server, outside of MCollective itself. The method will vary based on the middleware you use; the suggestions below are for ActiveMQ, the main recommended middleware. - -### Identifying Subcollectives on ActiveMQ - -The ActiveMQ connector plugin identifies subcollectives with the **first segment** of every destination (topic or queue) name. - -So for direct node addressing, for example, the default `mcollective` collective would use the `mcollective.nodes` queue, and `uk_collective` would use the `uk_collective.nodes` queue. For the package agent, they would use the `mcollective.package.agent` and `uk_collective.package.agent` topics, respectively. - -This makes it easy to use ActiveMQ destination wildcards to control access to a given collective. - -### Per-Subcollective Authorization - -To control subcollective access, identify the set of topics and queues that collective will use, then use ActiveMQ's authorization rules to secure them. - -* [See the "Authorization" section of the ActiveMQ config reference][activemq_authorization] for details. -* The ["Detailed Restrictions"][activemq_detailed] example shows all of the topics and queues used by the default collective; you can copy/paste/modify these for a small number of collectives. -* The ["Detailed Restrictions with Multiple Subcollectives"][activemq_subcollectives] example uses a snippet of ERB template to manage any number of collectives. - -You must then [configure multiple ActiveMQ user accounts][activemq_authentication] for your site's admins. Give each user membership in the groups they'll need to manage their collectives. - -### Destination Filters in a Network of Brokers - -In a [network of brokers][ActiveMQClustering], you can also prevent propagation across the network of sub collective -traffic. See the ["Destination Filtering"][activemq_filtering] section of the ActiveMQ config reference for details. diff --git a/website/reference/development/ec2_demo.md b/website/reference/development/ec2_demo.md deleted file mode 100644 index 1b30b123..00000000 --- a/website/reference/development/ec2_demo.md +++ /dev/null @@ -1,104 +0,0 @@ ---- -layout: default -title: EC2 Demo Creation -toc: false ---- -[Bundling]: http://support.rightscale.com/12-Guides/01-RightScale_Dashboard_User_Guide/02-Clouds/01-EC2/08-EC2_Image_Locator/Register_an_AMI#Step_2.3a_Bundle_the_Instance -[Console]: https://console.aws.amazon.com/ec2 - -Things to improve in next build: - - * set _plugin.urltest.syslocation_ to the availability zone the AMI is running on to improve mc-urltest output - -## RightScale AMI -Start up ami _ami-efe4cf9b_ or a newer RightScale EC2 image - -## Packages Needed -Install the following RPMs: - -{% highlight console %} -facter -tanukiwrapper -activemq -mcollective -mcollective-client -mcollective-common -rubygem-stomp -rubygems -ruby-shadow -puppet -net-snmp-libs -lm_sensors -net-snmp -perl-Socket6 -nrpe -perl-Crypt-DES -perl-Digest-SHA1 -nagios-plugins -perl-Digest-HMAC -perl-Net-SNMP -xinetd -dialog -rubygem-rdialog -{% endhighlight %} - -Gram and install _passmakr-1.0.0.gem_ from _http://passmakr.googlecode.com/_ - -## File modifications -Most of the files needed are in SVN in the _ext/ec2demo_ directory. - -{% highlight console %} -. -|-- etc -| |-- activemq -| | `-- activemq.xml.templ -| |-- mcollective -| | |-- client.cfg.templ -| | `-- server.cfg.templ -| |-- nagios -| | |-- command-plugins.cfg -| | |-- nrpe.cfg -| | `-- nrpe.d -| | |-- check_disks.cfg -| | |-- check_load.cfg -| | |-- check_swap.cfg -| | |-- check_totalprocs.cfg -| | `-- check_zombieprocs.cfg -| |-- rc.d -| | `-- rc.local -| `-- xinetd.d -| `-- nrpe -|-- opt -| `-- rightscale -| `-- etc -| `-- motd-complete -|-- root -| `-- README.txt -`-- usr - |-- lib - | `-- ruby - | `-- site_ruby - | `-- 1.8 - | `-- facter - | `-- rightscale.rb - `-- local - |-- bin - | `-- start-mcollective-demo.rb - `-- etc - `-- mcollective-node.motd -{% endhighlight %} - -## Bundling Changes -Bundling up is based on [RightScale docs][bundling]. - -You need to copy your key, cert and have your credentials all into _/mnt_: - -{% highlight console %} -% cp /dev/null /root/.bash_history -% rm -rf /var/tmp/mcollective/ - -% ec2-bundle-vol -d /mnt -k pk-xx.pem -c cert-xx.pem -u 481328239245 -r i386 -% ec2-upload-bundle -b mcollective-041-demo -m image.manifest.xml -a xx -s xxx -{% endhighlight %} - -Now register the AMI in the [AWS console][Console] then make public after testing diff --git a/website/reference/development/releasetasks.md b/website/reference/development/releasetasks.md deleted file mode 100644 index f65ee222..00000000 --- a/website/reference/development/releasetasks.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -layout: default -title: Release Tasks -toc: false ---- - -Notes on what to do when we release - - * update Changelog - * update ReleaseNotes - * update rakefile for version - * update website news section - * tag release - * update release notes with release date - * build and release rpms - * build and release debs using ami-0db89079 - * send mail - * Announce on freshmeat - -## Building RPMs -Boot up an instance of the EC2 demo AMIs - -{% highlight console %} -# gem install rake -# git clone git://github.com/puppetlabs/marionette-collective.git -# cd marionette-collective -# git checkout 0.x.x -# rake rpm -{% endhighlight %} - -Copy the RPMs and test it. - -## Building debs -Boot up an instance of _ami-0db89079_ and do more or less the following: - -**Note: There's some bug, you might need to run _make deb_ twice to make it work** - -{% highlight console %} -# apt-get update -# apt-get install rake irb rdoc build-essential subversion devscripts dpatch cdbs rubygems git-core -# git clone git://github.com/puppetlabs/marionette-collective.git -# cd marionette-collective -# git checkout 0.x.x -# rake deb -{% endhighlight %} - -Copy do test installs on the machine make sure it looks fine and ship them off the AMI, shut it. Make sure to copy the source deb as well. diff --git a/website/reference/index.md b/website/reference/index.md deleted file mode 100644 index 270e2ccd..00000000 --- a/website/reference/index.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -layout: default -title: Users Documentation and Reference -toc: false ---- - -Index to basic users documentation. - -## Basic Operations and Configuration - - 1. [Getting Started](basic/gettingstarted.html) - 1. [Configuration Guide](basic/configuration.html) - 1. [Message Flow](basic/messageflow.html) - 1. [Network Partitioning using Subcollectives](basic/subcollectives.html) - 1. [Message Format](basic/messageformat.html) - -### User Interface - - 1. [Using MCollective Command Line Applications](basic/basic_cli_usage.html) - 1. [Node Reports](ui/nodereports.html) - 1. [CLI Filters](ui/filters.html) - -### Integration - - 1. [Using with Puppet](integration/puppet.html) - 1. [ActiveMQ Security](integration/activemq_security.html) - 1. [ActiveMQ SSL](integration/activemq_ssl.html) - 1. [ActiveMQ Clusters](integration/activemq_clusters.html) - 1. [ActiveMQ Connector](plugins/connector_activemq.html) - 1. [RabbitMQ Connector](plugins/connector_rabbitmq.html) - -### Plugin Types - - 1. [Data Plugins](plugins/data.html) - 1. [Discovery Plugins](plugins/discovery.html) - 1. [Result Aggregation Plugins](plugins/aggregate.html) - 1. [The single application plugin system](plugins/application.html) - 1. [Registration](plugins/registration.html) - 1. [Fact Source Plugins](plugins/facts.html) - 1. [Validator Plugins](plugins/validator.html) - -### Plugins - - 1. [The _rpcutil_ helper Agent](plugins/rpcutil.html) - 1. [AES+RSA based Security Plugin](plugins/security_aes.html) - 1. [OpenSSL based Security Plugin](plugins/security_ssl.html) - 1. [Stomp Connector](plugins/connector_stomp.html) - 1. [ActiveMQ Connector](plugins/connector_activemq.html) - 1. [RabbitMQ Connector](plugins/connector_rabbitmq.html) - 1. [User Contributed Plugins](plugin_directory/) - -### Development - - 1. [Release Tasks](development/releasetasks.html) - 1. [EC2 Demo Creation](development/ec2_demo.html) diff --git a/website/reference/integration/activemq_clusters.md b/website/reference/integration/activemq_clusters.md deleted file mode 100644 index 64092944..00000000 --- a/website/reference/integration/activemq_clusters.md +++ /dev/null @@ -1,56 +0,0 @@ ---- -layout: default -title: ActiveMQ Clustering -toc: false ---- -[MessageFlow]: /mcollective/reference/basic/messageflow.html -[NetworksOfBrokers]: http://activemq.apache.org/networks-of-brokers.html -[SampleConfig]: http://github.com/puppetlabs/marionette-collective/tree/master/ext/activemq/ -[activemq_network]: /mcollective/deploy/middleware/activemq.html#settings-for-networks-of-brokers - -Relying on existing middleware tools and not re-inventing the transport wheel ourselves means we can take advantage of a lot of the built in features they provide. One such feature is clustering in ActiveMQ that allows for highly scalable and flexible network layouts. - -We'll cover here how to build a multi data center network of ActiveMQ servers with a NOC, the NOC computers would not need to send any packets direct to the nodes they manage and thanks to our meta data based approach to addressing machines they do not even need to know IPs or hostnames. - -There is an example of a 3 node cluster in the [ext/activemq directory of the MCollective source][SampleConfig]. - -## Network Design - -### Network Layout - -![ActiveMQ Cluster](/mcollective/images/activemq-multi-locations.png) - -The diagram above shows our sample network, I am using the same techniques to put an ActiveMQ in each of 4 countries and then having local nodes communicate to in-country ActiveMQ nodes. - -* These are the terminals of your NOC staff, they run the client code, these could also be isolated web servers for running admin tools etc. -* Each location has its own instance of ActiveMQ and nodes would only need to communicate to their local ActiveMQ. This greatly enhances security in a setup like this. -* The ActiveMQ instances speak to each other using a protocol called OpenWire, you can run this over IPSec or you could use the native support for SSL. -* These are the servers being managed, they run the server code. No direct communications needs to be in place between NOC and managed servers. - -Refer to the [MessageFlow][] document to see how messages would traverse the middleware in a setup like this. - -### General Observations -The main design goal here is to promote network isolation, the staff in your NOC are often high churn people, you'll get replacement staff relatively often and it's a struggle to secure what information they carry and how and when you can trust them. - -Our model of using middleware and off-loading authentication and authorization onto the middleware layer means you only need to give NOC people appropriate access to the middleware and not to each individual machine. - -Our usage of meta data to address machines rather than hostnames or ip address means you do not need to divulge this information to NOC staff, from their point of view they access machines like this: - -* All machines in _datacenter=a_ -* AND all machines with puppet class _/webserver/_ -* AND all machines with Facter fact _customer=acmeinc_ - -In the end they can target the machines they need to target for maintenance commands as above without the need for any info about hostnames, ips, or even what/where data center A is. - -This model works particularly well in a Cloud environment where hostnames are dynamic and not under your control, in a cloud like Amazon S3 machines are better identified by what AMI they have booted and in what availability zones they are rather than what their hostnames are. - -## ActiveMQ Configuration - -ActiveMQ supports many types of cluster; we think their Network of Brokers model works best for MCollective. - -You will need to configure your ActiveMQ servers with everything from the ["Settings for Networks of Brokers" section of the ActiveMQ config reference][activemq_network]. Note the comments about the bi-directional connections: In the example network described above, you could either configure a pair of connectors on each datacenter broker to connect them to the NOC, or configure several pairs of connectors on the NOC broker to connect it to every datacenter. Do whichever makes sense for your own convenience and security needs. - -There is also a set of example config files in the [ext/activemq directory of the MCollective source][SampleConfig]; refer to these while reading the config reference. - -See [the ActiveMQ docs][NetworksOfBrokers] for more detailed info about networks of brokers. - diff --git a/website/reference/integration/activemq_security.md b/website/reference/integration/activemq_security.md deleted file mode 100644 index 39923eb6..00000000 --- a/website/reference/integration/activemq_security.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -layout: default -title: ActiveMQ Security -toc: false ---- - -[Security]: http://activemq.apache.org/security.html -[Wildcard]: http://activemq.apache.org/wildcards.html -[activemq_config]: /mcollective/deploy/middleware/activemq.html -[mcollective_username]: /mcollective/reference/plugins/connector_activemq.html#configuring-mcollective -[mcollective_tls]: ./activemq_ssl.html#step-2-configure-mcollective-servers - -As part of rolling out MCollective you need to think about security. The various examples in the quick start guide and on this blog has allowed all agents to talk to all nodes all agents. The problem with this approach is that should you have untrusted users on a node they can install the client applications and read the username/password from the server config file and thus control your entire architecture. - -The default format for message topics is compatible with [ActiveMQ wildcard patterns][Wildcard] and so we can now do fine grained controls over who can speak to what. - -General information about [ActiveMQ Security can be found on their wiki][Security]. - -## Configuring Security in activemq.xml - -[The ActiveMQ config reference][activemq_config] contains all relevant info for configuring security is activemq.xml. The most relevant sections are: - -* [Topic and Queue Names](/mcollective/deploy/middleware/activemq.html#topic-and-queue-names) --- Info about the destinations that MCollective uses. -* [Transport Connectors](/mcollective/deploy/middleware/activemq.html#transport-connectors) --- URL structure for insecure and TLS transports. -* [TLS Credentials](/mcollective/deploy/middleware/activemq.html#tls-credentials) --- For use with TLS transports. -* [Authentication](/mcollective/deploy/middleware/activemq.html#authentication-users-and-groups) --- Establishing user accounts and groups. -* [Authorization](/mcollective/deploy/middleware/activemq.html#authorization-group-permissions) --- Limiting access to destinations based on group membership. -* [Destination Filtering](/mcollective/deploy/middleware/activemq.html#destination-filtering) --- Preventing certain messages from crossing between datacenters. - - - -## Configuring Security in MCollective - -MCollective clients and servers need security credentials that line up with ActiveMQ's expectations. Specifically: - -* [An ActiveMQ username and password][mcollective_username] -* [TLS credentials, if necessary][mcollective_tls] - diff --git a/website/reference/integration/activemq_ssl.md b/website/reference/integration/activemq_ssl.md deleted file mode 100644 index ea9a90d0..00000000 --- a/website/reference/integration/activemq_ssl.md +++ /dev/null @@ -1,224 +0,0 @@ ---- -layout: default -title: ActiveMQ TLS -toc: false ---- - -[keystores]: /mcollective/deploy/middleware/activemq_keystores.html -[sslcontext]: /mcollective/deploy/middleware/activemq.html#tls-credentials -[transport]: /mcollective/deploy/middleware/activemq.html#transport-connectors - -[activemq_connector]: /mcollective/reference/plugins/connector_activemq.html -[ssl_security]: /mcollective/reference/plugins/security_ssl.html -[aes_security]: /mcollective/reference/plugins/security_aes.html - -You can configure MCollective and ActiveMQ to do end-to-end encryption over TLS. This allows you to use MCollective's fast and efficient [SSL security plugin][ssl_security] instead of the slow and hard to configure [AES security plugin][aes_security]. - -There are two main approaches: - -* [CA-verified TLS](#ca-verified-tls) encrypts traffic, and also lets you restrict middleware connections --- only nodes with valid certificates from the site's CA can connect. -* [Anonymous TLS](#anonymous-tls) encrypts messages to prevent casual traffic sniffing, and will prevent the passwords MCollective uses to connect to ActiveMQ from being stolen. However, it doesn't check whether nodes are allowed to connect, so you have to trust your username and password security. - -This feature requires: - -* MCollective 2.0.0 or newer -* ActiveMQ 5.5.0 or newer -* Stomp gem 1.2.2 or newer -* The [activemq connector][activemq_connector] plugin (included with MCollective 2.0.0 and newer) - -## CA-Verified TLS - -**(Recommended For Most Users)** - -### Summary - -This approach configures MCollective and ActiveMQ to only accept connections when the peer has a certificate signed by the site's CA. - -**Benefits:** - -This approach gives extra security, and your MCollective servers will generally already have the credentials they need since you can re-use Puppet certificates. - -**Drawbacks:** - -You will need to go out of your way to issue keys and certificates to your admin users, which is an extra step when onboarding a new admin. - - -### Step 1: Configure ActiveMQ to Use TLS - -Do the following steps to get ActiveMQ configured: - -* Follow [the ActiveMQ keystores guide][keystores] to create Java keystores for ActiveMQ. -* [Configure activemq.xml's `` element to point at the keystores.][sslcontext] -* [Configure the proper URIs in the broker's transport connectors.][transport] -* Restart ActiveMQ. - -At this point, MCollective servers and clients should be unable to connect to ActiveMQ, since they do not yet have credentials configured. - -### Step 2: Configure MCollective Servers - -For the MCollective daemon you can use your existing Puppet certificates by editing the _server.cfg_ file: - -{% highlight ini %} -connector = activemq -# Optional: -# plugin.activemq.base64 = yes -plugin.activemq.pool.size = 1 -plugin.activemq.pool.1.host = stomp.example.com -plugin.activemq.pool.1.port = 61614 -plugin.activemq.pool.1.user = mcollective -plugin.activemq.pool.1.password = secret -plugin.activemq.pool.1.ssl = true -plugin.activemq.pool.1.ssl.ca = /var/lib/puppet/ssl/ca/ca_crt.pem -plugin.activemq.pool.1.ssl.key = /var/lib/puppet/ssl/private_keys/.pem -plugin.activemq.pool.1.ssl.cert = /var/lib/puppet/ssl/certs/.pem -{% endhighlight %} - -* Set the correct paths to each node's private key and certificate; they will be named after the node's Puppet certname and located in the ssldir. - * You can locate a node's private key by running `sudo puppet agent --configprint hostprivkey`, the certificate with `sudo puppet agent --configprint hostcert`, and the CA certificate with `sudo puppet agent --configprint localcacert`. -* Set the correct username and password. - -### Step 3: Configure MCollective Clients - -Each client will now need a TLS certificate issued by the Puppet CA in order to be able to -connect to the ActiveMQ: - -{% highlight bash %} -# puppet cert generate ripienaar -notice: ripienaar has a waiting certificate request -notice: Signed certificate request for ripienaar -notice: Removing file Puppet::SSL::CertificateRequest ripienaar at '/var/lib/puppet/ssl/ca/requests/ripienaar.pem' -notice: Removing file Puppet::SSL::CertificateRequest ripienaar at '/var/lib/puppet/ssl/certificate_requests/ripienaar.pem' -{% endhighlight %} - -Copy the certificates to your user: - -{% highlight bash %} -# mkdir /home/rip/.mcollective.d -# cp /var/lib/puppet/ssl/ca/ca_crt.pem /home/rip/.mcollective.d/ -# cp /var/lib/puppet/ssl/private_keys/ripienaar.pem /home/rip/.mcollective.d/ripienaar-private.pem -# cp /var/lib/puppet/ssl/public_keys/ripienaar.pem /home/rip/.mcollective.d/ripienaar.pem -# cp /var/lib/puppet/ssl/certs/ripienaar.pem /home/rip/.mcollective.d/ripienaar-cert.pem -# chown -R rip:rip /home/rip/.mcollective.d -{% endhighlight %} - -You can now configure the mcollective client config in _/home/rip/.mcollective_ to use these: - -{% highlight ini %} -connector = activemq -# Optional: -# plugin.activemq.base64 = yes -plugin.activemq.pool.size = 1 -plugin.activemq.pool.1.host = stomp.example.com -plugin.activemq.pool.1.port = 61614 -plugin.activemq.pool.1.user = ripienaar -plugin.activemq.pool.1.password = secret -plugin.activemq.pool.1.ssl = true -plugin.activemq.pool.1.ssl.ca = /home/rip/.mcollective.d/ca_crt.pem -plugin.activemq.pool.1.ssl.key = /home/rip/.mcollective.d/ripienaar-private.pem -plugin.activemq.pool.1.ssl.cert = /home/rip/.mcollective.d/ripienaar-cert.pem -{% endhighlight %} - -If you are using the SSL security plugin, you can use these same files by setting `/home/rip/.mcollective.d/ripienaar.pem` as the public key. - -### Finish: Verify Encryption - -If you want to be sure of this, you should now verify with tcpdump or wireshark that the connection and traffic -really is all encrypted. - -### Troubleshooting Common Errors - -You will get some obvious errors from this code if any files are missing, but the errors from SSL validation will be pretty -hard to understand. - -There are two main scenarios where ActiveMQ will reject an MCollective conneciton: - -#### MCollective Uses Wrong CA to Verify ActiveMQ Cert - -When the client connects using a CA set in _plugin.activemq.pool.1.ssl.ca_ that does not match the one -in the ActiveMQ _truststore.jks_: - -{% highlight console %} -failed: SSL_connect returned=1 errno=0 state=SSLv3 read server certificate B: certificate verify failed -{% endhighlight %} - -And in the ActiveMQ log: - -{% highlight console %} -Transport failed: javax.net.ssl.SSLHandshakeException: Received fatal alert: unknown_ca -{% endhighlight %} - -#### MCollective Certs Aren't Signed by the Right CA - -When your client has the correct CA but his certificates are not signed by that CA: - -{% highlight console %} -failed: SSL_connect returned=1 errno=0 state=SSLv3 read finished A: sslv3 alert certificate unknown -{% endhighlight %} - -And in the ActiveMQ log: - -{% highlight console %} -sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target -{% endhighlight %} - - - - - -## Anonymous TLS - -**(Less Recommended)** - -### Summary - -This approach configures MCollective and ActiveMQ to encrypt traffic via TLS, but accept connections from anyone. - -**Benefits:** - -This approach requires less configuration, especially when adding new admin users. - -**Drawbacks:** - -This approach has some relative security drawbacks. Depending on your site's security needs, these may not concern you --- since MCollective's application-level security plugins will prevent an attacker from issuing agent commands and owning your servers, attacks like those below would only result in denial of service plus some leakage of inventory data via lower-level discovery protocols. - -* Although attackers can't sniff MCollective's ActiveMQ passwords, there's nothing to prevent them from logging in if they steal a password through some other means. (With CA-verified TLS, on the other hand, they would also require a private key and certificate, which provides some additional security depth.) -* An attacker able to run a man-in-the-middle attack at your site could fool your MCollective servers into trusting a malicious ActiveMQ server. - - -### Step 1: Configure ActiveMQ to Use Anonymous TLS - -* Follow [the ActiveMQ keystores guide][keystores] to create a Java keystore for ActiveMQ. _You can skip the truststore._ -* [Configure activemq.xml's `` element to point at the keystore.][sslcontext] _You can skip the `trustStore` and `trustStorePassword` attributes._ -* [Configure the proper URIs in the broker's transport connectors][transport], but _leave off the `?needClientAuth=true` portion._ -* Restart ActiveMQ. - - -### Step 2: Configure MCollective Servers and Clients - -Set the following settings in the `server.cfg` and `client.cfg` (or `~/.mcollective`) files: - -{% highlight ini %} -connector = activemq -# Optional: -# plugin.activemq.base64 = yes -plugin.activemq.pool.size = 1 -plugin.activemq.pool.1.host = stomp.example.com -plugin.activemq.pool.1.port = 61614 -plugin.activemq.pool.1.user = mcollective -plugin.activemq.pool.1.password = secret -plugin.activemq.pool.1.ssl = true -plugin.activemq.pool.1.ssl.fallback = true -{% endhighlight %} - -The `plugin.activemq.pool.1.ssl.fallback` setting tells the plugin that it is allowed to: - -* Connect to an unverified server -* Connect without presenting its own SSL credentials - -...if it is missing any of the `.ca` `.cert` or `.key` settings or cannot find the files they reference. If the settings _are_ present (and point to correct files), MCollective will try to do a verified connection. - - -### Finish: Verify Encryption - -If you want to be sure of this, you should now verify with tcpdump or wireshark that the connection and traffic -really is all encrypted. diff --git a/website/reference/integration/puppet.md b/website/reference/integration/puppet.md deleted file mode 100644 index 60e6ea9f..00000000 --- a/website/reference/integration/puppet.md +++ /dev/null @@ -1,74 +0,0 @@ ---- -layout: default -title: Using with Puppet -toc: false ---- -[FactsRLFacter]: https://docs.puppetlabs.com/mcollective/plugin_directory/facter_via_yaml.html -[PluginSync]: http://docs.reductivelabs.com/guides/plugins_in_modules.html -[AgentPuppetd]: https://docs.puppetlabs.com/mcollective/plugin_directory/puppet_agent.html -[AgentPuppetca]: http://github.com/puppetlabs/mcollective-plugins/tree/master/agent/puppetca/ -[AgentPuppetRal]: http://github.com/puppetlabs/mcollective-plugins/tree/master/agent/puppetral/ -[PuppetCommander]: http://github.com/puppetlabs/mcollective-plugins/tree/master/agent/puppetd/commander/ -[RapidRuns]: http://www.devco.net/archives/2010/08/05/rapid_puppet_runs_with_mcollective.php -[EC2Bootstrap]: http://www.devco.net/archives/2010/07/14/bootstrapping_puppet_on_ec2_with_mcollective.php -[PuppetRalBlog]: http://www.devco.net/archives/2010/07/07/puppet_resources_on_demand.php -[SchedulingPuppet]: http://www.devco.net/archives/2010/03/17/scheduling_puppet_with_mcollective.php -[ManagingPuppetd]: http://www.devco.net/archives/2009/11/30/managing_puppetd_with_mcollective.php -[CloudBootstrap]: http://vuksan.com/blog/2010/07/28/bootstraping-your-cloud-environment-with-puppet-and-mcollective/ -[ServiceAgent]: https://docs.puppetlabs.com/mcollective/plugin_directory/services.html -[PackageAgent]: https://docs.puppetlabs.com/mcollective/plugin_directory/package.html -[Facter2YAML]: http://www.semicomplete.com/blog/geekery/puppet-facts-into-mcollective.html - -If you're a Puppet user you are supported in both facts and classes filters. - -There are a number of community plugins related to Puppet: - - * Manage the Puppetd, request runs, enable and disable - [AgentPuppetd][] - * Manage the Puppet CA, sign, list and revoke certificates - [AgentPuppetca][] - * Use the Puppet Ral to create resources on demand, a distributed *ralsh* - [AgentPuppetRal][] - * Schedule your puppetd's controlling concurrency and resource usage - [PuppetCommander][] - * The [ServiceAgent][] and [PackageAgent][] use the Puppet RAL to function on many operating systems - -There are also several blog posts related to Puppet and MCollective: - - * Running puppet on a number of nodes as quick as possible - [RapidRuns][] - * Bootstrapping a Puppet + EC2 environment with Puppet - [EC2Bootstrap][] - * Using the Puppet RAL with MCollective - [PuppetRalBlog][] - * General scheduling of Puppet Runs - [SchedulingPuppet][] - * Managing Puppetd - [ManagingPuppetd][] - * Bootstrapping your cloud environment - [CloudBootstrap][] - -## Facts -There is a [community plugin to enable Facter][FactsRLFacter] as a fact source. - -So you can use the facts provided by Facter in filters, reports etc. - -{% highlight console %} -$ mco find --with-fact lsbdistrelease=5.4 -{% endhighlight %} - -This includes facts pushed out with [Plugin Sync][PluginSync]. - -A less resource intensive approach has can be found [here][Facter2YAML], it converts the Puppet scope into a YAML file that the YAML fact source then loads. This is both less resource intensive and much faster. - -## Class Filters -Puppet provides a list of classes applied to a node by default in */var/puppet/state/classes.txt* or */var/lib/puppet/state/classes.txt* (depending on which Puppet version you are using. The latter is true for 0.25.5 onwards) , we'll use this data with *--with-class* filters. - -You should configure MCollective to use this file by putting the following in your *server.cfg* - - -{% highlight ini %} -classesfile = /var/lib/puppet/classes.txt -{% endhighlight %} - -or if using Puppet 0.23.0 and onwards - -{% highlight ini %} -classesfile = /var/lib/puppet/state/classes.txt -{% endhighlight %} - -You can now use your classes lists in filters: - -{% highlight console %} -$ mco find --with-class /apache/ -{% endhighlight %} diff --git a/website/reference/plugins/aggregate.md b/website/reference/plugins/aggregate.md deleted file mode 100644 index c4158d72..00000000 --- a/website/reference/plugins/aggregate.md +++ /dev/null @@ -1,367 +0,0 @@ ---- -layout: default -title: Aggregate Plugins ---- -[DDL]: /mcollective/reference/plugins/ddl.html -[Examples]: https://github.com/puppetlabs/marionette-collective/tree/master/lib/mcollective/aggregate - -## Overview -MCollective Agents return data and we try to provide as much usable user -interface for free. To aid in this we require agents to have [DDL][] files that -describe the data that the agent returns. - -DDL files are used to configure the client but also to assist with user -interface generation. They are used to ask questions that an action needs but -also to render the results when the replies come in. For example we turn -*:freecpu* into "Free CPU" when displaying the data based on the DDL. - -Previously if data that agents returned required any summarization this had to -be done using a custom application. Here is an example from *mco nrpe*: - -{% highlight console %} -% mco nrpe check_load -. -. -Finished processing 25 / 25 hosts in 556.48 ms - - OK: 25 - WARNING: 0 - CRITICAL: 0 - UNKNOWN: 0 -{% endhighlight %} - -Here to get the summary of results displayed in a way that has contextual -relevance to the nrpe plugin a custom application had to be written and anyone -who interacts with the agent using other RPC clients would not get the benefit -of this summary. - -By using aggregate plugins and updating the DDL we can now provide such a -summary in all result sets and display it using the *mco rpc* application and -any calls to *printrpc*. - -{% highlight console %} -% mco rpc nrpe runcommand command=check_load -Discovering hosts using the mongo method .... 25 - - * [============================================================> ] 25 / 25 - -Summary of Exit Code: - - OK : 25 - WARNING : 0 - UNKNOWN : 0 - CRITICAL : 0 - - -Finished processing 25 / 25 hosts in 390.70 ms -{% endhighlight %} - -Here you get a similar summary as before, all that had to be done was a simple -aggregate plugin be written and distributed with your clients. - -The results are shown as above using *printrpcstats* but you can also get access to -the raw data so you can decide to render it in some other way - perhaps using a -graph on a web interface. - -We provide a number of aggregate plugins with MCollective and anyone can write -more. - -For examples that already use functions see the *rpcutil* agent - its -*collective_info*, *get_fact*, *daemon_stats* and *get_config_item* actions all -have summaries applied. - -*NOTE:* This feature is available since version 2.1.0 - -## Using existing plugins - -### Updating the DDL -At present MCollective supplies 3 plugins *average()*, *summary()* and *sum()* -you can use these in any agent, here is an example from the *rpcutil* agent DDL -file: - -{% highlight ruby %} -action "get_config_item", :description => "Get the active value of a specific config property" do - output :value, - :description => "The value that is in use", - :display_as => "Value" - - summarize do - aggregate summary(:value) - end -end -{% endhighlight %} - -We've removed a few lines from this example DDL block leaving only the relevant -lines. You can see the agent outputs data called *:value* and we reference that -output in the summary function *summary(:value)*, the result would look like -this: - -### Viewing summaries on the CLI -{% highlight console %} -% mco rpc rpcutil get_config_item item=collectives -. -. -dev8 - Property: collectives - Value: ["mcollective", "uk_collective"] - -Summary of Value: - - mcollective = 25 - uk_collective = 15 - fr_collective = 9 - us_collective = 1 - -Finished processing 25 / 25 hosts in 349.70 ms -{% endhighlight %} - -You can see that the value in this case contains arrays, the *summary()* -function produce the table in the output showing the data distribution. - -### Producing summaries in your own clients -You can enable the same display in your own code, here is ruby code that has the -same affect as the CLI call above: - -{% highlight ruby %} -require 'mcollective' - -include MCollective::RPC - -c = rpcclient("rpcutil") - -printrpc c.get_config_item(:item => "collectives") - -printrpcstats :summarize => true -{% endhighlight %} - -Without passing in the *:summarize => true* you would not see the summaries - -### Getting access to the raw summary results -If you wanted to do something else entirely like produce a graph on a web page -of the summaries you can get access to the raw data, here's some ruby code to -show all computed summaries: - -{% highlight ruby %} -require 'mcollective' - -include MCollective::RPC - -c = rpcclient("rpcutil") -c.progress = false - -c.get_config_item(:item => "collectives") - -c.stats.aggregate_summary.each do |summary| - puts "Summary of type: %s" % summary.result_type - puts "Display format: '%s'" % summary.aggregate_format - puts - pp summary.result -end -{% endhighlight %} - -As you can see you will get an array of summaries this is because each DDL can -use many aggregate calls, this would be an array of all the computed summaries: - -{% highlight console %} -Summary of type: collection -Display format: '%13s = %s' - -{:type=>:collection, - :value=> - {"mcollective"=>25, - "fr_collective"=>9, - "us_collective"=>1, - "uk_collective"=>15}, - :output=>:value} -{% endhighlight %} - -There are 2 types of result *:collection* and *:numeric*, in the case of numeric -results the :value would just be a number. - -The *aggregate_format* is either a user supplied format or a dynamically -computed format to display the summary results on the console. In this case -each pair of the hash should be displayed using the format to produce a nice -right justified list of keys and values. - -## Writing your own function -We'll cover writing your own function by looking at the Nagios one from earlier -in this example. You can look at [the functions supplied with -MCollective][Examples] for more examples using other types than the one below. - -First lets look at the DDL for the existing *nrpe* Agent: - -{% highlight ruby %} -action "runcommand", :description => "Run a NRPE command" do - input :command, - :prompt => "Command", - :description => "NRPE command to run", - :type => :string, - :validation => '\A[a-zA-Z0-9_-]+\z', - :optional => false, - :maxlength => 50 - - output :output, - :description => "Output from the Nagios plugin", - :display_as => "Output", - :default => "" - - output :exitcode, - :description => "Exit Code from the Nagios plugin", - :display_as => "Exit Code", - :default => 3 - - output :perfdata, - :description => "Performance Data from the Nagios plugin", - :display_as => "Performance Data", - :default => "" -end -{% endhighlight %} - -You can see it will return an *:exitcode* item and from the default value you -can gather this is going to be a number. Nagios defines 4 possibly exit codes -for a Nagios plugin and we need to convert this *:exitcode* into a string like -WARNING, CRITICAL, UNKNOWN or OK. - -Usually when writing any kind of summarizer for an array of results your code -might contain 3 phases. - -Given a series of Nagios results like this: - -{% highlight ruby %} -[ - {:exitcode => 0, :output => "OK", :perfdata => ""}, - {:exitcode => 2, :output => "failure", :perfdata => ""} -] -{% endhighlight %} - -You would write a nagios_states() function that does roughly this: - -{% highlight ruby %} -def nagios_states(results) - # set initial values - result = {} - status_map = ["OK", "WARNING", "CRITICAL", "UNKNOWN"] - status_map.each {|s| result[s] = 0} - - # loop over all the data, increment the count for OK etc - results.each do |result| - status = status_map[result[:exitcode]] - result[status] += 1 - end - - # return the result hash, {"OK" => 1, "CRITICAL" => 1, "WARN" => 0, "UNKNOWN" => 0} - return result -end -{% endhighlight %} - -You could optimise the code but you can see there are 3 major stages in the life -of this code. - - * Set initial values for the return data - * Loop the data building up the state - * Return the data. - -Given this, here is our Nagios exitcode summary function, it is roughly the same -code with a bit more boiler plate to plugin into mcollective, but the same code -can be seen: - -{% highlight ruby %} -module MCollective - class Aggregate - class Nagios_states "Run a NRPE command" do - . - . - . - - if respond_to?(:summarize) - summarize do - aggregate nagios_states(:exitcode) - end - end -end -{% endhighlight %} - -Add the last few lines - we check that we're running in a version of MCollective -that supports this feature and then we call our function with the *:exitcode* -results. diff --git a/website/reference/plugins/application.md b/website/reference/plugins/application.md deleted file mode 100644 index 3c424954..00000000 --- a/website/reference/plugins/application.md +++ /dev/null @@ -1,308 +0,0 @@ ---- -layout: default -title: Single Executable Application Plugin ---- -[Clients]: ../../simplerpc/clients.html -[SimpleRPCAgents]: ../../simplerpc/agents.html - - -## Overview -The Marionette Collective 1.1.1 and newer supports a single executable - called -mco - and have a plugin type called application that lets you create -applications for this single executable. - -In the past we tended to write small standalone scripts to interact with -MCollective, this had a number of issues: - - * Large number of executables in _/usr/sbin_ - * Applications behave inconsistently with regard to error handling and reporting - * Discovering new applications is difficult since they are all over the filesystem - * Installation and packaging of plugins is complex - -We've attempted to address these concerns by creating a single point of access -for all applications - the _mco_ script - with unified help, error reporting and -option parsing. - -Below you can see the single executable system in use: - -{% highlight console %} -The Marionette Collective version 2.0.0 - -usage: /usr/bin/mco: command - -Known commands: - - cap controller exim - facts filemgr find - help inventory iptables - nettest nrpe package - pgrep ping plugin - puppetd rpc service - virt - -Type 'mco help' for a detailed list of commands and 'mco help command' -to get detailed help for a command - -{% endhighlight %} - -{% highlight console %} -$ mco help -The Marionette Collection version 2.0.0 - - facts Reports on usage for a specific fact - filemgr Generic File Manager Client - find Find hosts matching criteria - help Application list and RPC agent help - inventory Shows an inventory for a given node - ping Ping all nodes - rpc Generic RPC agent client application -{% endhighlight %} - -{% highlight console %} -$ mco rpc package status package=zsh -Determining the amount of hosts matching filter for 2 seconds .... 51 - - * [ ============================================================> ] 51 / 51 - - - test.com: - Properties: - {:provider=>:yum, - :release=>"3.el5", - :arch=>"x86_64", - :version=>"4.2.6", - :epoch=>"0", - :name=>"zsh", - :ensure=>"4.2.6-3.el5"} -{% endhighlight %} - -These applications are equivalent to the old mc-rpc and similar applications but without the problem of lots of files in _/usr/sbin_. - -## Basic Application -Applications goes in _libdir/mcollective/application/echo.rb_, the one below is a simple application that speaks to a hypothetical _echo_ action of a _helloworld_ agent. This agent has been demonstrated in : writing [agents][SimpleRPCAgents]. - -{% highlight ruby %} -class MCollective::Application::Echo "Message to send", - :arguments => ["-m", "--message MESSAGE"], - :type => String, - :required => true - - def main - mc = rpcclient("helloworld") - - printrpc mc.echo(:msg => configuration[:message], :options => options) - - printrpcstats - end -end -{% endhighlight %} - -Here's the application we wrote in action: - -{% highlight console %} -$ mco echo -The message option is mandatory - -Please run with --help for detailed help -{% endhighlight %} - -{% highlight console %} -$ mco echo -m test - - * [ ============================================================> ] 1 / 1 - - -example.com - Message: test - Time: Mon Jan 31 21:27:03 +0000 2011 - - -Finished processing 1 / 1 hosts in 68.53 ms -{% endhighlight %} - -Most of the techniques documented in SimpleRPC [Clients][] can be reused here, we've just simplified a lot of the common used patterns like CLI arguments and incorporated it all in a single framework. - -## Reference - -### Usage Messages - -To add custom usage messages to your application we can add lines like this: - -{% highlight ruby %} -class MCollective::Application::Echo "Message to send", - :arguments => ["-m", "--message MESSAGE"] -end -{% endhighlight %} - -In this case if the user used either `-m message` or `--message message` on the CLI the desired message would be in `configuration[:message]` - -#### Required Arguments -You can require that a certain parameter is always passed: - -{% highlight ruby %} -option :message, - :description => "Message to send", - :arguments => ["-m", "--message MESSAGE"], - :required => true -{% endhighlight %} - -#### Argument data types -CLI arguments can be forced to a specific type, we also have some additional special types that the default ruby option parser cant handle on its own. - -You can force data to be of type String, Integer etc: - -{% highlight ruby %} -option :count, - :description => "Count", - :arguments => ["--count MESSAGE"], - :type => Integer -{% endhighlight %} - -You can force an argument to be boolean: - -{% highlight ruby %} -option :detail, - :description => "Detailed view", - :arguments => ["--detail"], - :type => :bool -{% endhighlight %} - -If you have an argument that can be called many times you can force that to build an array: - -{% highlight ruby %} -option :args, - :description => "Arguments", - :arguments => ["--argument ARG"], - :type => :array -{% endhighlight %} - -Here if you supplied multiple arguments `configuration[:args]` will be an array with all the options supplied. - -#### Argument validation -You can validate input passed on the CLI: - -{% highlight ruby %} -option :count, - :description => "Count", - :arguments => ["--count MESSAGE"], - :type => Integer, - :validate => Proc.new {|val| val < 10 ? true : "The message count has to be below 10" } -{% endhighlight %} - -Should the supplied value be 10 or more a error message will be displayed. - -#### Disabling standard sections of arguments -By default every Application get all the RPC options enabling filtering, discovery etc. In some cases this is undesirable so we let users disable those. - -{% highlight ruby %} -class MCollective::Application::Echo "Hello World", :options => options) - - printrpcstats - - halt mc.stats - end -end -{% endhighlight %} - -As you can see you pass the _halt_ helper an instance of the RPC Client statistics and it will then -use that to do the right exit code. - diff --git a/website/reference/plugins/connector_activemq.md b/website/reference/plugins/connector_activemq.md deleted file mode 100644 index c1e95fab..00000000 --- a/website/reference/plugins/connector_activemq.md +++ /dev/null @@ -1,319 +0,0 @@ ---- -layout: default -title: ActiveMQ Connector -toc: false ---- - -[STOMP]: http://stomp.codehaus.org/ -[heartbeat]: http://stomp.github.io/stomp-specification-1.1.html#Heart-beating -[cipherstrings]: https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS -[wildcard]: http://activemq.apache.org/wildcards.html -[subcollectives]: /mcollective/reference/basic/subcollectives.html -[activemq_config]: /mcollective/deploy/middleware/activemq.html - - -The ActiveMQ connector uses the [STOMP][] rubygem to connect to ActiveMQ servers. It is specifically optimized for ActiveMQ -and uses features in ActiveMQ 5.5.0 and later. - -This plugin requires version _1.2.2_ or newer of the Stomp gem. - -## Differences between ActiveMQ connector and Stomp Connector - -### Topic and Queue Names - -The new connector uses different destination names from the old stomp connector. - -MCollective uses the following destination names. This list uses standard [ActiveMQ destination wildcards][wildcard]. "COLLECTIVE" is the name of the collective being used; by default, this is `mcollective`, but if you are using [subcollectives][], each one is implemented as an equal peer of the default collective. - -Topics: - -- `ActiveMQ.Advisory.>` (built-in topics that all ActiveMQ producers and consumers need all permissions on) -- `COLLECTIVE.*.agent` (for each agent plugin, where the `*` is the name of the plugin) - -Queues: - -- `COLLECTIVE.nodes` (used for direct addressing; this is a single destination that uses JMS selectors, rather than a group of destinations) -- `COLLECTIVE.reply.>` (where the continued portion is a request ID) - -Note especially that: - -* We can now do direct addressing to specific nodes. -* Replies now go directly to the instigating client instead of being brodcast on a topic. - -This has big impact on overall CPU usage by clients on busy networks, and also optimizes the traffic flow on -networks with many brokers. - - -## Configuring ActiveMQ - -See [the ActiveMQ config reference][activemq_config] for details on configuring ActiveMQ for this connector. As recommended at the top of the reference, you should skim the sections you care about and edit an example config file while reading. - - -## Configuring MCollective - -MCollective clients and servers use the same connector settings, although the value of settings involving credentials will vary. - -### Failover Pools - -A sample configuration can be seen below. Note this plugin does not support the old style config of the Stomp connector. - -{% highlight ini %} -connector = activemq -plugin.activemq.pool.size = 2 -plugin.activemq.pool.1.host = stomp1 -plugin.activemq.pool.1.port = 61613 -plugin.activemq.pool.1.user = me -plugin.activemq.pool.1.password = secret - -plugin.activemq.pool.2.host = stomp2 -plugin.activemq.pool.2.port = 61613 -plugin.activemq.pool.2.user = me -plugin.activemq.pool.2.password = secret -{% endhighlight %} - -This gives it 2 servers to attempt to connect to, if the first one fails it will use the second. Usernames and passwords can be set -with the environment variables `STOMP_USER`, `STOMP_PASSWORD`. - -If you do not specify a port it will default to _61613_ - -You can also specify the following options for the Stomp gem, these are the defaults in the Stomp gem: - -{% highlight ini %} -plugin.activemq.initial_reconnect_delay = 0.01 -plugin.activemq.max_reconnect_delay = 30.0 -plugin.activemq.use_exponential_back_off = true -plugin.activemq.back_off_multiplier = 2 -plugin.activemq.max_reconnect_attempts = 0 -plugin.activemq.randomize = false -plugin.activemq.connect_timeout = 30 -{% endhighlight %} - -### Message Priority - -ActiveMQ messages support priorities, you can pass in the needed priority header by setting: - -{% highlight ini %} -plugin.activemq.priority = 4 -{% endhighlight %} - -### STOMP 1.1 Heartbeats - -A common problem is that idle STOMP connections get expired by session -tracking firewalls and NAT devices. Version 1.1 of the STOMP protocol -combats this with protocol level heartbeats, which can be configured -with these settings: - -{% highlight ini %} -# Send heartbeats in 30 second intervals. This is the shortest supported period. -plugin.activemq.heartbeat_interval = 30 - -# By default if heartbeat_interval is set it will request STOMP 1.1 but support fallback -# to 1.0, but you can enable strict STOMP 1.1 only operation by disabling 1.0 fallback -plugin.activemq.stomp_1_0_fallback = 0 - -# Maximum amount of heartbeat read failures before retrying. 0 means never retry. -plugin.activemq.max_hbread_fails = 2 - -# Maximum amount of heartbeat lock obtain failures before retrying. 0 means never retry. -plugin.activemq.max_hbrlck_fails = 0 -{% endhighlight %} - -This feature is avaiable from version 2.4.0 and requires version -1.2.10 or newer of the stomp gem. - -More information about STOMP heartbeats can be found [in the STOMP specification][heartbeat] - -### Parameter reference - -#### `plugin.activemq.connect_timeout` - -Specify the timeout for the TCP+SSL connection to the middleware. - -- _Default:_ 30 -- _Allowed values:_ Any integer - -#### `plugin.activemq.use_exponential_back_off` - -Whether to use exponential backoff when reconnecting to the -middleware. - -- _Default:_ true -- _Allowed values:_ A boolean value - -#### `plugin.activemq.initial_reconnect_delay` - -When `use_exponential_back_off` is set, the initial delay to use when -reconnecting to the middleware. - -- _Default:_ 0.01 -- _Allowed values:_ A positive number expressing time in seconds - -#### `plugin.activemq.max_reconnect_delay` - -When `use_exponential_back_off` is set, the maximum delay to use when -reconnecting to the middleware. - -- _Default:_ 30 -- _Allowed values:_ A number integer expressing time in seconds - -#### `plugin.activemq.back_off_multiplier` - -When `use_exponential_back_off` is set, the amount to increase the -delay by (up to `max_reconnect_delay`). - -- _Default:_ 2 -- _Allowed values:_ A positive integer - -#### `plugin.activemq.max_reconnect_attempts` - -The number of times to attempt to connect to the middleware. 0 means -no limit (retry forever). - -- _Default:_ 0 -- _Allowed values:_ Any integer - -#### `plugin.activemq.heartbeat_interval` - -Setting this value enables STOMP 1.1 heartbeats, and sets the interval -to send/receive heartbeat messages to that number of seconds. - -- _Default:_ (no value) -- _Allowed values:_ An integer >= 30 (smaller values will be padded) - -#### `plugin.activemq.stomp_1_0_fallback` - -When `heartbeat_interval` is set it will request STOMP 1.1 but support fallback -to 1.0. You can force STOMP 1.1 only operation by setting this to false. - -- _Default:_ false -- _Allowed values:_ A boolean - -#### `plugin.activemq.max_hbread_fails` - -Maximum amount of heartbeat read failures to allow before assuming the -connection is dead and reconnecting. - -- _Default:_ 2 -- _Allowed values:_ Any integer - -#### `plugin.activemq.max_hbrlck_fails` - -Maximum amount of heartbeat lock obtain failures before assuming the -connection is dead and reconnecting. This setting is best left at 0 -due to MCollective's usage patterns. - -- _Default:_ 0 -- _Allowed values:_ Any integer - -#### `plugin.activemq.priority` - -Specifies the priority of the messages sent to ActiveMQ. 1 is the -lowest priority, 9 is the highest, and unspecified is the same as the -default value (4). - -- _Default:_ no default -- _Allowed values:_ An integer in the range 1..9 - -#### `plugin.activemq.randomize` - -Whether to randomize the order of the connection pool before connecting. - -- _Default:_ false -- _Allowed values:_ A boolean value - -#### `plugin.activemq.pool.size` - -Specifies the size of the connector pool. - -- _Default:_ no default -- _Allowed values:_ Any positive integer - -#### `plugin.activemq.pool.1.host` - -The hostname of this middleware server. - -- _Default:_ no default -- _Allowed values:_ Any string value - -#### `plugin.activemq.pool.1.port` - -The port number to connect to for this middleware server. - -- _Default:_ 61613 -- _Allowed values:_ Any positive integer - -#### `plugin.activemq.pool.1.user` - -The username to connect with to this middleware server. If the -`STOMP_USER` environment variable is set this value will be used -instead. - -- _Default:_ The empty string "" -- _Allowed values:_ Any string value - -#### `plugin.activemq.pool.1.password` - -The password to connect with to this middleware server. If the -`STOMP_PASSWORD` environment variable is set this value will be used -instead. - -- _Default:_ The empty string "" -- _Allowed values:_ Any string value - -#### `plugin.activemq.pool.1.ssl` - -Whether to use TLS when connecting to this middleware server. - -- _Default:_ false -- _Allowed values:_ Any boolean value - -#### `plugin.activemq.pool.ssl.fallback` - -Whether to allow unverified TLS if the ca/cert/key settings aren't set. - -- _Default:_ false -- _Allowed values:_ Any boolean value - -#### `plugin.activemq.pool.1.ssl.ca` - -The CA certificate to use when verifying the middlewares -certificate. Must be the fully-qualified path to a `.pem` file. - -- _Default:_ (nothing) -- _Allowed values:_ A fully-qualified path - -#### `plugin.activemq.pool.1.ssl.cert` - -The certificate to present when connecting to the middleware. Must be -the fully-qualified path to a `.pem` file. MCollective will also -check the environment variable `MCOLLECTIVE_ACTIVEMQ_POOL1_SSL_CERT` -for the client's ssl cert. - -- _Default:_ (nothing) -- _Allowed values:_ A fully-qualified path - -#### `plugin.activemq.pool.1.ssl.key` - -The private key corresponding to this node's certificate. Must be the -fully-qualified path to a `.pem` file. MCollective will also check -the environment variable `MCOLLECTIVE_ACTIVEMQ_POOL1_SSL_KEY` for the -client's ssl key. - -- _Default:_ (nothing) -- _Allowed values:_ A fully-qualified path - -#### `plugin.activemq.pool.1.ssl.ciphers` - -The SSL ciphers to use when communicating with this middleware server. - -- _Default:_ no default -- _Allowed values:_ A string supplying an [OpenSSL cipher suite][cipherstrings] - -#### `plugin.activemq.agents_multiplex` - -Whether to use a single target for all agents in a node. This is an optimization that may make sense when running collectives with several thousands of nodes in order to reduce the number of subscriptions in the message broker. -This is a trade-off between increasing network traffic by delivering messages to all nodes - and letting them select messages they care about - versus increasing work in the message broker to handle large numbers of subscriptions. - -- _Default:_ false -- _Allowed values:_ A boolean value - diff --git a/website/reference/plugins/connector_rabbitmq.md b/website/reference/plugins/connector_rabbitmq.md deleted file mode 100644 index 9704a5e8..00000000 --- a/website/reference/plugins/connector_rabbitmq.md +++ /dev/null @@ -1,344 +0,0 @@ ---- -layout: default -title: RabbitMQ Connector -toc: false ---- -[STOMP]: http://stomp.codehaus.org/ -[heartbeat]: http://stomp.github.io/stomp-specification-1.1.html#Heart-beating -[RabbitStomp]: http://www.rabbitmq.com/stomp.html -[RabbitCLI]: http://www.rabbitmq.com/management-cli.html -[RabbitClustering]: https://www.rabbitmq.com/clustering.html -[cipherstrings]: https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - -The RabbitMQ connector uses the [STOMP][] rubygem to connect to RabbitMQ servers. - -This code will only work with version _1.2.2_ or newer of the Stomp gem. - -## Differences between RabbitMQ connector and Stomp Connector - -The RabbitMQ connector requires MCollective 2.0.0 or newer. - -While this plugin still uses the Stomp protocol to connect to RabbitMQ it does use a nubmer of -RabbitMQ specific optimizations to work well and as such is a Stomp connector specific to the -RabbitMQ broker. - -## Configuring RabbitMQ - -Basic installation of the RabbitMQ broker is out of scope for this document apart from the -basic broker you need to enable the [Stomp plugin][RabbitStomp] and the [CLI Management Tool][RabbitCLI]. - -With that in place you need to create a few exchanges, topics and queues for each of your -sub collectives. - -First we create a virtual host, two users (one to act as an administrator who -will create the exchanges we need later) and some permissions on the vhost: - -{% highlight console %} -rabbitmqadmin declare vhost name=/mcollective -rabbitmqadmin declare user name=mcollective password=changeme tags= -rabbitmqadmin declare user name=admin password=changeme tags=administrator -rabbitmqadmin declare permission vhost=/mcollective user=mcollective configure='.*' write='.*' read='.*' -rabbitmqadmin declare permission vhost=/mcollective user=admin configure='.*' write='.*' read='.*' -{% endhighlight %} - -(Note that a `tags=` declaration may be required for the mcollective user, although it's allowed to be empty. Also note that your shell probably requires quotes to protect the `*` from glob expansion.) - -And then we need to create the exchanges that are needed for each collective: - -{% highlight console %} -for collective in mcollective ; do - rabbitmqadmin declare exchange --user=admin --password=changeme --vhost=/mcollective name=${collective}_broadcast type=topic - rabbitmqadmin declare exchange --user=admin --password=changeme --vhost=/mcollective name=${collective}_directed type=direct -done -{% endhighlight %} - -### Clustering -If you want to run multiple RabbitMQ's, say one per datacenter perhaps, you'll need to set them up as a cluster. If you don't you'll only receive the replies from the RabbitMQ that the broker you're talking to is connected to, instead of the whole network. Effectively, if you don't cluster you create a split-brain situation. - -If you're using the [puppetlabs-mcollective](https://github.com/puppetlabs/puppetlabs-mcollective) module see its documentation on how to configure RabbitMQ for clustering. Otherwise you'll have to make the following changes yourself: - -* Shutdown the RabbitMQ nodes; -* Modify the Erlang cookie at ``/var/lib/rabbitmq/.erlang.cookie``. It needs to be identical for all the nodes in a cluster; -* Wipe the database: ``rm -rf /var/lib/rabbitmq/mnesia``; -* Add an entry to ``/etc/rabbitmq/rabbitmq.config`` in the ``rabit`` section: - {% highlight erlang %} - {cluster_nodes, {['rabbit@rabbitmq1.example.com', 'rabbit2@rabbitmq2.example.com'], disc}}, - {cluster_partition_handling, ignore}, - {% endhighlight %} -* Start up the nodes. - -Once these configuration changes are made you still need to join the nodes together. To do this follow the instructions on clustering [here][RabbitClustering]. - -## Configuring MCollective - -### Common Options - -### Failover Pools -A sample configuration can be seen below. - -{% highlight ini %} -direct_addressing = 1 - -connector = rabbitmq -plugin.rabbitmq.vhost = /mcollective -plugin.rabbitmq.pool.size = 2 -plugin.rabbitmq.pool.1.host = rabbit1 -plugin.rabbitmq.pool.1.port = 61613 -plugin.rabbitmq.pool.1.user = mcollective -plugin.rabbitmq.pool.1.password = changeme - -plugin.rabbitmq.pool.2.host = rabbit2 -plugin.rabbitmq.pool.2.port = 61613 -plugin.rabbitmq.pool.2.user = mcollective -plugin.rabbitmq.pool.2.password = changeme -{% endhighlight %} - -This gives it 2 servers to attempt to connect to, if the first one fails it will use the second. Usernames and passwords can be set -with the environment variables STOMP_USER, STOMP_PASSWORD. - -If you do not specify a port it will default to _61613_ - -You can also specify the following options for the Stomp gem, these are the defaults in the Stomp 1.2.2 gem: - -{% highlight ini %} -plugin.rabbitmq.initial_reconnect_delay = 0.01 -plugin.rabbitmq.max_reconnect_delay = 30.0 -plugin.rabbitmq.use_exponential_back_off = true -plugin.rabbitmq.back_off_multiplier = 2 -plugin.rabbitmq.max_reconnect_attempts = 0 -plugin.rabbitmq.randomize = false -plugin.rabbitmq.timeout = -1 -{% endhighlight %} - -### Federation - -RabbitMQ federation only mirrors exchanges between nodes so replies need to be -sent to an exchange instead of a queue. In order to enable that add the -following snippet to your client configuration: - -{% highlight ini %} -plugin.rabbitmq.use_reply_exchange = true -{% endhighlight %} - -You will also need to create an exchange called `mcollective_reply` in your -rabbitmq vhost. Assuming you are using the same vhost names from earlier in this -guide you can create this with. - -{% highlight console %} -rabbitmqadmin declare exchange --user=admin --password=changeme --vhost=/mcollective name=mcollective_reply type=direct -{% endhighlight %} - -Note: the `rabbitmq.use_reply_exchange` feature is available from version 2.4.1. - -### STOMP 1.1 Heartbeats - -A common problem is that idle STOMP connections get expired by session -tracking firewalls and NAT devices. Version 1.1 of the STOMP protocol -combats this with protocol level heartbeats, which can be configured -with these settings: - -{% highlight ini %} -# Send heartbeats in 30 second intervals. This is the shortest supported period. -plugin.rabbitmq.heartbeat_interval = 30 - -# By default if heartbeat_interval is set it will request STOMP 1.1 but support fallback -# to 1.0, but you can enable strict STOMP 1.1 only operation by disabling 1.0 fallback -plugin.rabbitmq.stomp_1_0_fallback = 0 - -# Maximum amount of heartbeat read failures before retrying. 0 means never retry. -plugin.rabbitmq.max_hbread_fails = 2 - -# Maxium amount of heartbeat lock obtain failures before retrying. 0 means never retry. -plugin.rabbitmq.max_hbrlck_fails = 0 -{% endhighlight %} - -This feature is avaiable from version 2.4.0 and requires version -1.2.10 or newer of the stomp gem. - -More information about STOMP heartbeats can be found [in the STOMP specification][heartbeat] - -### Parameter reference - -#### `plugin.rabbitmq.connect_timeout` - -Specify the timeout for the TCP+SSL connection to the middleware. - -- _Default:_ 30 -- _Allowed values:_ Any integer - -#### `plugin.rabbitmq.use_exponential_back_off` - -Whether to use exponential backoff when reconnecting to the -middleware. - -- _Default:_ true -- _Allowed values:_ A boolean value - -#### `plugin.rabbitmq.initial_reconnect_delay` - -When `use_exponential_back_off` is set, the initial delay to use when -reconnecting to the middleware. - -- _Default:_ 0.01 -- _Allowed values:_ A positive number expressing time in seconds - -#### `plugin.rabbitmq.max_reconnect_delay` - -When `use_exponential_back_off` is set, the maximum delay to use when -reconnecting to the middleware. - -- _Default:_ 30 -- _Allowed values:_ A number integer expressing time in seconds - -#### `plugin.rabbitmq.back_off_multiplier` - -When `use_exponential_back_off` is set, the amount to increase the -delay by (up to `max_reconnect_delay`). - -- _Default:_ 2 -- _Allowed values:_ A positive integer - -#### `plugin.rabbitmq.max_reconnect_attempts` - -The number of times to attempt to connect to the middleware. 0 means -no limit (retry forever). - -- _Default:_ 0 -- _Allowed values:_ Any integer - -#### `plugin.rabbitmq.heartbeat_interval` - -Setting this value enables STOMP 1.1 heartbeats, and sets the interval -to send/receive heartbeat messages to that number of seconds. - -- _Default:_ (no value) -- _Allowed values:_ An integer >= 30 (smaller values will be padded) - -#### `plugin.rabbitmq.stomp_1_0_fallback` - -When `heartbeat_interval` is set it will request STOMP 1.1 but support fallback -to 1.0. You can force STOMP 1.1 only operation by setting this to false. - -- _Default:_ false -- _Allowed values:_ A boolean - -#### `plugin.rabbitmq.max_hbread_fails` - -Maximum amount of heartbeat read failures to allow before assuming the -connection is dead and reconnecting. - -- _Default:_ 2 -- _Allowed values:_ Any integer - -#### `plugin.rabbitmq.max_hbrlck_fails` - -Maximum amount of heartbeat lock obtain failures before assuming the -connection is dead and reconnecting. This setting is best left at 0 -due to MCollective's usage patterns. - -- _Default:_ 0 -- _Allowed values:_ Any integer - -#### `plugin.rabbitmq.randomize` - -Whether to randomize the order of the connection pool before connecting. - -- _Default:_ false -- _Allowed values:_ A boolean value - -#### `plugin.rabbitmq.pool.size` - -Specifies the size of the connector pool. - -- _Default:_ no default -- _Allowed values:_ Any positive integer - -#### `plugin.rabbitmq.pool.1.host` - -The hostname of this middleware server. - -- _Default:_ no default -- _Allowed values:_ Any string value - -#### `plugin.rabbitmq.pool.1.port` - -The port number to connect to for this middleware server. - -- _Default:_ 61613 -- _Allowed values:_ Any positive integer - -#### `plugin.rabbitmq.pool.1.user` - -The username to connect with to this middleware server. If the -`STOMP_USER` environment variable is set this value will be used -instead. - -- _Default:_ The empty string "" -- _Allowed values:_ Any string value - -#### `plugin.rabbitmq.pool.1.password` - -The password to connect with to this middleware server. If the -`STOMP_PASSWORD` environment variable is set this value will be used -instead. - -- _Default:_ The empty string "" -- _Allowed values:_ Any string value - -#### `plugin.rabbitmq.pool.1.ssl` - -Whether to use TLS when connecting to this middleware. - -- _Default:_ false -- _Allowed values:_ Any boolean value - -#### `plugin.rabbitmq.pool.ssl.fallback` - -Whether to allow unverified TLS if the ca/cert/key settings aren't set. - -- _Default:_ false -- _Allowed values:_ Any boolean value - -#### `plugin.rabbitmq.pool.1.ssl.ca` - -The CA certificate to use when verifying the middleware's -certificate. Must be the fully-qualified path to a `.pem` file. - -- _Default:_ (nothing) -- _Allowed values:_ A fully-qualified path - -#### `plugin.rabbitmq.pool.1.ssl.cert` - -The certificate to present when connecting to the middleware. Must be the -fully-qualified path to a `.pem` file. MCollective will also check -the environment variable `MCOLLECTIVE_RABBITMQ_POOL1_SSL_CERT` for the -client's ssl cert. - -- _Default:_ (nothing) -- _Allowed values:_ A fully-qualified path - -#### `plugin.rabbitmq.pool.1.ssl.key` - -The private key corresponding to this node's certificate. Must be the -fully-qualified path to a `.pem` file. MCollective will also check -the environment variable `MCOLLECTIVE_RABBITMQ_POOL1_SSL_KEY` for the -client's ssl key. - -- _Default:_ (nothing) -- _Allowed values:_ A fully-qualified path - -#### `plugin.rabbitmq.pool.1.ssl.ciphers` - -The SSL ciphers to use when communicating with the middleware. - -- _Default:_ no default -- _Allowed values:_ A string supplying an [OpenSSL cipher suite][cipherstrings] - -#### `plugin.rabbitmq.agents_multiplex` - -Whether to use a single target for all agents in a node. This is an optimization that may make sense when running collectives with several thousands of nodes in order to reduce the number of subscriptions in the message broker. -This is a trade-off between increasing network traffic by delivering messages to all nodes - and letting them select messages they care about - versus increasing work in the message broker to handle large numbers of subscriptions. - -- _Default:_ false -- _Allowed values:_ A boolean value - diff --git a/website/reference/plugins/connector_stomp.md b/website/reference/plugins/connector_stomp.md deleted file mode 100644 index 490266ee..00000000 --- a/website/reference/plugins/connector_stomp.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -layout: default -title: STOMP Connector -toc: false ---- -[STOMP]: http://stomp.codehaus.org/ -[ConnectorActiveMQ]: /mcollective/reference/plugins/connector_activemq.html -[ConnectorRabbitMQ]: /mcollective/reference/plugins/connector_rabbitmq.html - -*NOTE:* This connector is being deprecated and will not be supported in versions newer than 2.2.x. Please move to one of the [ConnectorActiveMQ][] or [ConnectorRabbitMQ][]. - -The stomp connector uses the [STOMP][] rubygem to connect to compatible servers. This is known to work with ActiveMQ and Stompserver. Anecdotal evidence suggests it works with RabbitMQ's Stomp plugin. - -This code will only work with version _1.1_ and _1.1.6_ or newer of the Stomp gem, the in between versions have threading issues. - -As this connector tries to be as generic as possible it is hard to support all the advanced features of MCollective using it. We do not recommend you use the directed mode -using this plugin, instead look towards specific ones written for ActiveMQ or your chosen middleware. - -## Middleware Layout - -For broadcast messages this connector will create _topics_ with names like _/topic/<collective>.<agent>.command_ and replies will go to -_/topic/<collective>.<agent>.reply_ - -For directed messages it will create queues with names like _/queue/<collective>.mcollective.<md5 hash of identity>_. - -You should configure appropriate ACLs on your middleware to allow this scheme - -## Configuring - -### Common Options -The most basic configuration method is supported in all versions of the gem: - -{% highlight ini %} -connector = stomp -plugin.stomp.base64 = false -plugin.stomp.host = stomp.my.net -plugin.stomp.port = 61613 -plugin.stomp.user = me -plugin.stomp.password = secret -{% endhighlight %} - -You can override all of these settings using environment variables STOMP_SERVER, STOMP_PORT, STOMP_USER, STOMP_PASSWORD. It is recommended that your _client.cfg_ do not have usernames and passwords in it, users should set their own in the environment. - -If you are seeing issues with the Stomp gem logging protocol errors and resetting your connections, especially if you are using Ruby on Rails then set the _plugin.stomp.base64_ to true, this adds an additional layer of encoding on packets to make sure they don't interfere with UTF8 encoding used in Rails. - -### Failover Pools -Newer versions of the Stomp gem supports failover between multiple Stomp servers, you need at least _1.1.6_ to use this. - -If you are using version _1.1.9_ and newer of the Stomp Gem and this method of configuration you will also receive more detailed -logging about connections, failures and other significant events. - -{% highlight ini %} -connector = stomp -plugin.stomp.pool.size = 2 -plugin.stomp.pool.host1 = stomp1 -plugin.stomp.pool.port1 = 61613 -plugin.stomp.pool.user1 = me -plugin.stomp.pool.password1 = secret - -plugin.stomp.pool.host2 = stomp2 -plugin.stomp.pool.port2 = 61613 -plugin.stomp.pool.user2 = me -plugin.stomp.pool.password2 = secret -{% endhighlight %} - -This gives it 2 servers to attempt to connect to, if the first one fails it will use the second. As before usernames and passwords can be set with STOMP_USER, STOMP_PASSWORD. - -If you do not specify a port it will default to _6163_ - -When using pools you can also specify the following options, these are the defaults in the Stomp 1.1.6 gem: - -{% highlight ini %} -plugin.stomp.pool.initial_reconnect_delay = 0.01 -plugin.stomp.pool.max_reconnect_delay = 30.0 -plugin.stomp.pool.use_exponential_back_off = true -plugin.stomp.pool.back_off_multiplier = 2 -plugin.stomp.pool.max_reconnect_attempts = 0 -plugin.stomp.pool.randomize = false -plugin.stomp.pool.timeout = -1 -plugin.stomp.pool.connect_timeout = 30 -{% endhighlight %} - -### Message Priority - -As of version 5.4 of ActiveMQ messages support priorities, you can pass in the needed -priority header by setting: - -{% highlight ini %} -plugin.stomp.priority = 4 -{% endhighlight %} diff --git a/website/reference/plugins/data.md b/website/reference/plugins/data.md deleted file mode 100644 index a1e6b4e3..00000000 --- a/website/reference/plugins/data.md +++ /dev/null @@ -1,271 +0,0 @@ ---- -layout: default -title: Data Plugins ---- -[DDL]: /mcollective/reference/plugins/ddl.html -[DiscoveryPlugins]: /mcollective/reference/plugins/discovery.html - -## Overview -Up to MCollective 2.0 the discovery system could only discover against -installed agents, configuration management classes or facts and the node -identities. We're extending this to support discovery against many -sources through a simple plugin system. - -*NOTE:* This feature is available since version 2.1.0 - -The basic idea is that you could do discovery statements like the ones -below: - -{% highlight console %} -% mco find -S "fstat('/etc/rsyslog.conf').md5=/4edff591f6e38/" -{% endhighlight %} - -{% highlight console %} -% mco find -S "sysctl('net.ipv4.conf.all.forwarding').value=1" -{% endhighlight %} - -{% highlight console %} -% mco find -S "sysctl('net.ipv4.conf.all.forwarding').value=1 and % location=dc1" -{% endhighlight %} - -You could also use these data sources in your own agents or other -plugins: - -{% highlight ruby %} -action "query" do - reply[:value] = Data.sysctl(request[:sysctl_name]).value -end -{% endhighlight %} - -*NOTE:* As opposed to the [DiscoveryPlugins][] which are used by the client -to communicate to the nodes using direct addressing, data plugins on the other -hand refer to data that the nodes can provide, and hence this uses the normal -broadcast discovery paradigm. - -These new data sources are plugins so you can provide via the plugin -system and they require DDL documents. The DDL will be used on both the -client and the server to provide strict validation and configuration. - -The DDL for these plugins will affect the client libraries in the -following ways: - -* You will get errors if you try to discover using unknown functions -* Your input argument values will be validated against the DDL -* You will only be able to use output properties that are known in the DDL -* If a plugin DDL says it needs 5 seconds to run your discovery and maximum run times will increase by 5 seconds automatically - -On the servers the DDL will: - -* be used to validate known plugins -* be used to validate input arguments -* be used to validate requests for known output values - -## Viewing or retrieving results from a data plugin - -You can view the output from a data plugin using the *rpcutil* agent: - -{% highlight console %} -% mco rpc rpcutil get_data source=fstat query=/etc/hosts -. -. -your.node.net - atime: 2012-06-14 21:41:54 - atime_age: 54128 - atime_seconds: 1339706514 - ctime: 2012-01-18 20:28:34 - ctime_age: 12842128 - ctime_seconds: 1326918514 - gid: 0 - md5: 54fb6627dbaa37721048e4549db3224d - mode: 100644 - mtime: 2010-01-12 13:28:22 - mtime_age: 76457740 - mtime_seconds: 1263302902 - name: /etc/hosts - output: present - present: 1 - size: 158 - type: file - uid: 0 -{% endhighlight %} - -The same action can be used to retrieve data programatically. - -## Writing a data plugin - -### The Ruby logic for the plugin -The data plugins should not change the system in anyway, you should take -care to create plugins that only reads the state of the system. If you -want to affect the status of the system you should write Agents. - -These plugins are kept simple as they will be typed on the command line -so the following restrictions are present: - -* They can only take 1 input argument -* They can only return simple String, Numeric or Booleans no Hashes or complex data types -* They should be fast as these will impact discovery times and agent run times. - -Writing data plugins is easy and mimic the basics of writing agents, -below we have a simple *sysctl* plugin that was used in the examples -earlier: - -{% highlight ruby linenos %} -module MCollective - module Data - class Sysctl_data "Sysctl values", - :description => "Retrieve values for a given sysctl", - :author => "R.I.Pienaar ", - :license => "ASL 2.0", - :version => "1.0", - :url => "https://docs.puppetlabs.com/mcollective/", - :timeout => 1 - -dataquery :description => "Sysctl values" do - input :query, - :prompt => "Variable Name", - :description => "Valid Variable Name", - :type => :string, - :validation => /^[\w\-\.]+$/, - :maxlength => 120 - - output :value, - :description => "Kernel Parameter Value", - :display_as => "Value" -end -{% endhighlight %} - -The *timeout* must be set correctly, if your data source is slow you -need to reflect that in the timeout here. The timeout will be used on -the clients to decide how long to wait for discovery responses from the -network so getting this wrong will result in nodes not being discovered. - -Each data plugin can only have one *dataquery* block with exactly 1 -*input* block but could have multiple *output* blocks. - -It's important to get the validation correct, here we only accept the -characters we know are legal in sysctl variables on Linux. We will -specifically never allow backticks to be used in arguments to avoid -accidental shell exploits. - -Note the correlation between output names and the use in discovery and -agents here we create an output called *value* this means we would use -it in discovery as: - -{% highlight console %} -% mco find -S "sysctl('net.ipv4.conf.all.forwarding').value=1" -{% endhighlight %} - -And we would output the result from our plugin code as: - -{% highlight ruby linenos %} -result[:value] = value -{% endhighlight %} - -And in any agent where we might use the data source: - -{% highlight ruby linenos %} -something = Data.sysctl('net.ipv4.conf.all.forwarding').value -{% endhighlight %} - -These have to match everywhere, you cannot reference undeclared data and -you cannot use input that does not validate against the DDL declared -validations. - -Refer to the full [DDL][] documentation for details on all possible values -of the *metadata*, *input* and *output* blocks. - -## Auto generated documentation -As with agents the DDL can be used to generate documentation, if you -wanted to know what the input and output values are for a specific -plugin you can use *mco plugin doc* to see generated documentation. - -{% highlight console %} -% mco plugin doc sysctl -Sysctl values -============= - -Retrieve values for a given sysctl - - Author: R.I.Pienaar - Version: 1.0 - License: ASL 2.0 - Timeout: 1 - Home Page: https://docs.puppetlabs.com/mcollective/ - -QUERY FUNCTION INPUT: - - Description: Valid Variable Name - Prompt: Variable Name - Type: string - Validation: (?-mix:^[\w\-\.]+$) - Length: 120 - -QUERY FUNCTION OUTPUT: - - value: - Description: Kernel Parameter Value - Display As: Value - -{% endhighlight %} - -## Available plugins for a node You can use the *mco inventory* -application to see remotely what plugins a node has available: - -{% highlight console %} -% mco inventory your.node -Inventory for your.node: - - . - . - . - - Data Plugins: - fstat sysctl - -{% endhighlight %} diff --git a/website/reference/plugins/ddl.md b/website/reference/plugins/ddl.md deleted file mode 100644 index 0ca698b0..00000000 --- a/website/reference/plugins/ddl.md +++ /dev/null @@ -1,274 +0,0 @@ ---- -layout: default -title: Data Definition Language ---- -[WritingAgents]: /mcollective/reference/basic/basic_agent_and_client.html -[SimpleRPCClients]: /mcollective/simplerpc/clients.html -[ResultsandExceptions]: /mcollective/simplerpc/clients.html#results_and_exceptions -[SimpleRPCAuditing]: /mcollective/simplerpc/auditing.html -[SimpleRPCAuthorization]: /mcollective/simplerpc/authorization.html -[WritingAgentsScreenCast]: https://www.youtube.com/watch?v=2Xhq_UqnqRE -[DDLScreenCast]: https://www.youtube.com/watch?v=xikjjXvN6nA -[RPCUtil]: /mcollective/reference/plugins/rpcutil.html -[ValidatorPlugins]: /mcollective/reference/plugins/validator.html - -As with other remote procedure invocation systems MCollective has a DDL that defines what remote methods are available, what inputs they take and what outputs they generate. - -In addition to the usual procedure definitions we also keep meta data about author, versions, license and other key data points. - -The DDL is used in various scenarios: - -* The user can access it in the form of a human readable help page -* User interfaces can access it in a way that facilitate auto generation of user interfaces -* The RPC client auto configures and use appropriate timeouts in waiting for responses -* Before sending a call over the network inputs get validated so we do not send unexpected data to remote nodes. -* Module repositories can use the meta data to display a standard view of available modules to assist a user in picking the right ones. -* The server will validate incoming requests prior to sending it to agents - -We've created [a screencast showing the capabilities of the DDL][DDLScreenCast] that might help give you a better overview. - -**NOTE:** As of version 2.1.1 the DDL is required on all servers before an agent will be activated - -## Examples -We'll start with a few examples as I think it's pretty simple what they do, and later on show what other permutations are allowed for defining inputs and outputs. - -A helper agent called [_rpcutil_][RPCUtil] is included that helps you gather stats, inventory etc about the running daemon. This helper has a full DDL included, see the plugins dir for this agent. - -The typical service agent is a good example, it has various actions that all more or less take the same input. All but status would have almost identical language. - -### Meta Data -First we need to define the meta data for the agent itself: - -{% highlight ruby linenos %} -metadata :name => "service", - :description => "Agent to manage services using the Puppet service provider", - :author => "R.I.Pienaar", - :license => "GPLv2", - :version => "1.1", - :url => "https://docs.puppetlabs.com/mcollective/plugin_directory/", - :timeout => 60 -{% endhighlight %} - -It's fairly obvious what these all do, *:timeout* is how long the MCollective daemon will let the threads run. - -## Required versions -As of MCollective 2.1.2 you can indicate which is the lowest version of MCollective needed to use a plugin. Plugins that do not meet the requirement can not be used. - -{% highlight ruby linenos %} -requires :mcollective => "2.0.0" -{% endhighlight %} - -You should add this right after the metadata section in the DDL - -## Actions, Input and Output -Defining inputs and outputs is the hardest part, below first the *status* action: - -{% highlight ruby linenos %} -action "status", :description => "Gets the status of a service" do - display :always # supported in 0.4.7 and newer only - - input :service, - :prompt => "Service Name", - :description => "The service to get the status for", - :type => :string, - :validation => '^[a-zA-Z\-_\d]+$', - :optional => false, - :default => "mcollective", - :maxlength => 30 - - output :status, - :description => "The status of service", - :display_as => "Service Status", - :default => "unknown status" -end -{% endhighlight %} - -As you see we can define all the major components of input and output parameters. *:type* can be one of various values and each will have different parameters, more on that later. - -As of version 2.1.1 the outputs can define a default value. For agents the reply structures are pre-populated with all the defined outputs, if no default is supplied a default of nil will be set. - -As of version 2.3.1 the inputs can also define default values, this is only processed and applied for non optional inputs. - -By default mcollective only show data from actions that failed, the *display* line above tells it to always show the results. Possible values are *:ok*, *:failed* (the default behavior) and *:always*. - -Finally the service agent has 3 almost identical actions - *start*, *stop* and *restart* - below we use a simple loop to define them all in one go. - -{% highlight ruby linenos %} -["start", "stop", "restart"].each do |act| - action act, :description => "#{act.capitalize} a service" do - input :service, - :prompt => "Service Name", - :description => "The service to #{act}", - :type => :string, - :validation => '^[a-zA-Z\-_\d]+$', - :optional => false, - :maxlength => 30 - - output :status, - :description => "The status of service after #{act}", - :display_as => "Service Status", - :default => "unknown status" - end -end -{% endhighlight %} - -All of this code just goes into a file, no special class or module bits needed, just save it as *service.ddl* in the same location as the *service.rb*. - -Importantly you do not need to have the *service.rb* on a machine to use the DDL, this means on machines that are just used for running client programs you can just drop the *.ddl* files into the agents directory. - -You can view a human readable version of this using *mco plugin doc <agent>* command: - -{% highlight console %} -% mco plugin doc service -service -======= - -Agent to manage services using the Puppet service provider - - Author: R.I.Pienaar - Version: 1.1 - License: GPLv2 - Timeout: 60 - Home Page: https://docs.puppetlabs.com/mcollective/plugin_directory/ - - - -ACTIONS: -======== - restart, start, status, stop - - restart action: - --------------- - Restart a service - - INPUT: - service: - Description: The service to restart - Prompt: Service Name - Type: string - Validation: ^[a-zA-Z\-_\d]+$ - Length: 30 - - - OUTPUT: - status: - Description: The status of service after restart - Display As: Service Status -{% endhighlight %} - -### Optional Inputs -The input block has a mandatory *:optional* field, when true it would be ok if a client attempts to call the agent without this input supplied. If it is supplied though it will be validated. - -### Types of Input -As you see above the input block has *:type* option, types can be *:string*, *:list*, *:boolean*, *:integer*, *:float* or *:number* - -#### :string type -The string type validates initially that the input is infact a String, then it validates the length of the input and finally matches the supplied Regular Expression. - -Both *:validation* and *:maxlength* are required arguments for the string type of input. - -If you want to allow unlimited length text you can make *:maxlength => 0* but use this with care. - -As of version 2.2.0 a new plugin type called [Validator Plugins][ValidatorPlugins] exist that allow you to supply your own validations for *:string* types. - -#### :list type -List types provide a list of valid options and only those will be allowed, see an example below: - -{% highlight ruby linenos %} -input :action, - :prompt => "Service Action", - :description => "The action to perform", - :type => :list, - :optional => false, - :list => ["stop", "start", "restart"] -{% endhighlight %} - -In user interfaces this might be displayed as a drop down list selector or another kind of menu. - -#### :boolean type - -The value input should be either _true_ or _false_ actual boolean values. This feature was introduced in version _0.4.9_. - -#### :integer type - -The value input should be an integer number like _1_ or _100_ but not _1.1_. This feature was introduced in version _1.3.2_ - -#### :float type - -The value input should be a floating point number like _1.0_ but not _1_. This feature was introduced in version _1.3.2_ - -#### :number type - -The value input should be an integer or a floating point number. This feature was introduced in version _1.3.2_ - -#### :any type - -The value input can be any type, this allows you to send rich objects like arrays of hashes around, it effectively disables validation of the type of input. - -The :any type is deprecated and will be removed after version 2.2.x. - -### Accessing the DDL -While programming client applications or web apps you can gain access to the DDL for any agent in several ways: - -{% highlight ruby linenos %} -require 'mcollective' - -config = MCollective::Config.instance -config.loadconfig(options[:config]) - -ddl = MCollective::DDL.new("service") -puts ddl.help("#{config.configdir}/rpc-help.erb") -{% endhighlight %} - -This will produce the text help output from the above example, you can supply any ERB template to format the output however you want. - -You can also access the data structures directly: - -{% highlight ruby linenos %} -ddl = MCollective::DDL.new("service") -puts "Meta Data:" -pp ddl.meta - -puts -puts "Status Action:" -pp ddl.action_interface("status") -{% endhighlight %} - -{% highlight ruby linenos %} -Meta Data: -{:license=>"GPLv2", - :author=>"R.I.Pienaar", - :name=>"service", - :timeout=>60, - :version=>"1.1", - :url=>"https://docs.puppetlabs.com/mcollective/plugin_directory/", - :description=>"Agent to manage services using the Puppet service provider"} - -Status Action: -{:action=>"status", - :input=> - {:service=> - {:validation=>"^[a-zA-Z\\-_\\d]+$", - :maxlength=>30, - :prompt=>"Service Name", - :type=>:string, - :optional=>false, - :description=>"The service to get the status for"}}, - :output=> - {"status"=> - {:display_as=>"Service Status", :description=>"The status of service"}}, - :description=>"Gets the status of a service"} - -{% endhighlight %} - -The ddl object is also available on any *rpcclient*: - -{% highlight ruby linenos %} -service = rpcclient("service") -pp service.ddl.meta -{% endhighlight %} - -In the case of accessing it through the service as in this example, if there was no DDL file on the machine for the service agent you'd get a *nil* back from the ddl accessor. - -### Input Validation -As mentioned earlier the client does automatic input validation using the DDL, if validation fails you will get an *MCollective::DDLValidationError* exception thrown with an appropriate message. diff --git a/website/reference/plugins/discovery.md b/website/reference/plugins/discovery.md deleted file mode 100644 index 464888d3..00000000 --- a/website/reference/plugins/discovery.md +++ /dev/null @@ -1,158 +0,0 @@ ---- -layout: default -title: Discovery Plugins ---- -[DDL]: /mcollective/reference/plugins/ddl.html - -## Overview -Up to MCollective 2.0.0 the discovery system could only discover against -the network by doing broadcasts over the middleware. - -The _direct addressing_ capability introduced in 2.0.0 enables users to -communicate with a node without doing broadcast if they know the -configured identity of that node. - -In version 2.1.0 we are introducing a new kind of plugin that works on -the client system to do discovery against any data source that can -return a list of identities such as flatfiles or databases. - -## Configuring and using discovery plugins -Your mcollective client has a setting called *default_discovery_method* -that defaults to *mc*, if you change this in your _client.cfg_ to -another known plugin you can use that instead. - -To get a list of known discovery plugins use the _mco plugin_ -application: - -{% highlight console %} -% mco plugin doc -Please specify a plugin. Available plugins are: - -Discovery Methods: - flatfile Flatfile based discovery for node identities - mc MCollective Broadcast based discovery - mongo MongoDB based discovery for databases built using registration -{% endhighlight %} - -Each plugin can have a different set of capabilities, for example a -flatfile with only hostnames cannot do class or fact based filters and -you will receive an error if you tried to do so. You can see the -capabilities of each plugin using the _mco plugin_ application: - -{% highlight console %} -$ mco plugin doc flatfile -flatfile -======== - -Flatfile based discovery for node identities - - Author: R.I.Pienaar - Version: 0.1 - License: ASL 2.0 - Timeout: 0 - Home Page: https://docs.puppetlabs.com/mcollective/ - -DISCOVERY METHOD CAPABILITIES: - Filter based on mcollective identity -{% endhighlight %} - -Here you can see the only capability that this plugin has is to filter -against identities. - -These plugins require DDL files to be written and distributed when -installing each plugin. - -When using the mcollective CLI you can choose which plugin to use per -request, some plugins require arguments like the file to discover -against: - -{% highlight console %} -$ mco rpc rpcutil ping --dm flatfile --do /some/text/file -{% endhighlight %} - -In the case of the flatfile plugin there is a convenient shortcut -available on all client applications that has the same effect as above: - -{% highlight console %} -$ mco rpc rpcutil ping --nodes /some/text/file -{% endhighlight %} - -Any request that uses the compound filters using *-S* will be forced to -use the network broadcast discovery method. - -## Writing a discovery plugin -Writing your own discovery plugin is very simple, you need to provide -one method that returns an array of node names. - -The plugins only need to be present on the client machines but no harm -in installing them on all machines. They need to be installed into the -*discovery* directory in the usual plugin directory. You can use the -*mco plugin package* command to create RPM or DEB packages for these -plugins. - -{% highlight ruby linenos %} -module MCollective - class Discovery - class Flatfile - def self.discover(filter, timeout, limit=0, client=nil) - unless client.options[:discovery_options].empty? - file = client.options[:discovery_options].first - else - raise "The flatfile discovery method needs a path to a text file" - end - - raise "Cannot read the file %s specified as discovery source" % file unless File.readable?(file) - - discovered = [] - - hosts = File.readlines(file).map{|l| l.chomp} - - unless filter["identity"].empty? - filter["identity"].each do |identity| - identity = Regexp.new(identity.gsub("\/", "")) if identity.match("^/") - - if identity.is_a?(Regexp) - discovered = hosts.grep(identity) - elsif hosts.include?(identity) - discovered << identity - end - end - else - discovered = hosts - end - - discovered - end - end - end -end -{% endhighlight %} - -This is the *flatfile* plugin that is included in the distribution. You -can see it using the *client.options\[:discovery_options\]* array to get -access to the file supplied using the *--do* command line argument, -reading that file and doing either string or Regular Expression matching -against it finally returning the list of nodes. - -As mentioned each plugin needs a DDL, the DDL for this plugin is very -simple: - -{% highlight ruby linenos %} -metadata :name => "flatfile", - :description => "Flatfile based discovery for node identities", - :author => "R.I.Pienaar ", - :license => "ASL 2.0", - :version => "0.1", - :url => "https://docs.puppetlabs.com/mcollective/", - :timeout => 0 - -discovery do - capabilities :identity -end -{% endhighlight %} - -Here we expose just the one capability, valid capabilities would be -*:classes*, *:facts*, *:identity*, *:agents* and *:compound*. In -practise you cannot create a plugin that supports the *:compound* -capability as mcollective will force the use of the *mc* plugin if you -use those. diff --git a/website/reference/plugins/facts.md b/website/reference/plugins/facts.md deleted file mode 100644 index 89a382f4..00000000 --- a/website/reference/plugins/facts.md +++ /dev/null @@ -1,82 +0,0 @@ ---- -layout: default -title: Writing Fact Plugins -toc: false ---- -[SimpleRPCAuthorization]: /mcollective/simplerpc/authorization.html -[Registration]: registration.html - -Fact plugins are used during discovery whenever you run the agent with queries like *-W country=de*. - -The default setup uses a YAML file typically stored in */etc/mcollective/facts.yaml* to read facts; usually, you'll configure Puppet or a background task to write this file using data from Facter. If you're using some other tool to profile system data, you can either configure it to write a similar YAML file or write a **fact plugin** to access that tool directly. - -Facts at the moment should be simple *variable = value* style flat hashes, where `value` is a string; structured facts aren't supported. - -## Details -Implementing a facts plugin is made simple by inheriting from *MCollective::Facts::Base*, in that case you just need to provide 1 method, the YAML plugin code can be seen below: - -For releases in the 1.0.x release cycle and older, use this plugin format: - -{% highlight ruby linenos %} -module MCollective - module Facts - require 'yaml' - - # A factsource that reads a hash of facts from a YAML file - class Yaml ["rpcutil", "discovery"], - :facts => {"mcollective"=>1}, - :classes => ["common::linux", "motd"], - :data_plugins=>["sysctl_data", "fstat_data"], - :collectives=>["mcollective"], - :main_collective=>"mcollective", - :version=>"2.0.0"} -{% endhighlight %} - -## `daemon_stats` Action - -Retrieves statistics about the running daemon, how many messages it's handled, passed, dropped etc. - -See the DDL for the agent for a full reference - -{% highlight ruby %} -{:configfile=>"/etc/mcollective/server.cfg", - :validated=>46, - :threads=> ["#", - "#", - "#"], - :starttime=>1284305683, - :agents=>["rpcutil", "discovery"], - :unvalidated=>0, - :pid=>15499, - :times=>{:cutime=>0.0, :utime=>0.15, :cstime=>0.0, :stime=>0.02}, - :passed=>46, - :total=>46, - :filtered=>0, - :replies=>45} -{% endhighlight %} - -Replies will always be less than received since the current message has not been sent yet when the stats are gathered. - -## `get_fact` Action - -Retrieves a single fact from the server - -{% highlight ruby %} -{:fact => "mcollective", - :value => 1} -{% endhighlight %} - -## `agent_inventory` Action - -Returns a list of all agents with their meta data like version, author, license etc - -{% highlight ruby %} -{:agents=> [ - {:agent=>"discovery", - :license=>"Apache License, Version 2", - :author=>"R.I.Pienaar "}, - - {:agent=>"rpcutil", - :license=>"Apache License, Version 2.0", - :name=>"Utilities and Helpers for SimpleRPC Agents", - :url=>"https://docs.puppetlabs.com/mcollective/", - :description=> "General helpful actions that expose stats and internals to SimpleRPC clients", - :version=>"1.0", - :author=>"R.I.Pienaar ", - :timeout=>3} - ] -} -{% endhighlight %} - -## `get_config_item` Action - -Retrieves the active value for any configuration item on a server - -{% highlight ruby %} -{:item => "loglevel", - :value => "debug"} -{% endhighlight %} - -## _ping_ Action - -A simple lightweight ping action that just returns each nodes local time - -{% highlight ruby %} -{:pong => 1295471526} -{% endhighlight %} - -## `collective_info` Action - -Retrieves the main and sub collectives configured - -For a server configured with: - -{% highlight ruby %} -collectives = mcollectivedev,subdev1 -main_collective = mcollectivedev -{% endhighlight %} - -The following structure gets returned: - -{% highlight ruby %} -{:collectives=>["mcollectivedev", "subdev1"], - :main_collective=>"mcollectivedev"} -{% endhighlight %} diff --git a/website/reference/plugins/security_aes.md b/website/reference/plugins/security_aes.md deleted file mode 100644 index 82abdcd2..00000000 --- a/website/reference/plugins/security_aes.md +++ /dev/null @@ -1,283 +0,0 @@ ---- -layout: default -title: AES Security Plugin ---- -[SimpleRPCAuthorization]: /mcollective/simplerpc/authorization.html -[Registration]: registration.html -[SSLSecurity]: security_ssl.html -[SecurityOverview]: ../../security.html - -## Overview -In most cases, users will probably want to use the [SSLSecurity][] plugin instead of this one. - -This plugin impliments a AES encryption and RSA public / private key based security system -for The Marionette Collective. - -Please review the [Security Overview][SecurityOverview] for a general discussion about security in Marionette Collective. - -The design goals of this plugin are: - -* Each actor - clients and servers - can have their own set of public and private keys -* All actors are uniquely and cryptographically identified -* Requests are encrypted using the clients private key and anyone that has - the public key can see the request. Thus an atacker may see the requests - given access to a public key of the requester. -* Request TTLs and Message Times are cryptographically secured and tampered - messages are not accepted by default. This is a first line defence in message - replaying and tampering. -* Replies are encrypted using the calling clients public key. Thus no-one but - the caller can view the contents of replies. -* Servers can all have their own SSL keys, or share one, or reuse keys created - by other PKI using software like Puppet -* Requests from servers - like registration data - can be secured even to external - eavesdropping depending on the level of configuration you are prepared to do -* Given a network where you can ensure third parties are not able to access the - middleware public key distribution can happen automatically - -During the design of this system we considered the replies back to clients to be most -important piece of information on the network. This is secured in a way that only -the client can decrypt the replies he gets. An attacker will need to gain access -to every private key of every client to see all the reply data. - -Serialization uses Marshal or YAML, which means data types in and out of mcollective -will be preserved from client to server and reverse. - -## Compared to the SSL plugin - -The earlier [SSLSecurity][] only provided message signing and identification of clients, this -new plugin builds on this adding payload encryption, identification of servers and optional -automatic key distribution. - -The [SSLSecurity][] plugin puts less drain on resources, if you do not specifically need encryption -you should consider using that one instead. - -If you are using ActiveMQ middleware with CA-verified TLS enabled, the features of this plugin -may not be required and you should probably use the [SSLSecurity][] plugin instead. - -## Deployment Scenarios - -There are various modes of deployment, the more secure you wish to be the more work you have -to do in terms of key exchange and initial setup. - -This plugin is designed to allow you to strike a balance between security and setup cost -the sections below discuss the possible deployment scenarios to help you choose an approach. - -In all of the below setups the following components are needed: - -* Each user making requests - the client - needs a public and private key pair -* Each server receiving requests need the public key of each client - -In cases where you wish to use [Registration][] or initiate requests from the server for any -reason the following are needed: - -* Each server needs a public and private key pair -* Each other server that wish to receive these requests need the public key of the sending server - -In this scenario each server will act as a client making RPC requests to the collective network -for any agent called `registration`. So in this scenario the server acts as a client and therefore -need a key-pair to identify it. - -### Manual key distribution with each server and client having unique keys - -In this setup each client and each server needs a unique set of keys. You need to -distribute these keys manually and securely - perhaps using Puppet. - -The setup cost is great, to enable registration the nodes receiving registration data -need to have the public key of every other node stored locally before registration -data can be received. - -If you do not use Puppet or some other PKI system that provide access to keys you need -create keypairs for each node and client. - -This is the most secure setup protecting all replies and registration data. Rogue people -on the network who do not compromise a running host cannot make requests on the network. - -Attackers who compromise a server can only make registration requests assuming you deployed -a strictly configured [Authorization][SimpleRPCAuthorization] system they cannot use those -machines as starting points to inject requests for the rest of your network. - -By gaining access to your Middleware an attacker will not be able to observe the contents of -requests, replies or registration messages. Attackers need to compromise servers and gain -access to private keys before they can start observing parts of the exchange. - -|Feature / Capability|Supported| -|--------------------|---------| -|Clients are uniquely identified using cryptographic means|yes| -|Anyone with the client public key can observe request contents|yes| -|Attackers can gain access to the client public key by just listening on the network|no| -|Replies back to the client are readable only by client that initiated the request|yes| -|Attackers can create new certificates and start using them to make requests as clients|no| -|Servers are uniquely identified using cryptographic means|yes| -|Anyone with the server public key can observe registration contents|yes| -|Attackers can gain access to the server public keys by just listening on the network|no| -|Registration data can be protected from rogue agents posing as registration agents|yes| -|Attackers can create new nodes and inject registration data for those new nodes|no| - -To configure this scenario use the following options and manually copy public keys to the -`plugin.aes.client_cert_dir` directory: - -|Settings|Value|Descritpion| -|--------|-----|-----------| -|`plugin.aes.send_pubkey`|0|Do not send public keys| -|`plugin.aes.learn_pubkeys`|0|Do not learn public keys| - -### Automatic public key distribution with each server and client having unique keys - -Here we enable the `plugin.aes.learn_pubkeys` feature on all servers. Your public keys -will now be distributed automatically on demand but you loose some security in that anyone -with access to your network or middleware can observe the contents of replies and registration -data - -You still need to create keys for every node - or use Puppets. You still need to create keys -for every user. - -In order to protect against attackers creating new certificates and making requests on your network -deploy a [Authorization][SimpleRPCAuthorization] plugin that denies unknown clients. - -|Feature / Capability|Supported| -|--------------------|---------| -|Clients are uniquely identified using cryptographic means|yes| -|Anyone with the client public key can observe request contents|yes| -|Attackers can gain access to the client public key by just listening on the network|*yes*| -|Replies back to the client are readable only by client that initiated the request|yes| -|Attackers can create new certificates and start using them to make requests as clients|*yes*| -|Servers are uniquely identified using cryptographic means|yes| -|Anyone with the server public key can observe registration contents|yes| -|Attackers can gain access to the server public keys by just listening on the network|*yes*| -|Registration data can be protected from rogue agents posing as registration agents|yes| -|Attackers can create new nodes and inject registration data for those new nodes|*yes*| - -To configure this scenario use the following options and ensure the `mcollectived` can write -to the `plugin.aes.client_cert_dir` directory: - -|Settings|Value|Descritpion| -|--------|-----|-----------| -|`plugin.aes.send_pubkey`|1|Send public keys| -|`plugin.aes.learn_pubkeys`|1|Learn public keys| - -### Manual public key distribution with servers sharing a key pair and clients having unique keys - -This is comparable to the older SSL plugin where all servers shared the same public / private -pair. Here anyone who is part of the network can decrypt the traffic related to registration -but replies to clients are still securely encrypted and visable only to them. - -You will not need to create unique keys for every server, you can simply copy the same one out -everywhere. You still need to create keys for every user. - -If you do not use registration, this is a very secure setup that requires a small configuration -overhead. - -|Feature / Capability|Supported| -|--------------------|---------| -|Clients are uniquely identified using cryptographic means|yes| -|Anyone with the client public key can observe request contents|yes| -|Attackers can gain access to the client public key by just listening on the network|no| -|Replies back to the client are readable only by client that initiated the request|yes| -|Attackers can create new certificates and start using them to make requests as clients|no| -|Servers are uniquely identified using cryptographic means|*no*| -|Anyone with the server public key can observe registration contents|yes| -|Attackers can gain access to the server public keys by just listening on the network|no| -|Registration data can be protected from rogue agents posing as registration agents|*no*| - -To configure this scenario use the following options and ensure the `mcollectived` can write -to the `plugin.aes.client_cert_dir` directory: - -|Settings|Value|Descritpion| -|--------|-----|-----------| -|`plugin.aes.send_pubkey`|0|Do not send public keys| -|`plugin.aes.learn_pubkeys`|0|Do not learn public keys| - -### Automatic public key distribution with servers sharing a key and client having unique keys - -This is comparable to the older SSL plugin where all servers shared the same public / private -pair. Here anyone who is part of the network can decrypt the traffic related to registration -but replies to clients are still securely encrypted and visable only to them. - -Here we enable the `plugin.aes.learn_pubkeys` feature on all servers. Your public keys -will now be distributed automatically on demand but you loose some security in that anyone -with access to your network or middleware can observe the contents of replies and registration -data - -You will not need to create unique keys for every server, you can simply copy the same one out -everywhere. You still need to create keys for every user. - -In order to protect against attackers creating new certificates and making requests on your network -deploy a [Authorization][SimpleRPCAuthorization] plugin that denies unknown clients. - -|Feature / Capability|Supported| -|--------------------|---------| -|Clients are uniquely identified using cryptographic means|yes| -|Anyone with the client public key can observe request contents|yes| -|Attackers can gain access to the client public key by just listening on the network|*yes*| -|Replies back to the client are readable only by client that initiated the request|yes| -|Attackers can create new certificates and start using them to make requests as clients|*yes*| -|Servers are uniquely identified using cryptographic means|*no*| -|Anyone with the server public key can observe registration contents|yes| -|Attackers can gain access to the server public keys by just listening on the network|*yes*| -|Registration data can be protected from rogue agents posing as registration agents|*no*| - -To configure this scenario use the following options and ensure the `mcollectived` can write -to the `plugin.aes.client_cert_dir` directory: - -|Settings|Value|Descritpion| -|--------|-----|-----------| -|`plugin.aes.send_pubkey`|1|Send public keys| -|`plugin.aes.learn_pubkeys`|1|Learn public keys| - -## Creating keys - -Keys are created using OpenSSL. The filenames of public keys are significant you should name -them so that they are unique for your network and they should match on the client and servers. - -{% highlight console %} - % openssl genrsa -out server-private.pem 1024 - % openssl rsa -in server-private.pem -out server-public.pem -outform PEM -pubout -{% endhighlight %} - -Client and Server keys are made using the same basic method. - -## Reusing Puppet keys - -Puppet managed nodes will all have keys created by Puppet already, you can reuse these if your -`mcollectived` runs as root. - -Generally Puppet stores these in `/var/lib/puppet/ssl/private_keys/fqdn.pem` and `/var/lib/puppet/ssl/public_keys/fqdn.pem` -simply configure these paths for your `server_private` and `server_public` options. - -Clients will still need their own keys made and distributed. - -## Future Roadmap - -* Depending on performance of the initial system we might validate request certificates are - signed by a known CA this will provide an additional level of security preventing attackers - from creating their own keys and using those on the network without also compromising the CA. -* Private keys will be secured with a password - - -## Configuration Options - -### Common Options - -|Setting|Example / Default|Description -|-------|-----------------|-----------| -|`securityprovider`|`aes_security`|Enables this security provider| -|`plugin.aes.serializer`|`yaml` or `marshal`|Serialization to use| -|`plugin.aes.send_pubkey`|`0` or `1`|Send the public key with every request| -|`plugin.aes.learn_pubkeys`|`0` or `1`|Receive public keys from the network and cache them locally| - -### Client Options - -|Setting|Example / Default|Description -|-------|-----------------|-----------| -|`plugin.aes.client_private`|`/home/user/.mcollective.d/user-private.pem`|The private key path for the user. File must be `/\w\.\-/`| -|`plugin.aes.client_public`|`/home/user/.mcollective.d/user.pem`|The public key path for the user. File must be `/\w\.\-/`| - -### Server Options - -|Setting|Example / Default|Description -|-------|-----------------|-----------| -|`plugin.aes.client_cert_dir`|`/etc/mcollective/ssl/clients`|Where to store and load client public keys| -|`plugin.aes.server_private`|`/etc/mcollective/ssl/server-private.pem`|Server private key. File must be `/\w\.\-/`| -|`plugin.aes.server_public`|`/etc/mcollective/ssl/server-public.pem`|Server public key. File must be `/\w\.\-/`| -|`plugin.aes.enforce_ttl`|`1`|Enforce TTL and Message time security, warn only when disabled. 1.3.2 and newer only| - diff --git a/website/reference/plugins/security_ssl.md b/website/reference/plugins/security_ssl.md deleted file mode 100644 index 47fc426d..00000000 --- a/website/reference/plugins/security_ssl.md +++ /dev/null @@ -1,125 +0,0 @@ ---- -layout: default -title: OpenSSL based Security Plugin ---- -[SimpleRPCAuthorization]: /mcollective/simplerpc/authorization.html -[Registration]: registration.html -[AESPlugin]: security_aes.html -[SecurityOverview]: ../../security.html - -## Overview -Implements a public/private key based message validation system using SSL -public and private keys. - -Please review the [Security Overview][SecurityOverview] for a general discussion about security in Marionette Collective. - -The design goal of the plugin are two fold: - -* give different security credentials to clients and servers to avoid a compromised server from sending new client requests. -* create a token that uniquely identify the client - based on the filename of the public key. This creates a strong identity token for [SimpleRPCAuthorization][]. -* As of 1.3.2 it cryptographically protect the TTL and Message Time properties of requests. Aiding in securing against replay atacks. - -Serialization uses Marshal or YAML, which means data types in and out of mcollective -will be preserved from client to server and reverse. - -Validation is as default and is provided by *MCollective::Security::Base* - -Initial code was contributed by Vladimir Vuksan and modified by R.I.Pienaar - -An [alternative plugin][AESPlugin] exist that encrypts data but is more work to setup and maintain. -If you are using ActiveMQ middleware with CA-verified TLS configured, this plugin is usually adequate. - -## Setup - -### Nodes -To setup you need to create a SSL key pair that is shared by all nodes. - -The certificate names must match /\w\.\-/. - -{% highlight console %} - % openssl genrsa -out server-private.pem 1024 - % openssl rsa -in server-private.pem -out server-public.pem -outform PEM -pubout -{% endhighlight %} - -Distribute the private and public file to */etc/mcollective/ssl* on all the nodes. -Distribute the public file to */etc/mcollective/ssl* everywhere the client code runs. - -server.cfg: - -{% highlight ini %} - securityprovider = ssl - plugin.ssl_server_private = /etc/mcollective/ssl/server-private.pem - plugin.ssl_server_public = /etc/mcollective/ssl/server-public.pem - plugin.ssl_client_cert_dir = /etc/mcollective/ssl/clients/ -{% endhighlight %} - -TTL and Message Times are protected by default 2.0.0, this means older clients will not be able to -communicate with servers running this version of the security plugin. You can make it warn but not -deny older clients: - -{% highlight ini %} - plugin.ssl.enforce_ttl = 0 -{% endhighlight %} - -### Users and Clients -Now you should create a key pair for every one of your clients, here we create one -for user john - you could also if you are less concerned with client id create one -pair and share it with all clients: - -The certificate names must match /\w\.\-/. - -{% highlight console %} - % openssl genrsa -out john-private.pem 1024 - % openssl rsa -in john-private.pem -out john-public.pem -outform PEM -pubout -{% endhighlight %} - -Each user has a unique userid, this is based on the name of the public key. -In this example case the userid would be *'john-public'*. - -Store these somewhere like: - -{% highlight console %} - /home/john/.mc/john-private.pem - /home/john/.mc/john-public.pem -{% endhighlight %} - -Every users public key needs to be distributed to all the nodes, save the john one -in a file called: - -{% highlight console %} - /etc/mcollective/ssl/clients/john-public.pem -{% endhighlight %} - -If you wish to use [Registration][] or auditing that sends connections over MC to a -central host you will need also put the *server-public.pem* in the clients directory. - -You should be aware if you do add the node public key to the clients dir you will in -effect be weakening your overall security. You should consider doing this only if -you also set up an Authorization method that limits the requests the nodes can make. - -client.cfg: - -{% highlight ini %} - securityprovider = ssl - plugin.ssl_server_public = /etc/mcollective/ssl/server-public.pem - plugin.ssl_client_private = /home/john/.mc/john-private.pem - plugin.ssl_client_public = /home/john/.mc/john-public.pem -{% endhighlight %} - -If you have many clients per machine and dont want to configure the main config file -with the public/private keys you can set the following environment variables: - -{% highlight console %} - export MCOLLECTIVE_SSL_PRIVATE=/home/john/.mc/john-private.pem - export MCOLLECTIVE_SSL_PUBLIC=/home/john/.mc/john-public.pem -{% endhighlight %} - -### Serialization Method - -You can choose either YAML or Marshal, the default is Marshal. The view with optional Marshal encoding is to have a serializer supported by other languages other than Ruby to enable future integration with those. - -To use YAML set this in both *client.cfg* and *server.cfg*: - -{% highlight ini %} -plugin.ssl_serializer = yaml -{% endhighlight %} diff --git a/website/reference/plugins/validator.md b/website/reference/plugins/validator.md deleted file mode 100644 index c7af44fd..00000000 --- a/website/reference/plugins/validator.md +++ /dev/null @@ -1,127 +0,0 @@ ---- -layout: default -title: Validator Plugins ---- -[DDL]: /mcollective/reference/plugins/ddl.html - -## Overview -MCollective provides extensive input data validation to prevent attacks and -injections into your agents preventing attack vectors like Shell Injection -Attacks. - -Traditionally we shipped a number of pre-made validator plugins that could be -used in agents and DDL files but you were not capable fo adding your own easily. - -As of version 2.2.0 you can write new Validator plugins that allow you to extend -the DDL and Agent validation methods. - -## Writing A New Validator -We'll write a new validator plugin that can validate a string matches valid Exim -message IDs like *1Svk5S-0001AW-I5*. - -Validator plugins and their DDL files goes in the libdir in the *validator* -directory on both the servers and the clients. - -### The Ruby Plugin -The basic validator plugin that will validate any data against this regular -expression can be seen here: - -{% highlight ruby %} -module MCollective - module Validator - class Exim_msgidValidator - def self.validate(msgid) - Validator.typecheck(msgid, :string) - - raise "Not a valid Exim Message ID" unless msgid.match(/(?:[+-]\d{4} )?(?:\[\d+\] )?(\w{6}\-\w{6}\-\w{2})/) - end - end - end -end -{% endhighlight %} - -All you need to do is provide a *self.validate* method that takes 1 argument and -do whatever validation you want to do against the input data. - -Here we first confirm it is a string and then we do the regular expression match -against that. Any Exception that gets raised will result in validation failing. - -### The DDL -As with other plugins these plugins need a DDL file, all they support is the -metadata section. - -{% highlight ruby %} -metadata :name => "Exim Message ID", - :description => "Validates that a string is a Exim Message ID", - :author => "R.I.Pienaar ", - :license => "ASL 2.0", - :version => "1.0", - :url => "http://devco.net/", - :timeout => 1 -{% endhighlight %} - -## Using the Validator in a DDL -You can use the validator in any DDL file, here is a snippet matching an input -using the new *exim_msgid* validator: - -{% highlight ruby %} -action "retrymsg", :description => "Retries a specific message" do - display :ok - - input :msgid, - :prompt => "Message ID", - :description => "Valid message id currently in the mail queue", - :type => :string, - :validation => :exim_msgid, - :optional => false, - :maxlength => 16 - - output :status, - :description => "Status Message", - :display_as => "Status" -end -{% endhighlight %} - -Note here we are using our new validator to validate the *msgid* input. - -## Using the Validator in an Agent -Agents can also have validation, traditionally this included the normal things -like regular expressions but now here you can also use the validator plugins: - -{% highlight ruby %} -action "retrymsg" do - validate :msgid, :exim_msgid - - # call out to exim to retry the message -end -{% endhighlight %} - -Here we've extended the basic *validate* helper of the RPC Agent with our own -plugin and used it to validate a specific input. - -## Listing available Validators -You can obtain a list of validators using the *plugin* application: - -{% highlight ruby %} -% mco plugin doc - -Please specify a plugin. Available plugins are: - -. -. -. - -Validator Plugins: - array Validates that a value is included in a list - exim_msgid Validates that a string is a Exim Message ID - ipv4address Validates that a value is an ipv4 address - ipv6address Validates that a value is an ipv6 address - length Validates that the length of a string is less or equal to a specified value - regex Validates that a string matches a supplied regular expression - shellsafe Validates that a string is shellsafe - typecheck Validates that a value is of a certain type - -{% endhighlight %} - -Note our new *exim_msgid* plugin appears in this list. - diff --git a/website/reference/ui/filters.md b/website/reference/ui/filters.md deleted file mode 100644 index 084657e0..00000000 --- a/website/reference/ui/filters.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -layout: default -title: Discovery Filters ---- - -[FactPlugin]: /mcollective/reference/plugins/facts.html - -Using filters to control discovery and addressing is a key concept in mcollective. -You can use facts, classes, agents and server identities in filters and combine -to narrow down what hosts you will affect. - -To determine if your client support filters use the _--help_ option: - - -{% highlight console %} -$ mco rpc --help -. -. -. -Host Filters - -W, --with FILTER Combined classes and facts filter - -F, --wf, --with-fact fact=val Match hosts with a certain fact - -C, --wc, --with-class CLASS Match hosts with a certain config management class - -A, --wa, --with-agent AGENT Match hosts with a certain agent - -I, --wi, --with-identity IDENT Match hosts with a certain configured identity -{% endhighlight %} - -If you see a section as above then the client supports filters, this is the default -for all clients using SimpleRPC. - -All filters support Regular Expressions and some support comparisons like greater than -or less than. - -Filters are applied in a combined manner, if you supply 5 filters they must all match -your nodes. - -## Fact Filters - -Filtering on facts require that you've correctly set up a [FactPlugin][]. The examples below -show common fact filters. - -Install the ZSH package on machines with the fact _country=de_: - -{% highlight console %} -% mco rpc package install zsh -F country=de -{% endhighlight %} - -Install the ZSH package on machines where the _country_ fact starts with the letter _d_: - -{% highlight console %} -% mco rpc package install zsh -F country=/^d/ -{% endhighlight %} - -{% highlight console %} -% mco rpc package install zsh -F country=~^d -{% endhighlight %} - -Install the ZSH package on machines with more than 2 CPUs, other available operators -include _==, <=, >=, <, >, !=_. For facts where the comparison and the -actual fact is numeric it will do a numerical comparison else it wil do alphabetical: - -{% highlight console %} -% mco rpc package install zsh -F "physicalprocessorcount>=2" -{% endhighlight %} - -## Agent, Identity and Class filters - -These filters all work on the same basic pattern, they just support equality or regular -expressions: - -Install ZSH on machines with hostnames starting with _web_: - -{% highlight console %} -% mco rpc package install zsh -I /^web/ -{% endhighlight %} - -Install ZSH on machines with hostnames _web1.example.com_: - -{% highlight console %} -% mco rpc package install zsh -I web1.example.com -{% endhighlight %} - -## Combining Fact and Class filters - -As a short-hand you can combine Fact and Class filters into a single filter: - -Install ZSH on machines in Germany that has classes matching _/apache/_: - -{% highlight console %} -% mco rpc package install zsh -W "/apache/ country=de" -{% endhighlight %} - diff --git a/website/reference/ui/nodereports.md b/website/reference/ui/nodereports.md deleted file mode 100644 index 840bb777..00000000 --- a/website/reference/ui/nodereports.md +++ /dev/null @@ -1,173 +0,0 @@ ---- -layout: default -title: Node Inventory Reports -description: "Simple inventory reporting with MCollective." ---- - -[Subcollectives]: ../basic/subcollectives.html - -As we can retrieve all facts, classes, and agent plugins for all nodes, we can do some custom reporting on all of these. - -The _mco inventory_ tool is a generic node and network reporting tool. It also has basic scripting abilities. - -## Single Node View - -To obtain a full inventory for a given node you can run `mco inventory ` and receive a report about the node's details: - -{% highlight console %} -$ mco inventory node.example.com -Inventory for node.example.com: - - - Server Statistics: - Start Time: Mon Sep 13 18:24:46 +0100 2010 - Config File: /etc/mcollective/server.cfg - Process ID: 5197 - Total Messages: 62 - Messages Passed Filters: 62 - Messages Filtered: 0 - Replies Sent: 61 - Total Processor Time: 0.18 seconds - System Time: 0.01 seconds - - Agents: - discovery echo nrpe - package process puppetd - rpctest service - - Configuration Management Classes: - aliases apache - - - Facts: - architecture => i386 - country => de - culturemotd => 1 - customer => rip - diskdrives => xvda - -{% endhighlight %} - -## Collective List - -MCollective nodes can be assigned to any number of [Subcollectives][], and you can use the inventory application to get an overview of all known collectives: - -{% highlight console %} -$ mco inventory --list-collectives - - * [ ===================================== ] 52 / 52 - - Collective Nodes - ========== ===== - za_collective 2 - us_collective 7 - uk_collective 19 - de_collective 24 - eu_collective 45 - mcollective 52 - - Total nodes: 52 - -{% endhighlight %} - -## Collective Map - -You can also create a DOT format graph of your collective: - -{% highlight console %} -$ mco inventory --collective-graph collective.dot - -Retrieving collective info.... -Graph of 52 nodes has been written to collective.dot -{% endhighlight %} - -The graph will be a simple dot graph that can be viewed with Graphviz, Gephi or -other compatible software. - -## Custom Reports - -You can create little scriptlets and pass them into *mco inventory* with the *--script* option. - -You have the following data available to your reports: - -Variable | Description ----------|--------------------------------------------------------- -time | The time the report was started (normal Ruby Time object) -identity | The sender id -facts | A hash of facts -agents | An array of agents -classes | An array of CF Classes - -### Printf Style Reports - -Say you need a report of all your IBM hardware listing hostname, serial number, and product name. You can write a scriptlet like this: - -{% highlight ruby linenos %} -# ./inventory.mc -inventory do - format "%s:\t\t%s\t\t%s" - - fields { [ identity, facts["serialnumber"], facts["productname"] ] } -end -{% endhighlight %} - -Assuming you saved the file as _inventory.mc_, run the inventory application like this: - -{% highlight console %} - % mco inventory -W "productname=/IBM|BladeCenter/" --script inventory.mc - xx12: 99xxx21 BladeCenter HS22 -[7870B3G]- - xx9: 99xxx46 BladeCenter HS22 -[7870B3G]- - xx10: 99xxx29 BladeCenter HS22 -[7870B3G]- - yy1: KDxxxFR IBM System x3655 -[79855AY]- - xx5: 99xxx85 IBM eServer BladeCenter HS21 -[8853GLG]- - -{% endhighlight %} - -### Perl Format Style Reports - -To use this you need to install the _formatr_ gem; once that's installed you can create a report scriptlet like below: - -{% highlight ruby linenos %} -# ./distro_inventory.mc -formatted_inventory do - page_length 20 - - page_heading < 
- -## 2.12.0 - 2017/03/20 - -### Changes since 2.11.4 - -|Date|Description|Ticket| -|----|-----------|------| -|2017/09/22|Allow default batch options to be set|MCO-818| -|2017/09/18|Use native array math to calculate responses more quickly|MCO-818| -|2017/08/02|Correctly determine reply status with JSON serializer|MCO-815| -|2017/06/16|Update stomp gem to 1.4.4 to fix SSL|| -|2017/06/07|Update rubocop/securitycop scans and convert YAML.load to safe_load for facts|MCO-807| - -  - -## 2.11.4 - 2017/10/26 - -### Changes since 2.11.3 - -|Date|Description|Ticket| -|----|-----------|------| -|2017/10/26|Release *2.11.4*|| -|2017/09/25|Restarting the mcollective service no longer kills running agent subprocesses|MCO-816| - -  - -## 2.11.3 - 2017/09/20 - -### Changes since 2.11.2 - -|Date|Description|Ticket| -|----|-----------|------| -|2017/09/20|Release *2.11.3*|| -|2017/09/18|Speed up calculation of no responses and unexpected responses|MCO-818| - -  - -## 2.11.2 - 2017/08/15 - -### Changes since 2.11.1 - -|Date|Description|Ticket| -|----|-----------|------| -|2017/08/15|Release *2.11.2*|| -|2017/08/02|Correctly determine reply status in all modes|MCO-815| - -  - -## 2.11.1 - 2017/07/18 - -### Changes since 2.11.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2017/07/18|Release *2.11.1*|| -|2017/06/28|Use OpenSSL::Cipher instead of OpenSSL::Cipher::Cipher to avoid warnings with Ruby 2.4|MCO-813| -|2017/06/28|Use Mutex instead of Thread.exclusive to avoid warnings with Ruby 2.4|MCO-812| -|2017/06/28|Use Integer instead of Fixnum to avoid warnings with Ruby 2.4|MCO-811| - -  - -## 2.11.0 - 2017/06/21 - -### Changes since 2.10.5 - -|Date|Description|Ticket| -|----|-----------|------| -|2017/06/21|Release *2.11.0*|| -|2017/06/21|MCollective client now logs sending a request at INFO level with additional details, and server logs handling those requests at INFO level with similar details to help identify corresponding events.|MCO-784| -|2017/06/21|The default location for mcollective server logs is moved from `/var/log/puppetlabs` to `/var/log/puppetlabs/mcollective` (on Unix). No change has been made to Windows log locations.|MCO-783| -|2017/06/21|MCollective will consider symbols and their string representation equivalent for serializing/deserializing and accessing message keys as long as they're unambiguous.|MCO-799| -|2017/06/21|mcollective-client is now compatible with OpenSSL 1.1.0|MCO-804| -|2017/06/21|systemu has been upgraded to a more recent version that fixes an issue marshaling multi-byte characters.|MCO-806| - -  - -## 2.10.6 - 2017/10/26 - -### Changes since 2.10.5 - -|Date|Description|Ticket| -|----|-----------|------| -|2017/10/26|Release *2.10.6*|| -|2017/09/25|Restarting the mcollective service no longer kills running agent subprocesses|MCO-816| - -  - -## 2.10.5 - 2017/06/09 - -### Changes since 2.10.4 - -|Date|Description|Ticket| -|----|-----------|------| -|2017/06/09|Release *2.10.5*|| -|2017/06/07|Fix acceptance source for JDK on Windows|MCO-808| - -  - -## 2.10.4 - 2017/04/04 - -### Changes since 2.10.3 - -|Date|Description|Ticket| -|----|-----------|------| -|2017/05/11|Release *2.10.4*|| -|2017/04/06|Switch to using YAML.safe_load|MCO-794| - -  - -## 2.10.3 - 2017/04/04 - -### Changes since 2.10.2 - -* Various testing fixes - -|Date|Description|Ticket| -|----|-----------|------| -|2017/04/04|Release *2.10.3*|| -|2017/03/30|Allow M::Client users to access M::Message|MCO-790| - -  - -## 2.10.2 - 2017/03/09 - -### Changes since 2.10.1 - -* Various testing fixes - -|Date|Description|Ticket| -|----|-----------|------| -|2017/03/09|Release *2.10.2*|| - -  - -## 2.10.1 - 2017/02/09 - -### Changes since 2.10.0 - -* Fixed a regression in rpc requests using the `--batch` option. - -|Date|Description|Ticket| -|----|-----------|------| -|2017/02/29|Release *2.10.1*|| -|2017/02/06|Fix regression in --batch option|MCO-785| - -  - -## 2.10.0 - 2017/01/23 - -### Changes since 2.9.1 - -* rpc queries will now wait for all discovered nodes to respond, and report responses -that came from undiscovered nodes -* Add activemq.agents_multiplex and rabbitmq.agents_multiplex options to replace the -per-agent destinations with a single one for all agents -* Use publish_timeout from config -* Output from a PQL query can be piped to STDIN for node discovery - -|Date|Description|Ticket| -|----|-----------|------| -|2017/01/23|Release *2.10.0*|| -|2016/12/30|Updates for acceptance testing|MCO-782| -|2016/12/20|Fix broken link on plugin index page|DOCUMENT-618| -|2016/12/15|Fix `mco ping` breakage from prior commit in MCO-777|MCO-777| -|2016/12/08|Remove references to old PE versions|| -|2016/11/21|Wait for all expected responses to rpc, report surprises|MCO-777| -|2016/11/16|Singletarget messaging|MCO-736| -|2016/11/07|Gemfile cleanup|| -|2016/11/04|Use publish_timeout from config|MCO-778| -|2016/11/01|Support PQL in STDIN discovery|MCO-776| - -  - -## 2.9.1 - 2016/10/24 - -### Changes since 2.9.0 - -* Ping application includes discovery_timeout so timeout can be increased via the config file - -|Date|Description|Ticket| -|----|-----------|------| -|2016/10/24|Release *2.9.1*|| -|2016/09/23|Ping application includes discovery_timeout|MCO-775| - - -  - -## 2.9.0 - 2016/08/05 - -### Changes since 2.8.9 - -* Updated Stomp gem dependency to >= 1.4.1 for SSL-related fixes -* Fixed mco plugins installation when using puppet-agent on Debian systems -* Added runtime username/password input to connect to middleware - -|Date|Description|Ticket| -|----|-----------|------| -|2016/08/05|Release *2.9.0*|| -|2016/06/24|Update Stomp gem dependency to >= 1.4.1|RE-7302| -|2016/06/13|acceptance: allow plugin versioned install|| -|2016/06/01|Update README link to documentation|| -|2016/05/24|Fix mco plugins installation when using puppet-agent on Debian family systems|MCO-688| -|2016/05/19|Move prepare_installation to run first|MCO-763| -|2016/05/17|Add --no-batch-files option|MCO-762| -|2016/05/17|Runtime username/password input to connect to middleware|MCO-760| - - -  - -## 2.8.9 - 2016/06/27 - -### Changes since 2.8.8 - -* Improved parsing of quoted strings in discovery filter expressions -* Dropped use of eval on unchecked strings - CVE-2016-2788 -* Updated documentation -* Fixed ActiveMQ install -* Worked around RuboCop parser change -* Restored support for running tests on ruby 1.8.7 - - -|Date|Description|Ticket| -|----|-----------|------| -|2016/06/20|Make parsing of quoted strings in discovery filter expressions side effects free|| -|2016/06/20|Do not use eval on unchecked strings in discovery filter expressions|MCO-765| -|2016/05/17|Link to PE docs|(#373)[https://github.com/puppetlabs/marionette-collective/pull/373]| -|2016/05/17|Update installation docs|(#376)[https://github.com/puppetlabs/marionette-collective/pull/376]| -|2016/05/11|Fix ActiveMQ install|(#378)[https://github.com/puppetlabs/marionette-collective/pull/378]| -|2016/05/03|Work around RuboCop parser change causing errors on non UTF-8 compliant strings|(#375)[https://github.com/puppetlabs/marionette-collective/pull/375]| -|2016/05/03|Pin rake to 10.4 required for running tests on ruby 1.8.7|(#375)[https://github.com/puppetlabs/marionette-collective/pull/375]| - - -  - -## 2.8.8 - 2016/02/25 - -### Changes since 2.8.7 - -* Fixed pidfile handling to prevent multiple running daemon processes. -* Added windows support to install.rb - - -|Date|Description|Ticket| -|----|-----------|------| -|2016/02/25|Fix dependency on aio for creating the pidfile directory|MCO-753| -|2016/02/18|Improve pidfile handling to avoid running multiple daemons|MCO-751| -|2016/02/18|Update build targets to LTS debian variants|MCO-634| -|2016/02/05|Update mco.bat file to allow for absence of CONFIG_FILE setting|MCO-748| -|2016/01/22|Add windows support to install.rb|MCO-745| - - -  - -## 2.8.7 - 2016/01/13 - -### Changes since 2.8.6 - -* Fixed logrotate on systemd-based systems. -* Fixed negative data plugin comparisons. -* Fixed run helper on systems where the path to ruby includes spaces. - - -|Date|Description|Ticket| -|----|-----------|------| -|2015/12/17|Fix systemd logrotate unit|MCO-744| -|2015/12/16|Fix run helper for systems where the path to ruby binary includes spaces|MCO-742| -|2015/12/01|Fix negative data plugin comparisons|MCO-739| - - -  - -## 2.8.6 - 2015/09/15 - -### Changes since 2.8.5 - -* Fixed an issue with the solaris smf service in AIO. The service manifest listed the -daemon binary in the wrong location (/opt/puppetlabs/bin as opposed to -/opt/puppetlabs/puppet/bin) - -|Date|Description|Ticket| -|----|-----------|------| -|2015/09/11|Fix solaris smf service manifest for aio|(#345)[https://github.com/puppetlabs/marionette-collective/pull/345]| - - -  - -## 2.8.5 - 2015/09/10 - -### Changes since 2.8.4 - -* Added condrestart action to suse AIO init script - - -|Date|Description|Ticket| -|----|-----------|------| -|2015/08/25|Add condrestart to the suse init script for AIO|RE-11690| - - -  - -## 2.8.4 - 2015/08/21 - -### Changes since 2.8.3 - -* Changed the OSX service name back to the 2.8.2 value - (com.puppetlabs.mcollective -> mcollective) as changing the service - name in a point release is too disruptive. - - -|Date|Description|Ticket| -|----|-----------|------| -|2015/08/20|Revert RE-5032|MCO-705| - - -  - -## 2.8.3 - 2015/08/18 - -### Bug fixes and improvements since 2.8.2 - -* Added a `describe_filter` application -* Fixed handling of quoted strings in compound filters -* Adds a solaris smf definition for puppet-agent -* Fixes for OSX service plist used by puppet-agent - -### `mco describe_filter` application - -In order to help you understand what the filtering options will -translate into when discovering nodes we've added a `describe_filter` -application. - -Some exmples of this might be, find nodes with the apache configuraion -class: - -{% highlight shell %} -$ mco describe_filter -C apache --C filter expands to the following class checks: - - Check if class 'apache' is present on the host -{% endhighlight %} - -Find hosts with the wizard class that came from the moon: - -{% highlight shell %} -$ mco describe_filter -S 'wizard and source="moon"' --S Query expands to the following instructions: - - Check if class 'wizard' is present on the host - AND - Check if fact 'source' = 'moon' -{% endhighlight %} - - -### Changes since 2.8.2 - -|Date|Description|Ticket| -|----|-----------|------| -|2015/08/05|Add solaris smf service for AIO|MCO-687| -|2015/07/17|Fully qualify the label in the osx mco plist|RE-5032| -|2015/07/13|Set character encoding in the OSX plist|(#326)[https://github.com/puppetlabs/marionette-collective/pull/326]| -|2015/07/03|Fix documentation links to use https|DOCS-2092| -|2015/06/05|Add `mco describe_filter` application|MCO-668| -|2015/06/05|Fix quote handling in compound query language|MCO-668| -|2015/06/04|Add acceptance tests|MCO-671| - - -  - -## 2.8.2 - 2015/05/19 - -### Bug fixes and improvements since 2.8.1 - -* `mco notacommand` now tells you where we looked for application plugins -* Exit code of `mco notacommand` now indicates failure -* Fixes to the init script used by SUSE under puppet-agent - -#### `mco nosuchapplication` behaviour changes - -The behaviour of the `mco` command when a subcommand is absent has -been changed to indicate failure in its exit code. We also now tell -you where we would have looked for the application plugins. - -{% highlight shell %} -$ mco notacommand -The Marionette Collective version 2.8.2 - -Unknown command 'notacommand', searched for applications in: - - /Users/richardc/src/mcollective/lib - /Users/richardc/.gem/ruby/1.9.3/gems/stomp-1.3.4/lib - /opt/rubies/ruby-1.9.3-p547/lib/ruby/site_ruby/1.9.1 - /opt/rubies/ruby-1.9.3-p547/lib/ruby/site_ruby/1.9.1/x86_64-darwin13.4.0 - /opt/rubies/ruby-1.9.3-p547/lib/ruby/site_ruby - /opt/rubies/ruby-1.9.3-p547/lib/ruby/vendor_ruby/1.9.1 - /opt/rubies/ruby-1.9.3-p547/lib/ruby/vendor_ruby/1.9.1/x86_64-darwin13.4.0 - /opt/rubies/ruby-1.9.3-p547/lib/ruby/vendor_ruby - /opt/rubies/ruby-1.9.3-p547/lib/ruby/1.9.1 - /opt/rubies/ruby-1.9.3-p547/lib/ruby/1.9.1/x86_64-darwin13.4.0 - /Users/richardc/src/mcollective/lib/mcollective/vendor/systemu/lib - -Known commands: - - completion facts find - help inventory ping - plugin rpc - -Type 'bin/mco help' for a detailed list of commands and 'bin/mco help command' -to get detailed help for a command -$ echo $? -1 -{% endhighlight %} - -### Changes since 2.8.1 - -|Date|Description|Ticket| -|----|-----------|------| -|2015/05/12|Do not build for debian stable or testing (target codenames instead)|MCO-665| -|2015/05/08|Exit non-zero when `mco` is called with a non-existent subcommand|MCO-640| -|2015/05/08|Add legacy `libdir` settings to aio sample config|MCO-641| -|2015/05/08|Downgrade warning on absent `libdir`|MCO-647| -|2015/05/04|Add OSX plist for AIO|MCO-646| -|2015/04/23|Drop lucid from build targets|MCO-638| -|2015/04/13|Drop fedora 19 from build targets|MCO-633| -|2015/03/22|Update AIO SUSE init script|RE-3977| - - -  - -## 2.8.1 - 2015/03/11 - -### Bug fixes since 2.8.0 - -* Fixed loading of the mcollective-client gem -* debian init condrestart action fixed for AIO - -### Configuration path changes for Puppet Labs All-In-One Agent - -Client applications will now use the first readable config file of -`~/.mcollective`, `/etc/puppetlabs/mcollective/client.cfg`, -`/etc/mcollective/client.cfg` when no configuration file is specified. - -The MCollective daemon will now use the first readable config file of -`/etc/puppetlabs/mcollective/server.cfg`, -`/etc/mcollective/server.cfg` when no configuration file is specified. - -*Note:* these are different to the paths announced in the 2.8.0 release -and may be a source of incompatibility if you have already rearranged -your files. - -### Changes since 2.8.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2015/03/05|Maintain version number in-tree|MCO-617| -|2015/02/18|Use updated AIO paths|MCO-594| -|2015/02/10|Fix AIO debian condrestart action|MCO-591| -|2015/02/05|Fix problems with 2.8.0 gem loading|MCO-587| - - -  - -## 2.8.0 - 2015/02/04 - -### New Features and Improvements from 2.7.0 - -* Puppet Labs All-In-One Agent paths are now consulted in preference - to traditional paths -* core plugins now live in lib, are installed into sitelibdir -* $libdir is now optional and extends the ruby $LOAD_PATH -* rubocop policy violations now cause Travis CI build failures - -### Configuration path changes for Puppet Labs All-In-One Agent - -Client applications will now use the first readable config file of -`~/.mcollective`, `/etc/puppetlabs/agent/mcollective/client.cfg`, -`/etc/mcollective/client.cfg` when no configuration file is specified. - -The MCollective daemon will now use the first readable config file of -`/etc/puppetlabs/agent/mcollective/server.cfg`, -`/etc/mcollective/server.cfg` when no configuration file is specified. - -This is to support the forthcoming All-In-One Agent packages from -Puppet Labs, which you can read about [here][aio]. - -[aio]: https://groups.google.com/d/msg/puppet-dev/qZ-nOvmfrig/htvN7tyo_1YJ - -### $libdir/$LOAD_PATH changes and core plugins - -What would have been known as the core plugins now live alongside the -core MCollective libraries and will be installed into ruby's -sitelibdir on installation. - -In addition to this we have changed the behaviour of plugin loading so -that all of ruby's $LOAD_PATH is consulted, the $libdir configuration -file directive now works as a way to add entries to the start of this -search path. - -The sum of these changes will make the mcollective-client gem usable -as a self-contained client, just supply `~/.mcollective`. - -### Bug fixes since 2.7.0 - -* Fixed crashing bug caused by interaction of autoload and threads -* Fixed `mco facts` when no fact value is returned - -### Changes since 2.7.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2015/02/02|Add AIO init scripts to ext/aio|MCO-555| -|2015/02/02|Move core plugins into sitelibdir|MCO-583| -|2015/01/29|Prefer configuration files from AIO paths|MCO-560| -|2015/01/29|Use $LOAD_PATH for loading plugins|MCO-315| -|2015/01/28|Replace uses of `autoload` with `require`|MCO-580| -|2015/01/21|Fix `mco facts` in absence of fact|MCO-558| -|2015/01/07|Ensure rubocop failures fail the build|MCO-519| -|2014/12/19|Fix powershell exit code interactions|MCO-550| - - -  - -## 2.7.0 - 2014/12/02 - - -### New Features and Improvements from 2.6.1 - -* A collective data plugin has been added -* `mco` now supports a --connection-timeout option -* The target collective is now visible to custom discovery plugins -* implemented_by now searches a more conventional path -* `mco plugin package` now supports 'lib' layout -* The version of the stomp gem in use is now logged at connector startup -* We now log the senderid of a successfully decoded frame at debug -* Default values for STOMP 1.1 heart-beating have been revisited - - -### New collective data plugin, and collective filtering - -We have added a collective data plugin, which enables you to filter -based on collective membership. - -For example, to find nodes connected to the `all` collective which are -also members of the `us` collective you can use the following compound -query: - -{% highlight console %} -$ mco find -T all -S 'collective("us").member=true' -{% endhighlight %} - - -### --connection-timeout client option - -`mco` now supports a --connection-timeout option (connection_timeout -in a client configuration file). It will cause the mcollective client -to abort if a connection to the middleware cannot be established in -the specified number of seconds (the default value is unspecified - no -timeout). - -{% highlight shell %} -$ mco ping --connection-timeout 5 -error 2014/11/24 17:10:47: activemq.rb:149:in `on_ssl_connectfail' SSL session creation with stomp+ssl://mcollective@127.0.0.1:61614 failed: Connection refused - connect(2) -error 2014/11/24 17:10:47: activemq.rb:149:in `on_ssl_connectfail' SSL session creation with stomp+ssl://mcollective@127.0.0.1:61614 failed: Connection refused - connect(2) -error 2014/11/24 17:10:47: activemq.rb:149:in `on_ssl_connectfail' SSL session creation with stomp+ssl://mcollective@127.0.0.1:61614 failed: Connection refused - connect(2) -error 2014/11/24 17:10:47: activemq.rb:149:in `on_ssl_connectfail' SSL session creation with stomp+ssl://mcollective@127.0.0.1:61614 failed: Connection refused - connect(2) -error 2014/11/24 17:10:47: activemq.rb:149:in `on_ssl_connectfail' SSL session creation with stomp+ssl://mcollective@127.0.0.1:61614 failed: Connection refused - connect(2) -error 2014/11/24 17:10:47: activemq.rb:149:in `on_ssl_connectfail' SSL session creation with stomp+ssl://mcollective@127.0.0.1:61614 failed: Connection refused - connect(2) -error 2014/11/24 17:10:47: activemq.rb:149:in `on_ssl_connectfail' SSL session creation with stomp+ssl://mcollective@127.0.0.1:61614 failed: Connection refused - connect(2) -error 2014/11/24 17:10:48: activemq.rb:149:in `on_ssl_connectfail' SSL session creation with stomp+ssl://mcollective@127.0.0.1:61614 failed: Connection refused - connect(2) -error 2014/11/24 17:10:49: activemq.rb:149:in `on_ssl_connectfail' SSL session creation with stomp+ssl://mcollective@127.0.0.1:61614 failed: Connection refused - connect(2) -error 2014/11/24 17:10:52: client.rb:39:in `rescue in initialize' Timeout occured while trying to connect to middleware - -The ping application failed to run, use -v for full error backtrace details: execution expired -{% endhighlight %} - -This is complementary to the `plugin.activemq.max_reconnect_attempts` -or `plugin.rabbitmq.max_reconnect_attempts` parameters available to the -activemq and rabbitmq connectors. - - -### STOMP 1.1 heart-beat values - -The initial default values for `plugin.activemq.max_hbrlck_fails` and -`plugin.rabbitmq.max_hbrlck_fails` have been changed to 0. This is -more appropriate for MCollective's usage pattern as the main receiver -thread will block the heartbeat threads ability to claim the read -lock on the connector socket. - -If you have previously specified a value for this parameter, we -suggest you use the new default of 0. - - -### lib layout and `mco plugin package` - -The traditional way to layout an MCollective plugin in your source -repository is what we are now referring to as 'flat' layout, and looks -like this: - -{% highlight console %} -$ tree -. -├── agent -│   ├── flat.ddl -│   └── flat.rb -└── spec - └── unit - └── flat_agent_spec.rb -{% endhighlight %} - - -A similar agent plugin in lib layout will look like this: - -{% highlight console %} -$ tree -. -├── lib -│   └── mcollective -│   └── agent -│   ├── lib.ddl -│   └── lib.rb -└── spec - └── unit - └── mcollective - └── agent - └── lib_spec.rb -{% endhighlight %} - -You'll see that under lib layout the paths now more closely match the -namespaces in the files, and also the structure you use when -installing the agent onto a target system. This means that you can -more readily test a plugin under development by adding something like -the following to your configuration files: - -{% highlight ini %} -libdir = /usr/src/mcollective-libdemo-agent/lib -{% endhighlight %} - -In order to allow you to adopt lib layout more easily, `mco plugin -package` has been updated to support it. - -### implemented_by path changes - -As originally written, the `implemented_by $command` feature searches -for the command in `$libdir/agent/$agentname/$command` when the path -is not fully-qualified. This was potentially confusing as the main -agent implementation file is in -`$libdir/mcollective/agent/$agentname.rb`. - -With this release we now search -`$libdir/mcollective/agent/$agentname/$command` in addition to -`$libdir/agent/$agentname/$command`, and will invoke the command from -the former location if the command exists in both locations. - -In MCollective 2.8.0 we will warn more strongly if we find a the -external helper in both locations, and remove the search of the old -path in 2.9.0. - - -### Bug fixes since 2.6.1 - -* Exceptions raised in the runner are now re-raised in the main thread -* Windows service_manager.rb now more aggressively tests for a ruby binary, avoiding broken installs - - -### Changes since 2.6.1 - -|Date|Description|Ticket| -|----|-----------|------| -|2014/11/24|Revisit STOMP 1.1 heart-beat defaults|MCO-522| -|2014/11/18|Log the senderid of messages at debug|MCO-521| -|2014/11/11|Add a --connection-timeout to the client options|MCO-464| -|2014/11/11|Add search of `$libdir/mcollective/agent/$agent/$action` to `implemented_by`|MCO-466| -|2014/11/10|`mco plugin package` support for 'lib' layout|MCO-314| -|2014/10/28|Add rubocop style checks to the codebase|MCO-136| -|2014/10/22|Expose target collective to custom discovery plugins|MCO-456| -|2014/10/13|Make windows service_manager.rb helper more vigorous in finding ruby|MCO-465| -|2014/10/02|Reraise exceptions caught by the runner thread in the main thread|MCO-475| -|2014/10/01|Add a collective data plugin|MCO-472| -|2014/10/01|Update windows scripts to pass --daemonize to daemon|MCO-474| -|2014/09/20|Report the version of stomp gem at startup|MCO-470| -|2014/09/19|Removed vendoring of the json gem|MCO-457| - - -  - -## 2.6.1 - 2014/10/29 - - -### Improvements from 2.6.0 - -* We now preconfigure the SSL context used to disallow the SSLv2 and SSLv3 protocols. -* The rabbitmq and activemq connectors now allow you to specify the - desired set of SSL ciphers. - -### Connector cipher specification - -The rabbitmq and activemq connector plugins now allow the -specification of ciphers to be used in the connection to the -middleware. - -To request the same set of cipher that ruby 2.1.2 defaults to you -could specify: - - # ciphers from ruby 2.1.2 - plugin.activemq.pool.1.ssl.ciphers = ALL:!ADH:!EXPORT:!SSLv2:RC4+RSA:+HIGH:+MEDIUM:+LOW - - -If not specified the default set of ciphers chosen will depend on a -combination of the version of the stomp gem in use and your version of -ruby. - -See the OpenSSL documentation for further explanation of what these cipher -strings mean. - -https://www.openssl.org/docs/apps/ciphers.html#CIPHER_STRINGS - - -### Changes since 2.6.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2014/10/23|Disable SSLv2 and SSLv3 protocols by default|MCO-489| -|2014/10/16|Expose SSL cipher selection via connector settings|MCO-486| - - -  - -## 2.6.0 - 2014/08/28 - - - -### New Features and Improvements from 2.5.3 - - * `mcollectived` now supports the command line options `--no-daemonize` and `--daemonize` - * Connector plugins now require DDL files - * The base64_decode method is now stricter, and will report errors from the correct place in the calling hierarchy - * Structured facts are now supported in simple discovery and compound filters - * Several actions now choose the number of display columns dynamically based on the values they are presenting - * rpc clients now respect a `--sort` option - * `mcollectived` now responds to the SIGWINCH signal to perform log rotation - * The `--batch` flag can now by used to specify percentages - * Agent loading can now be globally defaulted to false - * A new option `registration_splay` has been added to defer registration on startup - * `discovery_timeout` can now be specified in the client.cfg - * `soft_shutdown` is now configurable with a `soft_shutdown_timeout` option - * We now use a distinct reply queue per request, which should perform better at scale - -### Structured fact support - -We have added two mechanisms for dealing with structured facts when -filtering nodes. - -For the simple form of fact matching, --with-fact (-F) will now match -to array elements or hash keys where the value of a fact is a hash or -array. - -Given the set of facts: - - { - "baz": [ "a", "b" ], - "quux": { "foo": "flirble" }, - } - -These mco ping invocations would match: - - mco ping --with-fact 'baz=a' - mco ping --with-fact 'quux=foo' - -And the following would not: - - mco ping --with-fact 'baz=c' - mco ping --with-fact 'quux=flirble' - - -There is more complex and powerful mechanism available via the fact -data plugin which is exposed by the compound filter language use by -the `--select` (`-S) switch. It allows you to navigate the structured -facts with a path delimited by periods. - -Given these facts: - - { - "foo": "bar", - "baz": [ "a", "b" ], - "numbers": [ 6, 2, 1 ], - "quux": { "foo": "flirble" }, - } - -The following invocations would match: - - mco ping --select 'fact("foo").value=bar' - mco ping --select 'fact("baz.0").value=a' - mco ping --select 'fact("quux.foo").exists=true' # checks for existence of the key - mco ping --select 'fact("quux.foo").value=flirble' - - -### `registration_splay` - -Registration can now be delayed from sending the initial registration -message (splayed) with the `registration_splay` option. In the -following configuration the first registration message will be sent -after a random delay of up to 600 seconds, and then subsequent -registration messages will be every 600 seconds. - - # server.cfg - registration = 600 - registration_splay = true - -This can reduce load spikes on your middleware if you choose to -restart your agents in batches. - -### Changes to agent loading - -It is now possible to change the default behavior for agent loading -with the `activate_agents` option which complements the -`plugin.$plugin_name.activate_agent` settings. It defaults to `true` -which is the behavior in previous versions of MCollective. - -In this example we only enable the service and package agents, rather -than all agents installed: - - # server.cfg - activate_agents = false - plugin.service.activate_agent = true - plugin.package.activate_agent = true - -### mco rpc actions now have a '--sort' option - -It is now possible to order the results in an rpc result set with the -`--sort` flag. This adds a small overhead, and so is off by default. - - -{% highlight shell %} -$ mco rpc rpcutil ping --sort -Discovering hosts using the mc method for 2 second(s) .... 5 - -* [ ============================================================> ] 5 / 5 - - -server-0 - Timestamp: 1408313208 - -server-1 - Timestamp: 1408313208 - -server-2 - Timestamp: 1408313208 - -server-3 - Timestamp: 1408313208 - -server-4 - Timestamp: 1408313208 - - - -Finished processing 5 / 5 hosts in 10.29 ms -{% endhighlight %} - - - -### Bug fixes since 2.5.3 - - * Fixed the exitcode of `mco ping` - * Fixed the flow of rpc response processing - * Fixed reply-to behavior in the rabbitmq connector - * Fixed `call_agent_batched` to work with activerecord - * It is now possible to reset `limit_targets` - * Fixes to signal handling were made for ruby 2 compatibility - * It is now possible to unset `LC_ALL` when using the shell helper - * Fixed a race condition in validation plugin loader - - -### Backwards Compatibility and Upgrading - -If you are using a non-standard connector plugin, you will need to -ensure it has a DDL file or MCollective will refuse to use it. - -### Changes since 2.5.3 - -|Date|Description|Ticket| -|----|-----------|------| -|2014/08/20|Fix a race condition in type validator plugin loader|MCO-453| -|2014/08/12|Use a distinct reply queue per request|MCO-443| -|2014/08/07|Add `soft_shutdown_timeout` option|MCO-243| -|2014/08/06|Add documentation of heartbeat options to the connector pages|MCO-175| -|2014/08/01|Move signal handling into threads (ruby 2 compatibility)|MCO-421| -|2014/08/01|Allow LC\_ALL environment variable to be unset|MCO-156| -|2014/08/01|Add `discovery_timeout` to the configuration file|MCO-193| -|2014/08/01|Add `registration_splay` configuration option|MCO-272| -|2014/07/31|Allow for agent loading to be globally defaulted|MCO-408| -|2014/07/31|Change the 'expired message' message to indicate the message is being discarded|MCO-418| -|2014/07/31|Allow the --batch flag to specify percentages|MCO-68| -|2014/07/31|Reopen logfiles on SIGWINCH|MCO-328| -|2014/07/31|Add --sort option to rpc clients|MCO-83| -|2014/07/31|Dynamically decide number of columns for output|PR#215| -|2014/07/31|Specify the --daemonize option in sample init scripts|MCO-416| -|2014/07/29|Allow `limit_targets` to be reset|MCO-93| -|2014/07/29|Fixed rabbitmq reply-to under `use_reply_exchange`|MCO-351| -|2014/07/29|Reworked examples of catching uncatchable errors|MCO-411| -|2014/07/23|Make the base64 decoder more strict|MCO-293| -|2014/07/22|Add support for structured facts|MCO-363| -|2014/07/18|Fix `call_agent_batched` to work with activerecord|MCO-205| -|2014/07/18|Fix rpc response processing for bad replies|MCO-264| -|2014/07/17|Fix direct addressing regression introduced in MCO-360|MCO-410| -|2014/07/17|Require connector plugins to have ddls|MCO-407| -|2014/07/17|Add ddls to `activemq` and `rabbitmq` connectors|MCO-406| -|2014/07/16|Fix halt\_code to return the correct exitcode for `mco ping`|MCO-199| -|2014/07/16|Remove all reference to the 'mcollective' agent|MCO-360| -|2014/07/01|Add --no-daemonize and --daemonize option to mcollectived|MCO-181| -|2014/05/21|Fix a url in the solaris readme|MCO-186| - -  - -## 2.5.3 - 2014/07/15 - -### Bug fixes since 2.5.2 - -* Address potential flaw in aes security plugin - CVE-2014-3251 -* Fix data plugin load ordering - -### Changes since 2.5.2 - -|Date|Description|Ticket| -|----|-----------|------| -|2014/06/20|Address potential flaw in aes security plugin. CVE-2014-3251|MCO-329| -|2014/05/30|Fix data plugin load ordering|MCO-346| - - -  - -## 2.5.2 - 2014/06/10 - -### Bug fixes since 2.5.1 - -* Remove '.' from ruby `$LOAD_PATH` - CVE-2014-3248 - -### Changes since 2.5.1 - -|Date|Description|Ticket| -|----|-----------|------| -|2014/05/27|Remove '.' from ruby `$LOAD_PATH` CVE-2014-3248|MCO-311| - - -  - -## 2.5.1 - 2014/05/14 - -### Bug fixes since 2.5.0 - -* Improve line parsing in flatfile discovery -* Remove unused/broken `mc-call-agent` binary -* Fix `mco plugin package` for RedHat derivatives -* Allow stomp login/passcode to be optional - -### Changes since 2.5.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2014/05/12|Allow stomp login/passcode to be optional|MCO-316| -|2014/05/02|Fix dependencies of rpms from `mco plugin package`|MCO-292| -|2014/05/01|Fix rpmbuild error in `mco plugin package`|MCO-285| -|2014/04/24|Stop install mc-call-agent|MCO-266| -|2014/04/24|Improve line parsing in flatfile discovery|MCO-262| - - -  - -## 2.5.0 - 2014/04/23 - -### New Features and Improvements - - * MCollective should generally fail less awkwardly when dealing with middleware problems - * Added a new `soft_shutdown` option to allow agents to complete or timeout before exiting the daemon - -As part of the improvements we've made to connectors and their interaction -with the middleware, we've made a few changes that under semver need us to -bump the version number to 2.5.0. - -### The `soft_shutdown` option - -To make use of the new `soft_shutdown` feature add the following to your server.cfg - - soft_shutdown = true - -### Changes since 2.4.1 - -|Date|Description|Ticket| -|----|-----------|------| -|2014/04/23|Deprecate Runner#run method|MCO-265| -|2014/04/23|Drop ubuntu 13.04 (raring ringtail) from the supported package builds|MCO-263| -|2014/04/17|Add ubuntu 14.04 (trusty tahir) to the supported package builds|MCO-189| -|2014/04/10|Refactor the runner class|MCO-221| -|2014/04/04|Update rubygems requirement to 1.3.7 or greater|MCO-188| -|2014/04/02|Move exception classes from lib/mcollective.rb to lib/mcollective/exceptions.rb|MCO-215| -|2014/03/26|Plugin plugin - add dist macro to Release field|MCOP-17| -|2014/03/12|Implements exponential back-off at the connector level|MCO-192| -|2014/02/28|Log reciept and contents of non-MESSAGE STOMP frames|MCO-191| -|2014/02/25|Connectors should not suggest STOMP 1.1 heartbeats if the gem cannot support them|MCO-198| - - -  - -## 2.4.1 - 2014/02/10 - -### Bug Fixes from 2.4.0 - - * Remove reference to package iteration in #package_information - * Improve logging when connector fails to connect - * Fix `plugin.rabbitmq.use_reply_exchange` subscription behavior - -### Changes since 2.4.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2014/02/10|Remove reference to package iteration in #package_information|MCO-179| -|2014/02/07|Update documentation to note that `plugin.rabbitmq.use_reply_exchange` should work from 2.4.1|MCO-174| -|2014/01/30|Improve logging when connector fails to connect|MCO-173| -|2014/01/29|Fix `plugin.rabbitmq.use_reply_exchange` subscription behavior|MCO-172| - - -  - -## 2.4.0 - 2014/01/23 - -*Note: From 2.4.0 MCollective is observing semver (http://semver.org/)* - -### New Features and Improvements from 2.2.4 - - * Correct reply-to headers are now set for both ActiveMQ and RabbitMQ - * Fire and forget requests are now direct addressing aware - * Boolean values in the config classes have now been standardised via a new Util#str_to_bool helper - * SSL certificate paths for ActiveMQ and RabbitMQ can now be set in the users shell environment - * Aggregate plugins are supported in the 'mco plugin doc' application and bundled plugins now have usage information - * Default ports for ActiveMQ and RabbitMQ have changed to 61613 - * Data returned by data plugins are pre-populated with defaults from the DDL - * Direct addressing is now enabled by default - * Argument validation in the rpc application now happens before discovery to provide more timely user feedback - * plugin.discovery.timeout has been removed - * Certain paths have more platform appropriate defaults on Windows - * Filter methods on the RPC client are now idempotent - * The topicprefix, topicsep, queueprefix, rpchelptemplate, helptemplatedir options have been removed and will log a deprecation warning if used. - * Support for version 1.1 of the Stomp protocol has been added to the ActiveMQ and RabbitMQ connectors - * A get_facts action has been added to the rpcutil agent that can retrieve a list of facts - * The plugin packager has been updated to only create a single source artifact when building packages - * The plugin packager can now express operating system specific dependencies - * A Module packager has been added that will output Puppet modules that can be used with the Puppet Labs MCollective module - * A stdin discovery plugin has been added - * Message publishing time is no longer part of the request timeout and is now configurable - * An option has been added to enable threading in the client which improves responsiveness when publishing large amounts of directed messages - * RabbitMQ federation support has been added - * A timeout option has been added to the Shell command runner - * Packaging has been updated to conform with other Puppet Labs projects - * Test coverage has been improved - * Config values that are expected to be integers will no longer be incorrectly parsed - * The DDL action display preference, :flatten, has been deprecated and will be completely removed in the next minor release - -### Bug Fixes from 2.2.4 - - * Direct requests on sub-collectives will now work correctly when using the RabbitMQ connector - * The Plugin Packager now correctly sets the plugin version supplied by the --pluginversion flag - * The --nodes flag will no longer raise an error on Ruby 1.9.3 - * Stopping the MCollective agent on Windows will now exit cleanly - * The systemu guard thread will now exit cleanly when Shell.runcommand() is called from a long running thread - * Correctly handle discovery where data plugins return nil for a specific item - * The flatfile discovery method validates identities using the same rules as the config class - * The Util#versioncmp function behaves correctly with semver versions where the minor is larger than 10 - * Debian packages will now build correctly in a chroot - * The run() agent helper could sometimes return -1 and leave zombies, this has been improved - * Certain operations on a reply data item in an agent could alter the cached copy of the DDL thus affecting future agent invocations - * Absolute paths on Windows are detected correctly - * Line numbers are printed correctly in logs on Windows machines - * Whitespace before config keys in the config file are now ignored - -### Removed Functionality from 2.2.4 - - * The STOMP adapter has been deprecated and removed - -### Backwards Compatibility and Upgrading: - -With the removal of the Stomp connector, in this release we are deprecating a number of unused configuration options that was used by this -connector and a few others that has become pointless over the years. - -If you have any of the following in your configuration files you should consider removing them as they no longer have any effect. - - * topicprefix - * queueprefix - * rpchelptemplate - * helptemplatedir - * plugin.discovery.timeout - * topicsep - -### STOMP 1.1 support with RabbitMQ and ActiveMQ - -A common problem is that idle STOMP connections get expired by session tracking firewalls and NAT devices. Version 1.1 of the STOMP -protocol combats this with protocol level heartbeats which can now be enabled when using version 1.2.10 and up of the stomp gem. - - - # Send heartbeats in 30 second intervals. This is the shortest supported period. - plugin.activemq.heartbeat_interval = 30 - - # By default if heartbeat_interval is set it will request STOMP 1.1 but support fallback - # to 1.0, but you can enable strict STOMP 1.1 only operation by disabling 1.0 fallback - plugin.activemq.stomp_1_0_fallback = 0 - - # Maximum amount of heartbeat read failures before retrying. 0 means never retry. - plugin.activemq.max_hbread_fails = 2 - - # Maxium amount of heartbeat lock obtain failures before retrying. 0 means never retry. - plugin.activemq.max_hbrlck_fails = 2 - - -For the RabbitMQ connector the names of the options are as follows. - - - # Send heartbeats in 30 second intervals. This is the shortest supported period. - plugin.rabbitmq.heartbeat_interval = 30 - - # By default if heartbeat_interval is set it will request STOMP 1.1 but support fallback - # to 1.0, but you can enable strict STOMP 1.1 only operation by disabling 1.0 fallback - plugin.rabbitmq.stomp_1_0_fallback = 0 - - # Maximum amount of heartbeat read failures before retrying. 0 means never retry. - plugin.rabbitmq.max_hbread_fails = 2 - - # Maxium amount of heartbeat lock obtain failures before retrying. 0 means never retry. - plugin.rabbitmq.max_hbrlck_fails = 2 - -More information about STOMP heartbeats can be found http://stomp.github.io/stomp-specification-1.1.html#Heart-beating - -### RabbitMQ Federation - -RabbitMQ federation only mirrors exchanges between nodes so replies need to be -sent to an exchange instead of a queue. In order to enable that add the -following snippet to your client configuration: - - - plugin.rabbitmq.use_reply_exchange = true - - -You will also need to create an exchange called `mcollective_reply` in your -rabbitmq vhost. - - - $ rabbitmqadmin declare exchange --user=admin --password=changeme --vhost=/mcollective name=mcollective_reply type=direct - - -### Changes to the Client - -In this release we have made two changes to increase reliability when sending a large amount of messages. Firstly, we have -added a customisable publishing timeout which is independant from the agent timeout. This can be set either in the client -configuration file - - #client.cfg - publish_timeout = 2 - -or on the command line - - $ mco rpc rpcutil ping --publish_timeout 2 - -The publishing timeout will default to 2 seconds. - -Secondly this release adds the ability to start the client in threaded mode. This will greatly increase the amount of -messages that can be sent when using direct addressing. This can be enabled either in the client configuration file - - #client.cfg - threaded = true - -or on the command line - - $ mco rpc rpcutil ping --nodes large_node_file.txt --threaded - -### Plugin Packager - -This release brings three improvements to the plugin packager. - -A module target has been added which allows you output Puppet modules that can be used with the new MCollective Puppet Module. - - - $ git clone https://github.com/puppetlabs/mcollective-service-agent - $ cd mcollective-service-agent - $ mco plugin package \ - --format modulepackage \ - --vendor puppetlabs - - -This will create a module for the forge named puppetlabs-mcollective_service_agent containing the source -code and the class mcollective_service_agent::agent - -This release also adds the ability to specify system specific dependensies when building RPM's or Deb's. - - $ mco plugin package --dependency="debian::ruby-net-ping" \ - --dependency="redhat::rubygem-net-ping" - -Finally the plugin packager will no longer create multiple source artifacts when building packages. - -### Changes since 2.2.4 - -|Date|Description|Ticket| -|----|-----------|------| -|2014/01/16|MCollective service doesn't exit on Windows|MCO-158| -|2014/01/08|Turned use of removed options into warnings|MCO-151| -|2014/01/08|Removed i18n spike (#18863)|MCO-138| -|2014/01/07|Fixed a spurious warning in 'mco ping'|MCO-146| -|2014/01/07|Config class does not parse fixnum config parameters correctly|MCO-97| -|2014/01/06|deprecate and remove flattened output|MCO-84| -|2013/12/19|Make audit plugin log output match standard format|MCO-142| -|2013/11/07|Add a modulepackage target to the plugin packager|23099| -|2013/11/06|Fix possible thread leak in Shell|23090| -|2013/11/06|Add a timeout option for system commands|22114| -|2013/11/05|Redo the packaging of mcollective|17067| -|2013/10/30|Add rabbitmq federation support with `plugin.rabbitmq.use_reply_exchange`|22603| -|2013/10/30|Update rabbitmq connector documentation for recent version of rabbitmqadmin|19537| -|2013/10/17|mcollective service does not gracefully exit on windows|20467| -|2013/10/16|Add option to thread client|21910| -|2013/10/16|Publishing time should not be part of the request time|21910| -|2013/10/11|Add a stdin discovery method|22061| -|2013/10/08|Plugin packager doesn't apply --pluginversion option|22790| -|2013/10/07|Mcollective plugins cannot express dependencies|22361| -|2013/10/03|Ability to retrieve multiple facts through rpcutil|21788| -|2013/10/01|Fix packaging for debain/ubuntu with ruby 1.9|16572| -|2013/09/27|Fix buildmacpkg|16786| -|2013/09/27|Fix --nodes 'nodefile' on ruby 1.9.3|22720| -|2013/09/25|MCO Plugin Packager produces more than one source artifact|22316| -|2013/09/06|Fix directed request on subcollectives with rabbit connector|21755| -|2013/08/19|add an install.rb file to mcollective|22220| -|2013/08/02|Support Stomp 1.1 with RabbitMQ and ActiveMQ|15182| -|2013/07/31|Surpress Errno::ESRCH info messages when running shell commands|21779| -|2013/07/03|Improve error reporting when requesting docs for a non existing plugin|21429| -|2013/07/03|Support aggregate plugins in 'mco plugin doc'|18414| -|2013/07/03|Allow the ActiveMQ and RabbitMQ SSL cert paths to be set using environment variables|20550| -|2013/06/23|Gracefully handle whitespaces in the config file before config keys|21407| -|2013/06/19|Ensure the line numbers are printed correctly on both Windows and Unix|20506| -|2013/06/19|Remove the rpchelptemplate and helptemplatedir options|20714| -|2013/06/18|Correctly detect Windows absolute paths|21251| -|2013/06/10|Fix and centralize handling of boolean values for settings|19751| -|2013/06/06|Clone the default values from the DDL to avoid accidental modifications to the cached DDL file|21104| -|2013/06/05|Filter methods on the RPC Client are now idempotent|20233| -|2013/06/04|run() call in an agent can return incorrect Process::Status|17667| -|2013/06/03|Improve debian dependencies so packages can be rebuilt in a chroot|20950| -|2013/05/28|Set expire headers in the ActiveMQ and RabbitMQ message headers|19905| -|2013/05/10|Correctly detect version differences in semver version where the path level is greater 10|20661| -|2013/05/01|Improve behaviour of data matchers when return values are nil|20059| -|2013/04/29|Improve config defaults on windows machines|20388| -|2013/04/18|Enforce valid identity names in the file discovery method|20282| -|2013/04/11|Add direct addressing awareness to the fire and forget request mode|17930| -|2013/03/22|Remove the topicprefix, queueprefix and topicsep options|19673| -|2013/03/21|Remove the plugin.discovery.timeout setting as it's not relevant anymore|19694| -|2013/03/21|Improve error reporting from the rpc application in the light of direct_addressing|19827| -|2013/03/20|Fail with a friendly error message when no libdir is set|19752| -|2013/03/14|Change default RabbitMQ and ActiveMQ ports to 61613|19734| -|2013/03/13|Set correct reply-to headers in the RabbitMQ connector|17034| -|2013/03/12|Pre-populate the data from data plugins like agent replies|19564| -|2013/03/12|Explicitly include StringIO|19367| -|2013/03/12|Enable direct addressing by default|19665| -|2013/02/20|Fix error code collision on PLMC18|19366| -|2013/02/15|Validate arguments supplied to the RPC application and raise errors sooner|19181| - - -  - -## 2.3.3 - 2013/11/07 - -This is the fourth release in the new development series of MCollective. This -release features enchancements and bug fixes. - -This release is for early adopters, production users should consider the 2.2.x -series. - -### New Features and Improvements - - * Support for version 1.1 of the Stomp protocol has been added to the ActiveMQ and RabbitMQ connectors - * A get_facts action has been added to the rpcutil agent that can retrieve a list of facts - * The plugin packager has been updated to only create a single source artifact when building packages - * The plugin packager can now express operating system specific dependencies - * An experimental module packager has been added that will output Puppet modules that can be used with the Puppet Labs MCollective module - * A stdin discovery plugin has been added - * Message publishing time is no longer part of the request timeout and is now configurable - * An experimental option has been added to enable threading in the client which should improve responsiveness when publishing large amounts of directed messages - * Experimental RabbitMQ federation support has been added - * A timeout option has been added to the Shell command runner - * Packaging has been updated to conform with other Puppet Labs projects - -### Bug Fixes - - * Suppress Errno::ESRCH info messages when running shell commands - * Direct requests on sub-collectives will now work correctly when using the RabbitMQ connector - * The Plugin Packager now correctly sets the plugin version supplied by the --pluginversion flag - * The --nodes flag will no longer raise an error on Ruby 1.9.3 - * Stopping the MCollective agent on Windows will now exit cleanly - * The systemu guard thread will now exit cleanly when Shell.runcommand() is called from a long running thread - -### Backwards Compatibility and Upgrading - -There should be no additional steps required when upgrading from 2.3.2 to 2.3.3. If you are upgrading from 2.3.1 or earlier please -refer to the 2.3.2 compatibility notes. - -### Changes since 2.3.2 - -|Date|Description|Ticket| -|----|-----------|------| -|2013/11/07|Add a modulepackage target to the plugin packager|23099| -|2013/11/06|Fix possible thread leak in Shell|23090| -|2013/11/06|Add a timeout option for system commands|22114| -|2013/11/05|Redo the packaging of mcollective|17067| -|2013/10/30|Add rabbitmq federation support with `plugin.rabbitmq.use_reply_exchange`|22603| -|2013/10/30|Update rabbitmq connector documentation for recent version of rabbitmqadmin|19537| -|2013/10/17|mcollective service does not gracefully exit on windows|20467| -|2013/10/16|Add option to thread client|21910| -|2013/10/16|Publishing time should not be part of the request time|21910| -|2013/10/11|Add a stdin discovery method|22061| -|2013/10/08|Plugin packager doesn't apply --pluginversion option|22790| -|2013/10/07|Mcollective plugins cannot express dependencies|22361| -|2013/10/03|Ability to retrieve multiple facts through rpcutil|21788| -|2013/10/01|Fix packaging for debain/ubuntu with ruby 1.9|16572| -|2013/09/27|Fix buildmacpkg|16786| -|2013/09/27|Fix --nodes 'nodefile' on ruby 1.9.3|22720| -|2013/09/25|MCO Plugin Packager produces more than one source artifact|22316| -|2013/09/06|Fix directed request on subcollectives with rabbit connector|21755| -|2013/08/19|add an install.rb file to mcollective|22220| -|2013/08/02|Support Stomp 1.1 with RabbitMQ and ActiveMQ|15182| -|2013/07/31|Surpress Errno::ESRCH info messages when running shell commands|21779| - -  - -## 2.3.2 - 2013/07/11 - -This is the third release in the new development series of MCollective. This -release features enhancements and bug fixes. - -This release is for early adopters, production users should consider the 2.2.x -series. - -There are important steps to take before upgrading to this release, please carefully -read the updating section below. - -### New Features and Improvements - - * Correct reply-to headers are now set for both ActiveMQ and RabbitMQ - * Fire and forget requests are now direct addressing aware - * Boolean values in the config classes have now been standardised via a new `Util#str_to_bool` helper - * SSL certificate paths for ActiveMQ and RabbitMQ can now be set in the users shell environment - * Aggregate plugins are supported in the 'mco plugin doc' application and bundled plugins now have usage information - * Default ports for ActiveMQ and RabbitMQ have changed to 61613 - * Data returned by data plugins are pre-populated with defaults from the DDL - * Direct addressing is now enabled by default - * Argument validation in the rpc application now happens before discovery to provide more timely user feedback - * Improved error handling and error messages - * Remove the topicprefix, queueprefix, rpchelptemplate, helptemplatedir, plugin.discovery.timeout and topicsep configuration options - * Certain paths have more platform appropriate defaults on Windows - * Filter methods on the RPC client are now idempotent - -### Bug Fixes - - * Correctly handle discovery where data plugins return nil for a specific item - * The flatfile discovery method validates identities found using the same rules as the config class - * The Util#versioncmp function behaves correctly with semver versions where the minor is larger than 10 - * Debian packages will now build correctly in a chroot - * The run() agent helper could sometimes return -1 and leave zombies, this has been improved - * Certain operations on a reply data item in an agent could alter the cached copy of the DDL thus affecting future agent invocations - * Absolute paths on Windows are detected correctly - * Line numbers are printed correctly in logs on Windows machines - * Whitespace before config keys in the config file are now ignored - -### Backwards Compatibility and Upgrading - -In release 2.3.1 we removed the Stomp connector, in this release we are removing a number of unused -configuration options that was used by this connector and a few others that has become pointless over -the years. - -If you have any of the following in your configuration files you must remove them *before* upgrading -as the daemon and client will fail to start if any of them are present: - - * topicprefix - * queueprefix - * rpchelptemplate - * helptemplatedir - * plugin.discovery.timeout - * topicsep - -If you have in the past not configured the port for the ActiveMQ and RabbitMQ connectors the default -would have been 6163, this has now changed to 61613 to be more in line with what other projects default -to. This means if you rely on the defaulting behaviour you might now have to specifically state the -ports. We recommend always stating ports specifically. - -### Changes since 2.2.3 - -|Date|Description|Ticket| -|----|-----------|------| -|2013/07/03|Improve error reporting when requesting docs for a non existing plugin|21429| -|2013/07/03|Support aggregate plugins in 'mco plugin doc'|18414| -|2013/07/03|Allow the ActiveMQ and RabbitMQ SSL cert paths to be set using environment variables|20550| -|2013/06/23|Gracefully handle whitespaces in the config file before config keys|21407| -|2013/06/19|Ensure the line numbers are printed correctly on both Windows and Unix|20506| -|2013/06/19|Remove the rpchelptemplate and helptemplatedir options|20714| -|2013/06/18|Correctly detect Windows absolute paths|21251| -|2013/06/10|Fix and centralize handling of boolean values for settings|19751| -|2013/06/06|Clone the default values from the DDL to avoid accidental modifications to the cached DDL file|21104| -|2013/06/05|Filter methods on the RPC Client are now idempotent|20233| -|2013/06/04|run() call in an agent can return incorrect Process::Status|17667| -|2013/06/03|Improve debian dependencies so packages can be rebuilt in a chroot|20950| -|2013/05/28|Set expire headers in the ActiveMQ and RabbitMQ message headers|19905| -|2013/05/10|Correctly detect version differences in semver version where the path level is greater 10|20661| -|2013/05/01|Improve behaviour of data matchers when return values are nil|20059| -|2013/04/29|Improve config defaults on windows machines|20388| -|2013/04/18|Enforce valid identity names in the file discovery method|20282| -|2013/04/11|Add direct addressing awareness to the fire and forget request mode|17930| -|2013/03/22|Remove the topicprefix, queueprefix and topicsep options|19673| -|2013/03/21|Remove the plugin.discovery.timeout setting as it's not relevant anymore|19694| -|2013/03/21|Improve error reporting from the rpc application in the light of direct_addressing|19827| -|2013/03/20|Fail with a friendly error message when no libdir is set|19752| -|2013/03/14|Change default RabbitMQ and ActiveMQ ports to 61613|19734| -|2013/03/13|Set correct reply-to headers in the RabbitMQ connector|17034| -|2013/03/12|Pre-populate the data from data plugins like agent replies|19564| -|2013/03/12|Explicitly include StringIO|19367| -|2013/03/12|Enable direct addressing by default|19665| -|2013/02/20|Fix error code collision on PLMC18|19366| -|2013/02/15|Validate arguments supplied to the RPC application and raise errors sooner|19181| - -  - -## 2.2.4 - 2013/05/21 - -This is a maintenance release to the current production series of MCollective. -This release is a bug fix only release. - -### Bug Fixes - - * A work around for a API behaviour change in the latest JSON gem have been added - * Configuration defaults on MS Windows have been improved - * The correct reply-to headers are now set by the RabbitMQ connector allowing async communication modes - * The StringIO library is now specifically required to avoid a case where the client library would fail when a non YAML fact source is set on the client - * The Util#versioncmp function incorrectly compared semver versions with high minor versions - -### Backwards Compatibility and Upgrading - -This release should be 100% backwards compatible with 2.2.0, 2.2.1, 2.2.2 and 2.2.3, when upgrading -from earlier releases please review the Release notes for 2.0.0. - -### Changes since 2.2.3 - -|Date|Description|Ticket| -|----|-----------|------| -|2013/05/10|Correctly detect version differences in semver version where the path level is greater 10|20661| -|2013/05/07|Support the latest version of the JSON gem|20594| -|2013/04/29|Improve config defaults on windows machines|20388| -|2013/03/13|Set correct reply-to headers in the RabbitMQ connector|17034| -|2013/03/12|Explicitly include StringIO|19367| - -  - -## 2.3.1 - 2013/02/14 - -This is the second release in the new development series of MCollective. This -release features enhancements and bug fixes. - -This release is for early adopters, production users should consider the 2.2.x -series. - -### New Features and Improvements - - * Initial work towards online help, improved logging and internationalization - * The output from `--help` has been made clearer - * The output of a failed reply in the default `printrpc` method has been improved - -### Bug Fixes - - * The vendored JSON gem was updated to version 1.5.5 due to CVE-2013-0269 - * The RPC client inadvertently lost the ability to set discovery_timeout, this has been restored - * Plugins with underscores in their name were not packagable on Debian, we now change underscores to dashes - * The STOMP connector will not be maintained further and has been removed - * A config file reading race condition were fixed, we no longer attempt to use config details before parsing the config file thus always using defaults. - * Dependencies on packaged plugins have been made more specific to ensure updates work correctly - * When an argument to the rpc application fails to parse the command will fail instead of continue with unexpected side effects - * Processing of `--no-response` was broken in 2.3.0, this has been fixed - -### Removed Functionality - - * The STOMP adapter has been deprecated and removed - -### Online Help and Internationalization - -Starting in this release a number of errors and messages will start showing error codes along -with the error text and we have a method for obtaining detailed information about each coded -message. - -An example log line can be seen here: - -{% highlight console %} -puppetd.rb:26 PLMC34: setting meta data in agents have been deprecated, DDL files are now being used for this information. Please update the 'puppetd.rb' agent -{% endhighlight %} - -And an example CLI error string: - -{% highlight console %} -% mco rpc rpcutil get_fact - -The rpc application failed to run: PLMC30: Action 'get_fact' needs a 'fact' argument - -Use the 'mco doc PLMC30' command for details about this error, use -v for full error backtrace details -{% endhighlight %} - -You can now use the `mco doc PLMC30` command to get additional information about this error -and any other error code you might see. - -Only a small number of errors and log lines have been updated for the new system and -we will soon publish web versions of these help documents too which should help when -searching for resolution to common errors. - -### Backwards Compatibility and Upgrading - -The STOMP connector has been removed, if you are using it please move to the RabbitMQ -or ActiveMQ one before upgrading. Especially if you use Debian which would avoid the -package upgrading from failing - -### Changes since 2.3.0 - -|Date|Description|Ticket| -|----|-----------|------| -|*2013/02/14*|*Release 2.3.1*|19265| -|2013/02/14|Initial work towards internationalization and online help|18663| -|2013/02/14|Update vendored JSON gem for CVE-2013-0269|19265| -|2013/02/13|Restore the ability to set a discovery timeout on a RPC client|19238| -|2013/02/12|Replace underscores in plugin names with dashes to keep Debian happy|19200| -|2013/02/12|Fix package building on certain Debian systems|19141| -|2013/02/12|Remove the stomp connector|19146| -|2013/02/07|Read the client config before trying to use any configuration options|19105| -|2013/01/22|When an argument fails to parse in the rpc application fail rather than continue with unintended consequences|18773| -|2013/01/22|The fix the *--no-response* argument to the rpc application that broke due to 18438|18513| -|2013/01/22|Set *=* dependencies on the various packages that form a plugin rather than *>=*|18758| -|2013/01/21|Improve presentation of the --help output for applications|18447| -|2013/01/21|When a request failed via *reply.fail*, only show the message and not the half built data|18434| - -  - -## 2.2.3 - 2013/02/14 - -This is a maintenance release to the current production version of MCollective. -This release is a bug fix only release. - -### Bug Fixes - - * The vendored JSON gem was updated to version 1.5.5 due to CVE-2013-0269 - * The RPC client inadvertently lost the ability to set discovery_timeout, this has been restored - * Plugins with underscores in their name were not packagable on Debian, we now change underscores to dashes - * The STOMP adapter will not be maintained past this release series, we now issue deprecation warnigns - * A config file reading race condition were fixed, we no longer attempt to use config details before parsing the config file thus always using defaults. - * Dependencies on packaged plugins have been made more specific to ensure updates work correctly - -### Backwards Compatibility and Upgrading - -This release should be 100% backwards compatible with 2.2.0, 2.2.1 and 2.2.2, when upgrading -from earlier releases please review the Release notes for 2.0.0. - -If you packaged any plugins with a underscore in their name, future packages will have a dash -instead, this might cause upgrade problems. - -We are deprecating the STOMP connector, if you are using this connector please consider moving to the -ActiveMQ or RabbitMQ specific ones. - -### Changes since 2.2.2 - -|Date|Description|Ticket| -|----|-----------|------| -|*2013/02/14*|*Release 2.2.3*|19265| -|2013/02/14|Update vendored JSON gem for CVE-2013-0269|19265| -|2013/02/13|Restore the ability to set a discovery timeout on a RPC client|19238| -|2013/02/12|Replace underscores in plugin names with dashes to keep Debian happy|19200| -|2013/02/12|Fix package building on certain Debian systems|19141| -|2013/02/12|Deprecate the stomp connector|19146| -|2013/02/07|Read the client config before trying to use any configuration options|19105| -|2013/01/22|Set `=` dependencies on the various packages that form a plugin rather than `>=`|18758| - -  - -## 2.0.1 - 2013/02/14 - -This is a maintenance release against our unsupported past production release, it brings no -visible changes or bug fixes we only updated the vendored JSON gem to version 1.5.5 due to -CVE-2013-0269 - -  - -## 2.2.2 - 2013/01/17 - -This is a maintenance release to the current production version of MCollective. -This release is a bug fix only release. - -### Bug Fixes - - * Add the package iteration number as dependency for common packages - * The :any validator has been restored - * Packaging non-agent plugins failed when providing custom paths - * Packaging on RHEL5 systems failed due to an undefined buildroot - * When available packages will be built using rpmbuild-md5 - * Help for data plugins with no input queries are now rendered correctly - * The rpcutil#get_data action now supports data plugins without input queries - * The RPM packages will now require Ruby > 1.8 to improve packaging for 1.9.x - -### Backwards Compatibility and Upgrading - -This release should be 100% backwards compatible with 2.2.0 and 2.2.1, when upgrading -from earlier releases please review the Release notes for 2.0.0. - -### Changes since 2.2.1 - -|Date|Description|Ticket| -|----|-----------|------| -|*2013/02/17*|*Release 2.2.2*|18258| -|2013/01/03|Add the package iteration number as a dependency for the common packages|18273| -|2012/12/24|Restore the :any validator|18265| -|2012/12/19|Do not fail when packaging non-agent packages using custom paths|17281| -|2012/12/19|Require Ruby > 1.8 in the RPM specs for Ruby 1.9|17149| -|2012/11/08|Define a specific buildroot to support RHEL5 systems correctly|17516| -|2012/11/08|Use the correct rpmbuild commands on systems with rpmbuild-md5|17515| -|2012/10/22|Correctly show help for data plugins without any input queries|17137| -|2012/10/22|Allow the rpcutil#get_data action to work with data queries that takes no input|17138| - -  - -## 2.3.0 - 2012/01/10 - -This is the first release in the new development series of MCollective. This -release features small enhancements and bug fixes. - -This release is for early adopters, production users should consider the 2.2.x -series. - -### Enhancements and behaviour changes - - * Data queries can be written without any input queries - * Required inputs can now supply default values in their DDLs - * Support for Ruby 1.9 was improved in the packages - * The generated plugin documentation has been updated to show defaults and optional items - * Errors in agents will now log backtraces on the servers to assist with debugging - * libdirs will now be expanded to absolute paths and using relative ones will raise an error - * Various error and logging improvements - * Various improvements to the plugin packager - -### Bug fixes - - * Packaging non-agent plugins with custom paths caused an unexpected failure - * The plugin packager works correctly on RHEL5 now after previously using an incorrect buildroot - * Correctly handle custom formats passed to the aggregation plugins from the DDL - * Failure in one aggregate plugin does not impact other aggregate functions - * The chosen timeout for agents when using direct addressing could be wrong in some cases - * Data plugins can now return BigNum data like those found in timestamps - * Aggregate functions support non string data - * Boolean flags in applications can now support --noop and --no-noop style flags - * Data results were not raising the correct exception, this was not causing problems in practice but caused the mcollective-test gem to fail - -### Input defaults in the DDL - -You can now provide input defaults for required inputs in the DDL meaning if not -supplied they will default to the supplied format. - -{% highlight ruby %} -action "get_fact", :description => "Retrieve a single fact from the fact store" do - input :fact, - :prompt => "The name of the fact", - :description => "The fact to retrieve", - :type => :string, - :validation => '^[\w\-\.]+$', - :optional => false, - :maxlength => 40, - :default => "operatingsystems" -end -{% endhighlight %} - -The DDL file above defines a input *fact* that is required and sets a default value to -*operatingsystem*. - -Previously the following command would have failed stating the input is required, now it -will default to the supplied value and continue without error: - -{% highlight ruby %} -$ mco rpc rpcutil get_fact -{% endhighlight %} - -The defaults processing is done on the client side and not on the servers meaning at no -point does a non compliant request get published by the clients and older MCollective servers -will process these requests correctly. - -### Backwards Compatibility and Upgrading - -This release can cohabit with older versions with the only potential upgrade problem being -the changes to how the libdir variable is handled. - -In the past a libdir could be: - -{% highlight ini %} -libdir = /usr/libexec/mcollective:.mcollective.d -{% endhighlight %} - -This would have the effect of looking for *.mcollective.d* in the current directory. - -This represented a security risk and would fail on the server side when daemonizing. -We now force all libdir paths to be fully qualified and raises an error at start should -you have relative paths. - -### Changes since 2.2.1 - -|Date|Description|Ticket| -|----|-----------|------| -|2012/01/10|Raise the correct exception when trying to access unknown data items in a Data results|18466| -|2013/01/10|Fix failing documentation generation for data plugins|18437| -|2013/01/09|Correctly support negative boolean flags declared as --[no]-foo|18438| -|2013/01/03|Add the package iteration number as a dependency for the common packages|18273| -|2012/12/21|The libdirs supplied in the config file now has to be absolute paths to avoid issues when daemonising|16018| -|2012/12/20|Logs the error and backtrace when an action fails|16414| -|2012/12/20|Display the values of :optional and :default in DDL generated help|16616| -|2012/12/20|Allow the query string for the get_data action in rpcutil to be 200 chars|18200| -|2012/12/19|Do not fail when packaging non-agent packages using custom paths|17281| -|2012/12/19|Require Ruby > 1.8 in the RPM specs for Ruby 1.9|17149| -|2012/12/18|Allow required inputs to specify default data in DDLs|17615| -|2012/11/12|When disconnecting set the connection to nil|17384| -|2012/11/08|Define a specific buildroot to support RHEL5 systems correctly|17516| -|2012/11/08|Use the correct rpmbuild commands on systems with rpmbuild-md5|17515| -|2012/10/22|Correctly show help for data plugins without any input queries|17137| -|2012/10/22|Allow the rpcutil#get_data action to work with data queries that takes no input|17138| -|2012/10/03|Improve text output when providing custom formats for aggregations|16735| -|2012/10/03|Correctly process supplied formats when displaying aggregate results|16415| -|2012/10/03|Prevent one failing aggregate function from impacting others|16411| -|2012/10/03|When validation fails indicate which input key has the problem|16617| -|2012/09/26|Data queries can be written without any input queries meaning they take no input|16424| -|2012/09/26|Use correct timeout for agent requests when using direct addressing|16569| -|2012/09/26|Allow BigNum data to be used in data plugin replies|16503| -|2012/09/26|Support non string data in the summary aggregate function|16410| - - -  - -## 2.2.1 - 2012/10/17 - -This is a maintenance release to the current production version of MCollective. -This release is a bug fix only release. - -### Bug Fixes - - * Various display and stability improvements with aggregate plugins - * Improve error messages - * Data queries that does not take an input still had to provide a bogus query input, now not needed - * When using direct addressing and identity filter the client timeout was incorrect - * BigNum type data can now be used in data plugin replies - -### Backwards Compatibility and Upgrading - -This release should be 100% backwards compatible with 2.2.0, when upgrading from earlier releases -pleas reivew the Release notes for 2.0.0. - -### Changes since 2.1.0 - -|Date|Description|Ticket| -|----|-----------|------| -|*2012/10/17*|*Release 2.2.1*|16965| -|2012/10/03|Improve text output when providing custom formats for aggregations|16735| -|2012/10/03|Correctly process supplied formats when displaying aggregate results|16415| -|2012/10/03|Prevent one failing aggregate function from impacting others|16411| -|2012/10/03|When validation fails indicate which input key has the problem|16617| -|2012/09/26|Data queries can be written without any input queries meaning they take no input|16424| -|2012/09/26|Use correct timeout for agent requests when using direct addressing|16569| -|2012/09/26|Allow BigNum data to be used in data plugin replies|16503| -|2012/09/26|Support non string data in the summary aggregate function|16410| -|2012/09/14|Package discovery plugins that was left out for debian|16413| - -  - -## 2.2.0 - 2012/09/13 - -This is the next production release of MCollective. It brings to an end active -support for versions 2.1.1 and older. - -### Major Enhancements - - * A new plugin type called data plugins were added making network discovery extendible by users - * Discovery is now pluggable allowing network based, database based, file based or any other data source to be used as a source of truth - * Automatic result summarization methods can be declared in the DDL and users can write their own - * A RabbitMQ specific Direct Addressing capable connector was added - * Agent DDLs must be present on the servers, input validation is done against the DDL and prior to running user code - * DDL files can define default values for returned data - all declared data fields are pre-populated by agents - * DDL files can store general usage information that gets rendered via the help application - * DDL files can declare the minimum version mcollective they need to be functional and loading plugins on older mcollective versions will fail - * New validation logic in DDL files and Agents can now be delivered using plugins - * A thread safe caching system was added that users can use in their Agents to store information between invocations - * Code generators to assist writing agents - * Support deterministic random node selection - * Display mode can be overriden on the CLI using the new *--display* option - * The plugin packager will now keep source debs and rpms and has had major improvements done - * A new application called *completion* was added to assist in writing shell completion systems. ZSH and Bash examples are in *ext/* - * Various improvements to documentation was made especially around using the CLI tools and discovery available plugins - -### Bug Fixes - - * The vendored systemu gem has been updated to remove a rude error message - * Improved error reporting in many areas - * Boolean and numeric data is correctly parsed on the RPC application command line - * Improved parsing of compound filters - * Batched requests will now all have the same request id thus improving consistency of auditing information - -### Deprecations - - * Remove the traditional Client#discovered_req method - * The metadata section in the agent is being removed as the DDL is now present everywhere - -### Data Plugins - -A new plugin type called _data plugins_ have been added, these plugins are -usable in discovery requests and in any agent code. - -You can use these plugins to expose any node side data to your client discovery -command line, an example can be seen below, this will discover all nodes where -_/etc/syslog.conf_ has a md5 sum matching the regular expression _/19ff4997e/_: - -{% highlight console %} -$ mco rpc rpcutil ping -S "fstat('/etc/rsyslog.conf').md5 = /19ff4997e/" -{% endhighlight %} - -For full information see the plugins documentation on our website. The _fstat_ -plugin seen above is included at the moment, more will be added in due course -but as always users can also write their own suitable to their needs. - -### Custom Discovery Sources - -A new type of plugin that can be used as alternative data sources for discovery -data has been added. The traditional network broadcast mode is supported and -remains the default but a new flat file one was added. - -Custom discovery sources can be made the default for a client using the -*default_discovery_method* configuration option but can be selected on the -command line using _--disc-method_. - -All applications now have a _--nodes_ option that takes as an argument a flat -file full of mcollective identity names, one per line. - -Users can write their own discovery plugins and distribute it using the normal -plugin packager. A complex example can be seen in the community plugin site -for the MongoDB registration plugin. - -In the event that the _-S_ filter is used the network discovery mode will be -forced so that data source plugins in discovery queries will always work as -expected. - -This feature requires Direct Addressing. - -### DDL files on the servers - -The DDL files now have to be on the servers and the clients. On the servers the -results will be pre-populated with default data for all defined output values of -a specific action and you can now supply defaults. - -Additionally input will be validated on each node prior to running the agent -code providing consistent input validation on client and server. This should -remove the need to add *validate* statements to agents. - -An example for a Nagios plugin can be seen below, here we default to *UNKNOWN* -so that even if the action fails to run we will still see valid data being -returned thats appropriate for the specific use case. - -{% highlight ruby %} -action "runcommand", :description => "Run a NRPE command" do - output :exitcode, - :description => "Exit Code from the Nagios plugin", - :display_as => "Exit Code", - :default => 3 -end -{% endhighlight %} - -### Summarization Plugins - -Often custom applications are written just to summarize data like the *facts* -application or *nrpe* ones. - -We have added a new plugin type that allows you to define summarization logic -and included a few of our own. These summaries are declared in the DDL, here is -a section from the new DDL for the *get_fact* action: - -{% highlight ruby %} -action "get_fact", :description => "Retrieve a single fact from the fact store" do - output :value, - :description => "The value of the fact", - :display_as => "Value" - - summarize do - aggregate summary(:value) - end -end -{% endhighlight %} - -Here we are using the *summarize* block to say that we wish to summarize the -output *:value*. The *summary(:value)* is the call to a custom plugin and you -can provide your own. - -Now when interacting with this action you will see summaries produced -automatically: - -{% highlight ruby %} -% mco rpc rpcutil get_fact fact=operatingsystemrelease -. -. -dev2 - Fact: operatingsystemrelease - Value: 6.2 - - -Summary of Value: - - 6.2 = 19 - 6.3 = 7 - -Finished processing 26 / 26 hosts in 294.97 ms -{% endhighlight %} - -The last section of the rpc output shows the summarization in action. - -The NRPE plugin on GitHub shows an example of a Nagios specific aggregation -function and the plugin packager supports distributing these plugins. - -### Validation Plugins - -Users can now write their own plugins to perform input validation, these -validations are usable in DDL files and agents. - -Below is a snippet from a DDL file using a custom *exim_msgid* validation -plugin: - -{% highlight ruby %} - input :msgid, - :prompt => "Message ID", - :description => "Valid message id currently in the mail queue", - :type => :string, - :validation => :exim_msgid, - :optional => false, - :maxlength => 16 -{% endhighlight %} - -And a snippet using the same plugin inside your agent: - -{% highlight ruby %} -action "retrymsg" do - validate :msgid, :exim_msgid - - # call out to exim to retry the message -end -{% endhighlight %} - -The error messages shown when validation fails are more user friendly than -before, in this example the new error would be *Not a valid Exim Message ID* -where in the past it would have been *value should match ^(?:[+-]\d{4})?(?:\[\d+\] )?(\w{6}\-\w{6}\-\w{2})/* - -### Code generation - -Code for agents and data sources can now be generated to assist development, you -can use the _plugin_ command to create a basic skeleton agent or data source -including the DDL files. - -{% highlight console %} -$ mco plugin generate agent myagent actions=do_something,do_something_else -{% endhighlight %} - -Defaults used in the metadata templates can be set in the config file: - -{% highlight ini %} -plugin.metadata.url=http://devco.net -plugin.metadata.author=R.I.Pienaar -plugin.metadata.license=ASL2.0 -plugin.metadata.version=0.0.1 -{% endhighlight %} - -All generator produced output will have these settings set, the other fields are -constructed using a pattern convenient for using in your editor as a template. - -### Backwards Compatibility and Upgrading - -As of this version every agent on every node and client must have a DDL file. If -the DDL file is not present or not valid the agent will not activate. Further -input validation is done according to the content of the DDL prior to running -any actions. You should therefore prepare for this upgrade by writing and -deploying DDL files for all your agents. - -Version 2.0.0 and 2.2.0 can co-exist on the same network. If a new client uses -any of the new features added such as data plugins the older clients will simply -refuse to run the request but requests using features shared between versions -will continue to work. - -When you first start this version of mcollectived you will see warnings logged -similar to the one below: - -{% highlight ruby %} -puppetd.rb:26: setting meta data in agents have been deprecated, DDL files are now being used for this information. -{% endhighlight %} - -This is only a warning and not a critical problem. The next major release will -remove support for metadata in agents. - -Upgrading from versions prior to 2.0.0 was not tested, please refer to the -release notes for 2.0.0. - -  - -## 2.1.1 - 2012/07/12 - -This release features major new features, enhancements and bug fixes. - -This release is for early adopters, production users should consider the 2.0.x -series. - -### Major Enhancements - - * A new discovery source was added capable of querying agent properties - * When doing limited discovery you can now supply a random seed for deterministic random selection - * A *get_data* action has been added to the *rpcutil* agent to retrieve the result of a data plugin - * RPC Agents must have DDLs on the MCollective Servers, agents will not load without them - * Output values can now have defaults assigned in the DDL, the server will set those defaults before running an action - * A new plugin type used to summarize sets of replies has been added. Summarization is declared in the DDL for an Agent - -### Bug Fixes - - * Correctly parse numeric and boolean input arguments in the RPC application - -### Deprecations - - * The old *Client#discovered_req* is removed along with the *controller* application that used it - * Parsing compound filters were improved wrt complex regular expressions - * Metadata sections in agents are not needed anymore and deprecation notices are logged when they are found - -### Summarization Plugins - -Often custom applications are written just to summarize data like the *facts* -application or *nrpe* ones. - -We have added a new plugin type that allows you to define summarization logic -and included a few of our own. These summaries are declared in the DDL, here is -a section from the new DDL for the *get_fact* action: - -{% highlight ruby %} -action "get_fact", :description => "Retrieve a single fact from the fact store" do - output :value, - :description => "The value of the fact", - :display_as => "Value" - - summarize do - aggregate summary(:value) - end -end -{% endhighlight %} - -Here we are using the *summarize* block to say that we wish to summarize the -output *:value*. The *summary(:value)* is the call to a custom plugin and you -can provide your own. - -Now when interacting with this action you will see summaries produced -automatically: - -{% highlight ruby %} -% mco rpc rpcutil get_fact fact=operatingsystemrelease -. -. -dev2 - Fact: operatingsystemrelease - Value: 6.2 - - -Summary of Value: - - 6.2 = 19 - 6.3 = 7 - -Finished processing 26 / 26 hosts in 294.97 ms -{% endhighlight %} - -The last section of the rpc output shows the summarization in action. - -The NRPE plugin on GitHub shows an example of a Nagios specific aggregation -function and the plugin packager supports distributing these plugins. - -### DDL files on the servers - -The DDL files now have to be on the servers and the clients. On the servers the -results will be pre-populated with default data for all defined output values of -a specific action and you can now supply defaults. - -An example for a Nagios plugin can be seen below, here we default to *UNKNOWN* -so that even if the action fails to run we will still see valid data being -returned thats appropriate for the specific use case. - -{% highlight ruby %} -action "runcommand", :description => "Run a NRPE command" do - output :exitcode, - :description => "Exit Code from the Nagios plugin", - :display_as => "Exit Code", - :default => 3 -end -{% endhighlight %} - -As the servers now have the DDL the *metadata* section at the top of agents are -not needed anymore and deprecations will be logged when the mcollectived starts -up warning you of this. - -### Backwards Compatibility and Upgrading - -As this release now requires DDL files to exist before an agent can be loaded in -the server you might have to adjust your deployment strategy and possibly write -some DDLs for your custom agents. The DDL files have to be on both client and -servers. - -The servers will now pre-populate the replies with all output defined in the DDL -and supply defaults if no default is provided in the DDL it will default to nil. -This might potentially change the behavior of custom applications that are -designed around the approach of checking if a field is included in the results -or not. - -When you first start this version of mcollectived you will see warnings logged -similar to the one below: - -{% highlight ruby %} -puppetd.rb:26: setting meta data in agents have been deprecated, DDL files are now being used for this information. -{% endhighlight %} - -This is only a warning and not a critical problem. Once 2.2.0 is out we will be -updating all the agents to remove metadata sections in favour of those in the DDL. -You should also remove metadata from your own agents. - -### Changes since 2.1.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2012/07/11|Add a --display option to RPC clients that overrides the DDL display mode|15273| -|2012/07/10|Do not add a metadata to agents created with the generator as they are now deprecated|15445| -|2012/07/03|Correctly parse numeric and boolean data on the CLI in the rpc application|15344| -|2012/07/03|Fix a bug related to parsing regular expressions in compound statements|15323| -|2012/07/02|Update vim snippets in ext for new DDL features|15273| -|2012/06/29|Create a common package for agent packages containing the DDL for servers and clients|15268| -|2012/06/28|Improve parsing of compound filters where the first argument is a class|15271| -|2012/06/28|Add the ability to declare automatic result summarization in the DDL files for agents|15031| -|2012/06/26|Surpress subscribing to reply queues when no reply is expected|15226| -|2012/06/25|Batched RPC requests will now all have the same requestid|15195| -|2012/06/25|Record the request id on M::Client and in the RPC client stats|15194| -|2012/06/24|Use UUIDs for the request id rather than our own weak implementation|15191| -|2012/06/18|The DDL can now define defaults for outputs and the RPC replies are pre-populated|15087| -|2012/06/18|Remove unused agent help code|15084| -|2012/06/18|Remove unused code from the *discovery* agent related to inventory and facts|15083| -|2012/06/18|Nodes will now refuse to load RPC agents without DDL files|15082| -|2012/06/18|The Plugin Name and Type is now available to DDL objects|15076| -|2012/06/15|Add a get_data action to the rpcutil agent that can retrieve data from data plugins|15057| -|2012/06/14|Allow the random selection of nodes to be deterministic|14960| -|2012/06/12|Remove the Client#discovered_req method and add warnings to the documentation about its use|14777| -|2012/06/11|Add a discovery source capable of doing introspection on running agents|14945| -|2012/06/11|Only do identity filter optimisations for the *mc* discovery source|14942| - -  - -## 2.1.0 - 2012/06/08 - -This is the first release in the new development series of MCollective. This -releas features major new features and enhancements. - -This release is for early adopters, production users should consider the 2.0.x -series. - -### Major Enhancements - - * Discovery requests can now run custom data plugins on nodes to facilitate discovery against any node-side data - * Discovery sources are now pluggable, one supporting flat files are included in this release - * All applications now have a --nodes option to read a text file of identities to operate on - * A new _completion_ application was added to assist with shell completion systems, zsh and bash tab completion plugins are in ext - * Users can now use a generator to create skeleton agents and data sources - -### Changes in behavior - - * The _mco controller_ application is being deprecated for the next major release and has now been removed from the development series - * The _mco find_ application is now a discovery client so it's output mode has changed slightly but the functionality stays the same - -### Bug Fixes - - * Numerous small improvement to user facing errors and status outputs have been made - * Sub collectives combined with direct addressing has been fixed - * Various packaging issues were resolved - * The ActiveMQ and Stomp connectors will now by default handle dual homed IPv6 and IPv4 hosts better in cases where the IPv6 target isn't reachable - -### Data Plugins - -A new plugin type called _data plugins_ have been added, these plugins are -usable in discovery requests and in any agent code. - -You can use these plugins to expose any node side data to your client discovery -command line, an example can be seen below, this will discover all nodes where -_/etc/syslog.conf_ has a md5 sum matching the regular expression -_/19ff4997e/_: - -{% highlight console %} -$ mco rpc rpcutil ping -S "fstat('/etc/rsyslog.conf').md5 = /19ff4997e/" -{% endhighlight %} - -For full information see the plugins documentation on our website. The _fstat_ -plugin seen above is included at the moment, more will be added in due course -but as always users can also write their own suitable to their needs. - -### Custom Discovery Sources - -Since the introduction of direct addressing mode in 2.0.0 you've been able to -pragmatically specify arbitrary host lists as discovery data but this was never -exposed to the user interface. - -We now introduce plugins that can be used as alternative data sources and -include the traditional network broadcast mode and a flat file one. The hope -is that more will be added in future perhaps integrating with systems like -PuppetDB. There is also one that uses the MongoDB Registration plugin to build -a local node cache. - -Custom discovery sources can be made the default for a client using the -*default_discovery_method* configuration option but can be selected on the -command line using _--disc-method_. - -All applications now have a _--nodes_ option that takes as an argument a flat -file full of mcollective identity names, one per line. - -Users can write their own discovery plugins and distribute it using the normal -plugin packager. - -In the event that the _-S_ filter is used the network discovery mode will be -forced so that data source plugins in discovery queries will always work as -expected. - -### Code generation - -Code for agents and data sources can now be generated to assist development, -you can use the _plugin_ command to create a basic skeleton agent or data source -including the DDL files. - -{% highlight console %} -$ mco plugin generate agent myagent actions=do_something,do_something_else -{% endhighlight %} - -Defaults used in the metadata templates can be set in the config file: - -{% highlight ini %} -plugin.metadata.url=http://devco.net -plugin.metadata.author=R.I.Pienaar -plugin.metadata.license=ASL2.0 -plugin.metadata.version=0.0.1 -{% endhighlight %} - -All generator produced output will have these settings set, the other fields -are constructed using a pattern convenient for using in your editor as a -template. - -### Backwards Compatibility and Upgrading - -This release can co-exist with 2.0.0 but using the new discovery data plugins in a -mixed environment will result in the old nodes not being discovered and they will -log exceptions in their logs. This was done by choice and ensures the safest -possible upgrade path. - -When the 2.0.0 collective is running with directed mode enabled a client using the -new discovery plugins will be able to communicate wth the older nodes without -problem. - -### Changes since 2.0.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2012/06/07|Force discovery state to be reset when changing collectives in the RPC client|14874| -|2012/06/07|Create code generators for agents and data plugins|14717| -|2012/06/07|Fix the _No response from_ report to be correctly formatted|14868| -|2012/06/07|Sub collectives and direct addressing mode now works correctly|14668| -|2012/06/07|The discovery method is now pluggable, included is one supporting flat files|14255| -|2012/05/28|Add an application to assist shell completion systems with bash and zsh completion plugins|14196| -|2012/05/22|Improve error messages from the packager when a DDL file cannot be found|14595| -|2012/05/17|Add a dependency on stomp to the rubygem|14300| -|2012/05/17|Adjust the ActiveMQ and Stomp connect_timeout to allow IPv4 fall back to happen in dual homed hosts|14496| -|2012/05/16|Add a plugable data source usable in discovery and other plugins|14254| -|2012/05/04|Improve version dependencies and upgrade experience of debian packages|14277| -|2012/05/03|Add the ability for the DDL to load DDL files from any plugin type|14293| -|2012/05/03|Rename the MCollective::RPC::DDL to MCollective::DDL to match its larger role in all plugins|14254| - -  - -## 2.0.0 - 2012/04/30 - -This is the next production release of MCollective. It brings to an -end active support for versions 1.3.3 and older. - -This release brings to general availability all the features added in the -1.3.x development series. - -### Major Enhancements - - * Complete messaging protocol rewrite to enable direct style connectivity that would allow programs to bypass normal discovery instead using their own data sources - * An additional more robust messaging paradigm supporting a more assured addressing and delivery scheme - * Batched mode allowing users to address machines in small groups thus avoiding thundering herd and enabling more granular changes - * A more complete language for expressing discovery that includes and/or/not style queries across the infrastructure - * Improved Stomp connection security using normal industry standard Certificate Authority validated TLS - * New connector that uses ActiveMQ specific features for better performance and scalability - * Security of the SSL and AES security plugins have been improved for tamper protection by middle men - * A message validity period has been introduced to lower the window of message replay attacks - * Better error handling and better logging for Stomp connections - * JSON output from the 'rpc' application - * Ability to pipe RPC requests into each other creating a chain of related RPC calls - * Better validations, better error handling and better documentation creation from the DDL - * Performance improvements in the CLI, more consistently formatted output of received data - * A Ruby GEM of the client is now made available on rubygems.org - * The rc script for Debian based systems have been improved to prevent duplicate daemons from running - * Built in packager for plugins into native OS packages - RedHat and Debian supported - * MS Windows Support - -### Point to Point comms - -Previously MCollective could only broadcast messages and was tied to a discovery model. - -The messaging layer now supports per node destinations that allows you to address a node, even if its down, -doesn't yet exist or if you cannot come up with a filter that would match a group of arbitrarily selected -nodes. - -When this mode is in use the user configure which machine to communicate with using either text, arrays or -JSON data. It will then communicate directly to those nodes via the middleware and if any of them are down -you will get the usual no responses report after DDL configured timeout, this is a smooth transparent to the -end user mix in communication modes. - -It is ideal for building deployers, web apps and so forth where you know exactly which nodes should be there -and you'd like to influence the MCollective network addressing, perhaps from a CMDB you built yourself. - -This is the start towards an assured style of delivery, you can consider it the TCP to MCollective's UDP. -Both modes of communication will be supported in the future and both will have access to all the same agents -and clients. - -This is feature is enabled using the *direct_addressing* configuration option. At present only the new -ActiveMQ connector supports this at scale. The ActiveMQ connector is now the recommended standard connector -combined with Apache ActiveMQ. More brokers could be supported in future. - -### Pluggable / Optional Discovery - -If the user did _mco rpc rpcutil ping -I box.example.com -I another.example.com_ mcollective will now just -assume you know what she wants, it won't do a discover to confirm those machines exist or not, it will just go -and communicate with them. This is a big end user visible speed improvement. If however you did a filter -like *-I /example.com/* mcollective cannot know which machines you want to reach and so a traditional -broadcast discovery is done first. - -When the direct addressing mode is enabled various behind the scenes optimizations are being done: - - * If a discovery is done and it finds you only want to address 10 or fewer nodes it will use direct mode for that - request. This avoids a second needless broadcast. This is less efficient to the middleware but does not send - needless messages to uninterested nodes that would then just ignore them. - * The _rpc_ application supports piping output from one to the next. Example of this below. - -{% highlight console %} -$ mco rpc package update package=foo -W customer=acme -j|mco rpc service restart service=bar -{% endhighlight %} - -This will update a package on machines matching *customer=foo* and then restart the service *bar* on those -machines. - -The first request is doing traditional discovery based on the fact while the 2nd request is not doing -discovery at all, it uses the JSON output enabled by -j as discovery data and then restart the service on only -those machines. - -These abilities are exposed in the SimpleRPC client API and you can write your own schemes, query your own -databases etc - -### Batching - -Often the speed of MCollective is a problem, you want to install a package on thousands of machines but your -APT or YUM server isn't up to the task. - -You can now do batching of requests: - -{% highlight console %} -$ mco package update myapp --batch 10 --batch-sleep 60 -{% endhighlight %} - -This performs the update as usual but only affecting machines in groups of 10 and sleeps for a minute between. - -You can also access this functionality via the API please see the docs for usage. Any existing script or -application should support this functionality without any code changes. - -The results, error reporting, statistics reporting and so forth all stays consistent with non batched -behavior. - -At any time you can interrupt the process and only the current group of machines will have been affected. - -The batching requires a direct addressing capable collective as it is built using the new direct to node -communications and pluggable discovery features - -### ActiveMQ specific connector - -A new connector plugin has been added that is specific to ActiveMQ and is compatible with the new direct -addressing communication system. - -You will need to change your ActiveMQ configuration to support this plugin, see the documentation for this -plugin and the examples in _ext/activemq_ have also been updated for the new plugin. - -Anyone who use ActiveMQ is strongly recommended to use this plugin as it uses a few ActiveMQ specific -optimizations that can have a big performance enhancing effect on your collective. - -### Packaging Agent plugins - -Distributing agents has been a problem as they are just files that have limited meta data and attached. - -We now support packaging agents into rpm or deb packages, your agent must have a DDL file for this to work: - -{% highlight console %} -$ mco plugin package . --vendor "My Company" -Successfully built RPM 'mcollective-exim_ng-client-0.1-1.noarch.rpm' -Successfully built RPM 'mcollective-exim_ng-common-0.1-1.noarch.rpm' -Successfully built RPM 'mcollective-exim_ng-agent-0.1-1.noarch.rpm' -{% endhighlight %} - -The packages will have meta data like Author, Version and so forth as per your DDL file. - -Users can provide their own packaging implementations for other package managers or custom layouts using the -MCollective plugin system. - -### Full verified CA - -When using the new ActiveMQ specific connector combined with Stomp version 1.2.2 or newer you can get full CA -verified connection handling ensuring that only clients using signed certificates can connect to ActiveMQ. - -The documentation for the ActiveMQ SSL setup now includes instructions on setting up ActiveMQ and your clients -using the built in Puppet CA but any CA could be used to manage these certificates. - -This feature will work best when ActiveMQ 5.6.0 is released in a few weeks since there will then be a NIO+SSL -Stomp connector. The current SNAPSHOT release of ActiveMQ has this feature as well as the most recent Service -Pack release of the Fuse Message Broker. - -### MS Windows Support - -The MS Windows platform is now supported as both a client and a server. The _ext/windows_ directory has some -helpers and read me documentation that has been confirmed to work but we have not yet completed packaging -ourselves so this is still a manual process. - -Combined with Puppet 2.7.12 or newer the Package and Service agents can be used to manage Windows resources -using the same commands as those on Linux via mcollective. - -### New Discovery Language - -Previously dicovery was very limited, filters were simply run one after the other and you could not do -anything complex like a mix of OR and AND boolean logic. - -A new compact discovery language was introduced perfect for use on the command line, an example below: - -{% highlight console %} -$ mco find -S "((fqdn=/example.com/ or fqdn=/another.com/) or customer=acme) and apache and physicalprocessorcount>2" -{% endhighlight %} - -The EBNF for this language can be seen below, it's available on the command line and the API - - compound = ["("] expression [")"] {["("] expression [")"]} - expression = [!|not]statement ["and"|"or"] [!|not] statement - char = A-Z | a-z | < | > | => | =< | _ | - |* | / { A-Z | a-z | < | > | => | =< | _ | - | * | / | } - int = 0|1|2|3|4|5|6|7|8|9{|0|1|2|3|4|5|6|7|8|9|0} - -### Backwards Compatibility and Upgrading - -This release is not compatible with older versions. Client scripts and agents written for older versions will -continue to work but a network hosting both 2.0.0 clients and older one will effectively be split into 2 -networks. While planning your upgrade you should plan to have machines running the client for both versions -to retain full control during upgrade. The upgrade is best done in an scheduled window where all machines are -updated together. - -While upgrading you must ensure that the plugins that come with the release are updated at the same time as -the release. Older security and connector plugins will not function with this release. This also means if -you wrote your own connector or security plugin you will need to port these prior to upgrading. - -Past this it should be a simple matter of updating using your operating systems package manager. - -We recommend you switch to the new ActiveMQ based connector plugin away from the previous generic Stomp one as -this is the primary supported method of deployment and the generic Stomp one will be deprecated in future. -Additionally the Stomp connector does not support the new direct messaging communications mode. - -In order to upgrade to the new ActiveMQ connector you will need to change your broker setup including ACLs, -transport connectors, message policies and inter broker connections. Sample configuration files for single -and multi broker setups can be found in the Git repository or the tar file in _ext/activemq_ - -  - -## 1.3.3 - 2012/04/05 - -This is a release in the development series of MCollective. It feature major new features and bug fixes. - -This release is for early adopters, production users should consider the 1.2.x series. - -### Major Enhancements - - * The MS Windows platform is now supported, packaging is still outstanding - * Agents can now be packaged to native OS packages using the new _mco plugin_ command - * _mco help rpc_ now show the help for the rpc application, _mco plugin doc puppetd_ shows the help for the puppetd agent - * Full CA verified Stomp is supported and documented between ActiveMQ and MCollective using Stomp > 1.2.2 - * Application exit codes have been standardized using a new _halt_ helper function - * A new validator that allows users to check if a supplied value is one of a fixed list - * The syslog facility can now be set in the configuration file - * The client libraries are now available as a Ruby Gem - * Batch mode can now be enabled and disabled at will in an application - * The client config files now default to console based logging at warn level - -### Bug Fixes - - * nil or empty results are correctly displayed by printrpc - * Some exceptions under Ruby 1.9.3 when using run() related to nil exit code has been fixed - * Various exceptions have been silence in inventory application, stomp plugin, rpc application and others - * Previous SSL_read errors when using the Stomp+TLS configuration is now avoided on Ruby 1.8 - -### Packaging Agent plugins - -Distributing agents has been a problem as they are just files that have limited meta data and attached. - -We now support packaging agents into rpm or deb packages, your agent must have a DDL file -for this to work: - -{% highlight console %} -$ mco plugin package . --vendor "My Company" -Successfully built RPM 'mcollective-exim_ng-client-0.1-1.noarch.rpm' -Successfully built RPM 'mcollective-exim_ng-common-0.1-1.noarch.rpm' -Successfully built RPM 'mcollective-exim_ng-agent-0.1-1.noarch.rpm' -{% endhighlight %} - -The packages will have meta data like Author, Version and so forth as per your DDL file. - -We support building all the main plugin types in this manner but need to restructure the plugins -repository to support this layout. - -To use this you need to install the fpm gem, you must install 0.4.3 and not a newer version, we are -currently working on removing the fpm dependency as it's proven to be too unreliable to use. - -Users can provide their own packaging implementations for other package managers or custom layouts -using the MCollective plugin system. - -### Full verified CA - -When using the new ActiveMQ specific connector combined with Stomp version 1.2.2 or newer you can -get full CA verified connection handling ensuring that only clients using signed certificates -can connect to ActiveMQ. - -The documentation for the ActiveMQ SSL setup now includes instructions on setting up ActiveMQ and your -clients using the built in Puppet CA but any CA could be used to manage these certificates. - -This feature will work best when ActiveMQ 5.6.0 is released in a few weeks since there will then be a NIO+SSL -Stomp connector. The current SNAPSHOT release of ActiveMQ has this feature as well as the most recent Service -Pack release of the Fuse Message Broker. - -### MS Windows Support - -The MS Windows platform is now supported as both a client and a server. The _ext/windows_ directory -has some helpers and read me documentation that has been confirmed to work but we have not yet -completed packaging ourselves so this is still a manual process. - -Combined with Puppet 2.7.12 or newer the Package and Service agents can be used to manage Windows -resources using the same commands as those on Linux via mcollective. - -### Backwards compatibility - -This release is backwards compatible with version 1.3.2, if you are coming from an older version please -review earlier release notes. - -If you have been using the ActiveMQ specific plugin and its SSL settings you will now need to enable -fallback mode as it will now only connect to ActiveMQ machines that present the correct CA certificate -and will refuse to use anonymous certificates - -{% highlight ini %} -plugin.activemq.pool.1.ssl.fallback = 1 -{% endhighlight %} - -### Changes since 1.3.2 - -|Date|Description|Ticket| -|----|-----------|------| -|2012/04/04|Use the MCollective::SSL utility class for crypto functions in the SSL security plugin|13615| -|2012/04/02|Support reading public keys from SSL Certificates as well as keys|13534| -|2012/04/02|Move the help template to the common package for both Debian and RedHat|13434| -|2012/03/30|Support Stomp 1.2.2 CA verified connection to ActiveMQ|10596| -|2012/03/27|_mco help rpc_ now shows the help for the rpc application|13350| -|2012/03/22|Add a mco command that creates native OS packaging for plugins|12597| -|2012/03/21|Default to console based logging at warning level for clients|13285| -|2012/03/20|Work around SSL_read errors when using SSL or AES plugins and Stomp+SSL in Ruby < 1.9.3|13207| -|2012/03/16|Improve logging for SSL connections when using Stomp Gem newer than 1.2.0|13165| -|2012/03/14|Simplify handling of signals like TERM and INT and remove pid file on exit|13105| -|2012/03/13|Create a conventional place to store implemented_by scripts|13064| -|2012/03/09|Handle exceptions added to the Stomp 1.1 compliant versions of the Stomp gem|13020| -|2012/03/09|Specifically enable reliable communications while using the pool style syntax|13040| -|2012/03/06|Initial support for the Windows Platform|12555| -|2012/03/05|Application plugins can now disable any of 3 sections of the standard CLI argument parsers|12859| -|2012/03/05|Fix base 64 encoding and decoding of message payloads that would previous raise unexpected exceptions|12950| -|2012/03/02|Treat :hosts and :nodes as equivalents when supplying discovery data, be more strict about flags discover will accept|12852| -|2012/03/02|Allow exit() to be used everywhere in application plugins, not just in the main method|12927| -|2012/03/02|Allow batch mode to be enabled and disabled on demand during the life of a client|12854| -|2012/02/29|Show the progress bar before sending any requests to give users feedback as soon as possible rather than after first result only|12865| -|2012/02/23|Do not log exceptions in the RPC application when a non existing action is called with request parameters|12719| -|2012/02/17|Log miscellaneous Stomp errors at error level rather than debug|12705| -|2012/02/17|Improve subscription tracking by using the subID feature of the Stomp gem and handle duplicate exceptions|12703| -|2012/02/15|Improve error handling in the inventory application for non responsive nodes|12638| -|2012/02/14|Comply to Red Hat guideline by not setting mcollective to start by default after RPM install|9453| -|2012/02/14|Allow building the client libraries as a gem|9383| -|2012/02/13|On Red Hat like systems read /etc/sysconfig/mcollective in the init script to allow modification of the environment|7441| -|2012/02/13|Make the handling of symlinks to the mco script more robust to handle directories with mc- in their name|6275| -|2012/02/01|systemu and therefore MC::Shell can sometimes return nil exit code, the run() method now handles this better by returning -1 exit status|12082| -|2012/01/27|Improve handling of discovery data on STDIN to avoid failures when run without a TTY and without supplying discovery data|12084| -|2012/01/25|Allow the syslog facility to be configured|12109| -|2012/01/13|Add a RPC agent validator to ensure input is one of list of known good values|11935| -|2012/01/09|The printrpc helper did not correctly display empty strings in received output|11012| -|2012/01/09|Add a halt method to the Application framework and standardize exit codes|11280| -|2011/11/21|Remove unintended dependency on _pp_ in the ActiveMQ plugin|10992| -|2011/11/17|Allow reply to destinations to be supplied on the command line or API|9847| - - -  - -## 1.3.2 - 2011/11/17 - -This is a release in the development series of MCollective. It feature major new features. - -This release is for early adopters, production users should consider the 1.2.x series. - -### Enhancements - - * Handling of syntax errors in Application plugins have been improved - * The limit method can now be set per RPC Client instance - * Optionally show response distribution in the _ping_ application with the _--graph_ option - * Expose a statistic about expired messages via the _rpcutil_ agent and show them in the inventory application. - * Remove all the _mc-_ scripts that has been ported to applications - * AES and TTL security plugins prevent tampering with the TTL and Message Times - * The RPC client can now raise an exception rather than exit on failure - ideal for use in web apps - * Discovery during requests that has a specific limit count set have been sped up - * Specific types for :number, :float and :integer has been aded to the DDL and the RPC application has special handling for them - * Caller ID, Certificate Names and Identity Names can now only be word characters, full stop and dash - * Security plugins are now quicker to ignore miss directed messages - * The client now unsubscribes from topics it does not need anymore - * SimpleRPC now supports performing actions in batches with a sleep between each batch - * A direct request capable ActiveMQ specific plugin has been included - * Message TTLs can be set globally in the config or in the API - -### ActiveMQ specific connector - -A new connector plugin has been added that is specific to ActiveMQ and is compatible -with the new direct addressing communication system. - -You will need to change your ActiveMQ configuration to support this plugin, see the -documentation for this plugin and the examples in _ext/activemq_ have also been -updated for the new plugin. - -Anyone who use ActiveMQ is strongly recommended to use this plugin as it uses a -few ActiveMQ specific optimizations that can have a big performance enhancing effect -on your collective. - -### Batching - -Often the speed of MCollective is a problem, you want to install a package on thousands -of machines but your APT or YUM server isn't up to the task. - -You can now do batching of requests: - -{% highlight console %} -$ mco package update myapp --batch 10 --batch-sleep 60 -{% endhighlight %} - -This performs the update as usual but only affecting machines in groups of 10 and -sleeps for a minute between. - -You can also access this functionality via the API please see the docs for usage. -Any existing script or application should support this functionality without any -code changes. - -The results, error reporting, statistics reporting and so forth all stays consistent -with non batched behavior. - -The batching requires a direct addressing capable collective. - -### Backwards Compatibility - -As this release does a few more tweaks to the security system it might not work with older -versions of MCollective. - -Hopefully this will be the last release in this dev cycle to break backwards compatibility -as we're nearing the next major release. - -#### Identities, Certificates and Caller ID names - -These items have been tightened up to only match _\w\.-_. Plugins like the registration -ones might assume it is safe to just write files based on names contained in these fields -so rather than expect everyone to write secure code the framework now just enforce -a safe approach to these. - -This means if you have cases that would violate this rule you would need to change that -configuration prior to upgrading to 1.3.2 - -#### AES and SSL plugins are more secure - -If you use the AES or SSL plugins you will need to plan your rollout carefully, these plugins -are not capable of communicating with older versions of MCollective. - -#### Changes since 1.3.1 - -|Date|Description|Ticket| -|----|-----------|------| -|2011/11/16|Imrpove error reporting for code errors in application plugins|10883| -|2011/11/15|The limit method is now configurable on each RPC client as well as the config file|7772| -|2011/11/15|Add a --graph option to the ping application that shows response distribution|10864| -|2011/11/14|An ActiveMQ specific connector was added that supports direct connections|7899| -|2011/11/11|SimpleRPC clients now support native batching with --batch|5939| -|2011/11/11|The client now unsubscribes from topics when it's idle minimising the risk of receiving missdirected messages|10670| -|2011/11/09|Security plugins now ignore miss directed messages early thus using fewer resources|10671| -|2011/10/28|Support ruby-1.9.2-p290 and ruby-1.9.3-rc1|10352| -|2011/10/27|callerid, certificate names, and identity names can now only have \w . and - in them|10327| -|2011/10/25|When discovery information is provided always accept it without requiring reset first|10265| -|2011/10/24|Add :number, :integer and :float to the DDL and rpc application|9902| -|2011/10/22|Speed up discovery when limit targets are set|10133| -|2011/10/22|Do not attempt to validate TTL and Message Times on replies in the SSL plugin|10226| -|2011/10/03|Allow the RPC client to raise an exception rather than exit on failure|9360| -|2011/10/03|Allow the TTL of requests to be set in the config file and the SimpleRPC API|9399| -|2011/09/26|Cryptographically secure the TTL and Message Time of requests when using AES and SSL plugins|9400| -|2011/09/20|Update default shipped configurations to provide a better out of the box experience|9452| -|2011/09/20|Remove deprecated mc- scripts|9402| -|2011/09/20|Keep track of messages that has expired and expose the stat in rpcutil and inventory application|9456| - -  - -## 1.3.1 - 2011/09/16 - -This is a release in the development series of MCollective. It feature major new features -and bug fixes. - -This release is for early adopters, production users should consider the 1.2.x series. - -### Enhancements - - * Messaging has been completely reworked internally to be more generic and easier to integrate - with other middleware - * When using Stomp 1.1.9 detailed connection logs are kept showing connections, reconnections - and communication errors - * A new point to point - but still via the middleware - communications ability has been introduced - * When point to point comms is enabled, favour this mode when small number of nodes are being addressed - * Add -j to any SimpleRPC client. Clients using _printrpc_ will automatically support a new JSON output format - * A new rich discovery language was added using the -S flag - * SimpleRPC validators can now also validate boolean data - * The default location of _classes.txt_ has changed to be in line with Puppet defaults. - * A default TTL of 60 seconds are set on all messages. This is a start towards replay protection and is needed - for the new point to point comms style - * Discovery is now optional. If you supply an identity filter discovery will be bypassed. Additionally discovery - can be supplied in arrays, text or JSON formats. This requires the new point to point comms model. - -### Bug Fixes - - * Missing DDL files on the servers are now logged at debug level to minimise noise in the logs - * The RC scripts set RUBYLIB, remove this and rely on the operating system to be set up correctly - * Invalid fact filters supplied on the CLI now raises an error rather than create empty filters - -### New Discovery Language - -Previously dicovery was very limited, filters were simply run one after the other and you could not do -anything complex like a mix of OR and AND boolean logic. - -A new compact discovery language was introduced perfect for use on the command line, an example below: - -{% highlight console %} -$ mco find -S "((fqdn=/example.com/ or fqdn=/another.com/) or customer=acme) and apache and physicalprocessorcount>2" -{% endhighlight %} - -The EBNF for this language can be seen below, it's available on the command line and the API - - compound = ["("] expression [")"] {["("] expression [")"]} - expression = [!|not]statement ["and"|"or"] [!|not] statement - char = A-Z | a-z | < | > | => | =< | _ | - |* | / { A-Z | a-z | < | > | => | =< | _ | - | * | / | } - int = 0|1|2|3|4|5|6|7|8|9{|0|1|2|3|4|5|6|7|8|9|0} - -### Point to Point comms - -Previously MCollective could only broadcast messages and was tied to a discovery model. This is in line -with the initial goals of the project, having solved that we want to mix in a more traditional messaging -style. - -The messaging layer now supports per node destinations that allows you to address a node, even if its down, -doesn't yet exist or if you cannot come up with a filter that would match a group of arbitrarily selected -nodes. - -When this mode is in use you tell it using either text, arrays or JSON data which machines to communicate with -it will then talk directly to those nodes via the middleware and if any of them are down you will get the -usual no responses report after DDL configured timeout, this is a smooth transparent to the end user mix -in communication modes. - -It is ideal for building deployers, web apps and so forth where you know exactly which nodes should be there -and you'd like to influence the MCollective network addressing, perhaps from a CMDB you built yourself. - -This is the start towards an assured style of delivery, you can consider it the TCP to MCollective's UDP. -Both modes of communication will be supported in the future and both will have access to all the same agents -clients etc. - -This is feature is still maturing, you enable it using the _direct\_\addressing_ configuration option. At -present the STOMP connector supports it but it is not optimized for networks larger than 20 to 30 hosts. A -new connector is being developed that uses ActiveMQ features to achieve this efficiently. - -### Pluggable / Optional Discovery - -If you did _mco rpc rpcutil ping -I box.example.com -I another.example.com_ mcollective will now just assume -you know what you want, it won't do a discover to confirm those machines exist or not, it will just go and -talk with them. This is a big end user visible speed improvement. If however you did a filter like _-I /example.com/_ -it cannot know which machines you want to reach and so a traditional broadcast discovery is done first. - -When the direct addressing mode is enabled various behind the scenes optimizations are being done: - - * If a discovery is done and it finds you only want to address 10 or fewer nodes it will use direct mode for that - request. This avoids a second needless broadcast. This is less efficient to the middleware but does not send - needless messages to uninterested nodes that would then just ignore them. - * The _rpc_ application supports piping output from one to the next. Example of this below. - -{% highlight console %} -$ mco rpc package update package=foo -W customer=acme -j|mco rpc service restart service=bar -{% endhighlight %} - -This will update a package on machines matching _customer=foo_ and then restart the service _bar_ on those machines. - -The first request is doing traditional discovery based on the fact while the 2nd request is not doing discovery -at all, it uses the JSON output enabled by -j as discovery data and then restart the service on only those machines. - -These abilities are exposed in the SimpleRPC client API and you can write your own schemes, query your own databases etc - -### Backwards Compatibility - -This is a big release and the entire messaging system has been redesigned, rewritten and has had features added. -As such there might be problems running mixed 1.2.x and 1.3.1 networks, we'd ask users to test this in lab situations -and provide us feedback to improve the eventual transition from 1.2.x to 1.4.x. We did though aim to maintain backward -compatibility and the intention is to fix any bugs reported where a default configured 1.3.x cannot co-habit with a -previous 1.2.x build. - -Enabling the new direct addressing mode is a big configuration change both in your collective and the middleware as such -soon as you enable it there will be compatibility issues until all your nodes are up to the same level. Specifically old -nodes will just ignore your direct requests. - -The default location for _classes.txt_ has changed to _/var/lib/puppet/state/classes.txt_ you need to ensure -this file exists or configure either MCollective or Puppet accordingly else your classes filters will break - -Messages are now valid for only 60 seconds, nodes will _ignore_ messages older than 60 seconds. This means -your clocks have to be in sync on your entire collective. We use UTC time for the TTL check so your machines -can be in different time zones. At present the 60 second threshold is hard coded, it will become configurble on a -per message basis in future. - -#### Changes since 1.3.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2011/09/9|Use direct messaging where possible for identity filters and make the rpc application direct aware|8466| -|2011/08/29|Enforce a 60 second TTL on all messages by default|8325| -|2011/08/29|Change the default classes.txt file to be in line with Puppet defaults|9133| -|2011/08/06|Add reload-agents and reload-loglevel commands to the redhat RC script|7730| -|2011/08/06|Avoid reloading the authorization class over and over from disk on each request|8703| -|2011/08/06|Add a boolean validator to SimpleRPC agents|8799| -|2011/08/06|Justify text results better when using printrpc|8807| -|2011/07/22|Add --version to the mco utility|7822| -|2011/07/22|Add missing meta data to the discovery agent|8497| -|2011/07/18|Raise an error if invalid format fact filters are supplied|8419| -|2011/07/14|Add a rich discovery query language|8181| -|2011/07/08|Do not set RUBYLIB in the RC scripts, the OS should do the right thing|8063| -|2011/07/07|Add a -j argument to all SimpleRPC clients that causes printrpc to produce JSON data|8280| -|2011/06/30|Add the ability to do point to point comms for requests affecting small numbers of hosts|7988| -|2011/06/21|Add support for Stomp Gem version 1.1.9 callback based logging|7960| -|2011/06/21|On the server side log missing DDL files at debug and not warning level|7961| -|2011/06/16|Add the ability for nodes to subscribe to per-node queues, off by default|7225| -|2011/06/12|Remove assumptions about middleware structure from the core and move it to the connector plugins|7619| - -  - -## 1.2.1 - 2011/06/30 - -This is a maintenance release in the production series of MCollective and is a recommended -upgrade for all users of 1.2.0. - -### Bug Fixes - - * Improve error handling in the inventory application - * Fix compatablity problems with RedHat 4 init scripts - * Allow . in Fact names - * Allow applications to use the exit method - * Correct parsing of the MCOLLECTIVE_EXTRA_OPTS environment variable - -### Backwards compatibility - -This release should be 100% backward compatable with version 1.2.0 - -#### Changes since 1.2.0 - -|Date|Description|Ticket| -|----|-----------|------| -|2011/06/02|Correct parsing of MCOLLECTIVE_EXTRA_OPTS in cases where no config related settings were set|7755| -|2011/05/23|Allow applications to use the exit method as would normally be expected|7626| -|2011/05/16|Allow _._ in fact names|7532| -|2011/05/16|Fix compatibility issues with RH4 init system|7448| -|2011/05/15|Handle failures from remote nodes better in the inventory app|7524| -|2011/05/06|Revert unintended changes to the Debian rc script|7420| -|2011/05/06|Remove the _test_ agent that was accidentally checked in|7425| - -  - -## 1.3.0 - 2011/06/08 - -This is a release in the development series of mcollective. It features major -new features, some bug fixes and internal structure refactoring. - -This release is for early adopters, production users should consider the 1.2.x series. - -### Enhancements - - * Agents can now programatically declare if they should work on a node - * Applications can now use the exit method as normal and clean disconnects will be done - * The target collective for registration messages is configurable. In the past it defaulted to main_collective - -### Bug Fixes - - * Error reporting in applications, agents and mcolletive core has been improved - * The RC script works better on Red Hat 4 based systems - -### Other Changes - - * The connector layer is being improved to make it easier to use other middleware. - This release starts this process but it's far from complete. - * The sshkey plugin was removed from core and moved to the plugins project - -### Backwards Compatibility - -If you were using the sshkey plugin you need to ensure your CM system is copying it out prior to this -upgrade as the packages will not contain it anymore. - -If you have your own connectors other than the STOMP one we supply you should wait to upgrade till 1.3.1 -at which point you will need to make extensive changes to your plugins internals. If your CM is copying -out the connector you have to ensure that when this version of MCollective start that the new plugin is -in place. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2011/06/07|Exceptions raised during option parsing were not handled and resulted in stack traces|7796| -|2011/06/06|Remove the sshkey, it's being moved to the plugin repository|7794| -|2011/06/02|Correct parsing of MCOLLECTIVE_EXTRA_OPTS in cases where no config related settings were set|7755| -|2011/05/31|Disconnect from the middleware when an application calls exit|7712| -|2011/05/29|Validations failure in RPC agents will now raise the correct exceptions as documented|7711| -|2011/05/25|Make the target collective for registration messages configurable|7650| -|2011/05/24|Rename the connector plugins send method to publish to avoid issues ruby Object#send|7623| -|2011/05/23|Log a warning when the CF file parsing fails rather than raise a whole ruby exception|7627| -|2011/05/23|Allow applications to use the exit method as would normally be expected|7626| -|2011/05/22|Refactor subscribe and unsubscribe so that middleware structure is entirely contained in the connectors|7620| -|2011/05/21|Add the ability for agents to programatically declare if they should work on a node|7583| -|2011/05/20|Improve error reporting in the single application framework|7574| -|2011/05/16|Allow _._ in fact names|7532| -|2011/05/16|Fix compatibility issues with RH4 init system|7448| -|2011/05/15|Handle failures from remote nodes better in the inventory app|7524| -|2011/05/06|Revert unintended changes to the Debian rc script|7420| -|2011/05/06|Remove the _test_ agent that was accidentally checked in|7425| - -  - -## 1.2.0 - 2011/05/04 - -This is the next production release of MCollective. It brings to an -end active support for versions 1.1.4 and older. - -This release brings to general availability all the features added in the -1.1.x development series. - -### Enhancements - - * The concept of sub-collectives were introduced that help you partition - your MCollective traffic for network isolation, traffic management and security - * The single executable framework has been introduced replacing the old - _mc-\*_ commands - * A new AES+RSA security plugin was added that provides strong encryption, - client authentication and message security - * New fact matching operators <=, >=, <, >, !=, == and =~. - * Actions can be written in external scripts and therefore other languages - than Ruby, wrappers exist for PHP, Perl and Python - * Plugins can now be configured using the _plugins.d_ directory - * A convenient and robust exec wrapper has been written to assist in calling - external scripts - * The _MCOLLECTIVE\_EXTRA\_OPTS_ environment variable has been added that will - add options to all client scripts - * Network timeout handling has been improved to better take account of latency - * Registration plugins can elect to skip sending of registration data by - returning _nil_, previously nil data would be published - * Multiple libdirs are supported - * The logging framework is pluggable and easier to use - * Fact plugins can now force fact cache invalidation. The YAML plugin will - force a cache clear as soon as the source YAML file updates - * The _ping_ application now supports filters - * Network payload can now be Base64 encoded avoiding issues with Unicode characters - in older Stomp gems - * All fact plugins are now cached and only updated every 300 seconds - * The progress bar now resizes based on terminal dimensions - * DDL files with missing output blocks will not invalidate the whole DDL - * Display of DDL assisted complex data has been improved to be more readable - * Stomp messages can have a priority header added for use with recent versions - of ActiveMQ - * Almost 300 unit tests have been written, lots of old code and any new code being - written is subject to continuos testing on Ruby 1.8.5, 1.8.6 and 1.9.2 - * Improved the Red Hat RC script to be more compliant with distribution policies - and to reuse the builtin functions - -### Deprecations and removed functionality - - * The old _mc-\*_ commands are being removed in favor for the new _mco_ command. - The old style is still available and your existing scripts will keep working but - porting to the new single executable system is very easy and encouraged. - * _MCOLLECTIVE_TIMEOUT_ and _MCOLLECTIVE_DTIMEOUT_ were removed in favor of _MCOLLECTIVE\_EXTRA\_OPTS_ - * _mc-controller_ could exit all mcollectived instances, this feature was not ported - to the new _mco controller_ application - -### Bug Fixes - - * mcollectived and all of the standard supplied client scripts now disconnects - cleanly from the middleware avoiding exceptions in the ActiveMQ logs - * Communications with the middleware has been made robust by adding a timeout - while sending - * Machines that do not pass security validation are now handled as having not - responded at all - * When a fire and forget request was sent, replies were still sent, they are - now suppressed - -### Backwards compatibility - -This release can communicate with machines running older versions of mcollective -there are though a few steps to take to ensure a smooth upgrade. - -#### Backward compatible sub-collective setup - -{% highlight ini %} -topicprefix = /topic/mcollective -{% endhighlight %} - -This has to change to: - -{% highlight ini %} -topicprefix = /topic/ -main_collective = mcollective -collectives = mcollective -{% endhighlight %} - -#### Security Plugins - -The interface for the _encodereply_ method on the security plugins have changed -if you are using any of the community plugins or wrote your own you should update -them with the latest at the time you upgrade to 1.2.0 - -#### Fact Plugins - -The interface to the fact plugins have been greatly simplified, this means you need to -update to new plugins at the time you upgrade to 1.2.0 - -You can place these new plugins into the plugindir before upgrading. The old mcollective -will not use these plugins and the new one will not touch the old ones. This will allow -for a clean rollback. - -Once the new version is deployed you will immediately have caching on all fact types -at 300 seconds you can tune this using the fact_cache_time setting in the configuration file. - -#### New fact selectors - -The new fact selectors are only available on newer versions of mcollective. If a client -attempts to use them and an older version of the server is on the network those older -servers will treat all fact lookups as == - -#### Changes since 1.1.4 - -|Date|Description|Ticket| -|----|-----------|------| -|2011/05/03|Improve Red Hat RC script by using distro builtin functions|7340| -|2011/05/01|Support setting a priority on Stomp messages|7246| -|2011/04/30|Handle broken and incomplete DDLs better and improve the format of DDL output|7191| -|2011/04/23|Encode the target agent and collective in requests|7223| -|2011/04/20|Make the SSL Cipher used a config option|7191| -|2011/04/20|Add a clear method to the PluginManager that deletes all plugins, improve test isolation|7176| -|2011/04/19|Abstract the creation of request and reply hashes to simplify connector plugin development|5701| -|2011/04/15|Improve the shellsafe validator and add a Util method to do shell escaping|7066| -|2011/04/14|Update Rakefile to have a mail_patches task|6874| -|2011/04/13|Update vendored systemu library for Ruby 1.9.2 compatibility |7067| -|2011/04/12|Fix failing tests on Ruby 1.9.2|7067| -|2011/04/11|Update the DDL documentation to reflect the _mco help_ command|7042| -|2011/04/11|Document the use filters on the CLI|5917| -|2011/04/11|Improve handling of unknown facts in Util#has_fact? to avoid exceptions about nil#clone|6956| -|2011/04/11|Correctly set timeout on the discovery agent to 5 seconds as default|7045| -|2011/04/11|Let rpcutil#agent_inventory supply _unknown_ for missing values in agent meta data|7044| - -  - -## 1.1.4 - 2011/04/07 - -This is a release in the development series of mcollective. It features major -new features and some bug fixes. - -This release is for early adopters, production users should consider the 1.0.x series. - -### Actions in other languages - -We have implemented the ability to write actions in languages other than Ruby. -This is done via simple JSON API documented in [in our docs](simplerpc/agents.html#actions-in-external-scripts) - -The _ext_ directory on [GitHub](https://github.com/puppetlabs/marionette-collective/tree/master/ext/action_helpers) -hosts wrappers for PHP, Perl and Python that makes using this interface easier. - -{% highlight ruby %} -action "test" do - implemented_by "/some/external/script" -end -{% endhighlight %} - -Special thanks to the community members who contributed the wrappers. - -### Enhancements - - * Actions can now be written in any language - * Plugin configuration can be kept in _/etc/mcollective/plugin.d_ - * _mco inventory_ now shows collective and sub-collective membership - * mc-controller has been deprecated for _mco controller_ - * Agents are now ran using new instances of the classes rather than reuse the exiting - one to avoid concurrency related problems - -### Bug Fixes - - * When mcollectived exits it now cleanly disconnects from the Middleware - * The _rpcutil_ agent is less strict about valid Fact names - * The default configuration files have been updated for sub-collectives - -### Backwards Compatibility - -This release will be backward compatible with version _1.1.3_ for compatibility -with earlier releases see the notes for _1.1.3_ and the sub collective related -configuration changes. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2011/03/28|Correct loading of vendored JSON gem|6877| -|2011/03/28|Show collective and sub collective info in the inventory application|6872| -|2011/03/23|Disconnect from the middleware when mcollectived disconnects|6821| -|2011/03/21|Update rpcutil ddl file to be less strict about valid fact names|6764| -|2011/03/22|Support reading configuration from configfir/plugin.d for plugins|6623| -|2011/03/21|Update default configuration files for sub-collectives|6741| -|2011/03/16|Add the ability to implement actions using external scripts|6705| -|2011/03/15|Port mc-controller to the Application framework and deprecate the exit command|6637| -|2011/03/13|Only cache registration and discovery agents, handle the rest as new instances|6692| -|2011/03/08|PluginManager can now create new instances on demand for a plugin type|6622| - -  - -## 1.1.3 - 2011/03/07 - -This is a release in the development series of mcollective. It features major -new features and some bug fixes. - -This release is for early adopters, production users should consider the 1.0.x series. - -### Enhancements - - * Add the ability to partition collectives into sub-collectives for security and - network traffic management - * Add a exec wrapper for agents that provides unique environments and cwds in a - thread safe manner as well as avoid zombie processes - * Automatically pass Application options to rpcclient when options are not - specifically provided - * Rename _/usr/sbin/mc_ to _/usr/bin/mco_ - -### Bug Fixes - - * Missing _libdirs_ will not cause crashes anymore - * Parse `MCOLLECTIVE_EXTRA_OPTS` correctly with multiple options - * `file_logger` failures are handled better - * Improve middleware communication in unreliable settings by adding timeouts - around middleware operations - -### Backwards Compatibility - -The configuration format has changed slightly to accomodate the concept of -collective names and sub-collectives. - -In older releases the configuration was: - -{% highlight ini %} -topicprefix = /topic/mcollective -{% endhighlight %} - -This has to change to: - -{% highlight ini %} -topicprefix = /topic/ -main_collective = mcollective -collectives = mcollective -{% endhighlight %} - -When setup as above a old and new version will be compatible but as soon as you -start configuring the new sub-collective feature you will loose compatiblity -between versions. - -Various defaults apply, if you configure it with these exactly topic and -collective names you can leave off the `main_collective` and `collectives` -directives as the above settings would be their defaults - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2011/03/04|Rename /usr/sbin/mc to /usr/bin/mco|6578| -|2011/03/01|Wrap rpcclient in applications ensuring that options is always set|6308| -|2011/02/28|Make communicating with the middleware more robust by including send calls in timeouts|6505| -|2011/02/28|Create a wrapper to safely run shell commands avoiding zombies|6392| -|2011/02/19|Introduce Sub-collectives for network partitioning|5967| -|2011/02/19|Improve error handling when parsing arguments in the rpc application|6388| -|2011/02/19|Fix error logging when file_logger creation fails|6387| -|2011/02/17|Correctly parse MCOLLECTIVE\_EXTRA\_OPTS in the new unified binary framework|6354| -|2011/02/15|Allow the signing key and Debian distribution to be customized|6321| -|2011/02/14|Remove inadvertently included package.ddl|6313| -|2011/02/14|Handle missing libdirs without crashing|6306| - -  - -## 1.0.1 - 2011/02/16 - -### Release Focus and Notes - -This is a minor bug fix release. - -### Bugs Fixed - - * The YAML fact plugin failed to remove deleted facts from memory - * The _-_ character is now allowed in Fact names for the rpcutil agent - * Machines that fali security validations were not reported correctly, - they are now treated as having not responded at all - * Timeouts on RPC requests were too aggressive and did not allow for slow networks - -### Backwards Compatibility - -This release will be backward compatible with older releases. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2011/02/02|Include full Apache 2 license text|6113| -|2011/01/29|The YAML fact plugin kept deleted facts in memory|6056| -|2012/01/04|Use the LSB based init script on SUSE|5762| -|2010/12/30|Allow - in fact names|5727| -|2010/12/29|Treat machines that fail security validation like ones that did not respond|5700| -|2010/12/25|Allow for network and fact source latency when calculating client timeout|5676| -|2010/12/25|Increase the rpcutil timeout to allow for slow facts|5679| - -## 1.1.2 - 2011/02/14 - -This is a release in the development series of mcollective. It features minor -bug fixes and features. - -This release is for early adopters, production users should consider the 1.0.x series. - -### Bug Fixes - - * The main fix in this release is a packaging bug in Debian systems that prevented - both client and server from being installed on the same machine. - * Backwards compatibility fix for fact filters that are empty strings - -### Enhancement - - * Registration plugins can now return nil which will skip that specific registration - message. This will enable plugins to decide based on some node state if a message - should be sent or not. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2011/02/13|Surpress replies to SimpleRPC clients who did not request results|6305| -|2011/02/11|Fix Debian packaging error due to the same file in multiple packages|6276| -|2011/02/11|The application framework will now disconnect from the middleware for consistency|6292| -|2011/02/11|Returning _nil_ from a registration plugin will skip registration|6289| -|2011/02/11|Set loglevel to warn by default if not specified in the config file|6287| -|2011/02/10|Fix backward compatibility with empty fact strings|6278| - -## 1.1.1 - 2011/02/02 - -This is a release in the development series of mcollective. It features major new -features and numerous bug fixes. Please pay careful attention to the upgrading -section as there is some changes that are not backward compatible. - -This release is for early adopters, production users should consider the 1.0.x series. - -### AES+RSA Security Plugin - -A new security plugin that encrypts the payloads, uniquely identify senders and secure -replies from inspection by other people on the collective has been written. The plugin -can re-use Puppet certificates and supports distributing of public keys if you wish. - -This plugin and its deployment is very complex and it has a visible performance impact -but we felt it was a often requested feature and so decided to implement it. - -Full documentation for this plugin can be found [in our docs](reference/plugins/security_aes.html), please read them very -carefully should you choose to deploy this plugin. - -### Single Executable Framework - -In the past a lot of the CLI tools have behaved inconsistently as the mc scripts were -mostly just written to serve immediate needs, we are starting a process of improving -these scripts and making them more robust. - -The first step is to create a new framework for CLI commands, we call these Single Executable -Applications. A new executable called _mc_ is being distributed with this release: - -{% highlight console %} -$ mc -The Marionette Collective version 1.1.1 - -/usr/sbin/mc: command (options) - -Known commands: rpc filemgr inventory facts ping find help -{% endhighlight %} - -{% highlight console %} -$ mc help -The Marionette Collection version 1.1.1 - - facts Reports on usage for a specific fact - filemgr Generic File Manager Client - find Find hosts matching criteria - help Application list and RPC agent help - inventory Shows an inventory for a given node - ping Ping all nodes - rpc Generic RPC agent client application -{% endhighlight %} - -{% highlight console %} -$ mc rpc package status package=zsh -Determining the amount of hosts matching filter for 2 seconds .... 51 - - * [ ============================================================> ] 51 / 51 - - - test.com: - Properties: - {:provider=>:yum, - :release=>"3.el5", - :arch=>"x86_64", - :version=>"4.2.6", - :epoch=>"0", - :name=>"zsh", - :ensure=>"4.2.6-3.el5"} -{% endhighlight %} - -You can see these commands behave just like their older counter parts but is more integrated -and discovering available commands is much easier. - -Agent help that was in the past available through _mc-rpc --ah agentname_ is now available through -_mc help agentname_ and error reporting is short single line reports by default but by adding -_-v_ to the command line you can get full Ruby backtraces. - -We've maintained backward compatibility by creating wrappers for all the old mc scripts but these -might be deprecated in future. - -These application live in the normal plugin directories and should make it much easier to distribute -plugins in future. - -We will port the scripts for plugins to this framework and encourage you to do the same when writing -new CLI tools. An example of a ported CLI can be seen in the _filemgr_ agent. - -Find the documentation for these plugins [here](reference/plugins/application.html). - -### Miscellaneous Improvements - -The logging system has been ra-efactored to not use a Signleton, logging messages are now simply: - -{% highlight ruby %} -MCollective::Log.notice("hello world") -{% endhighlight %} - -A backwards compatible wrapper exist to prevent existing code from breaking. - -In some cases - like when using MCollective from within Rails - the STOMP -gem would fail to decode the payloads. We've worked with the authors and -a new release was made that makes this more robust but we've also enabled -Base64 encoding on the Stomp connector for those who can't upgrade the Gem -and who are running into this problem. - -### Bug Fixes - - - * Machines that do not pass security checks are handled as having not responded - so that these are listed in the usual stat for non responsive hosts - * The - character is now allowed in Fact names by the DDL for rpcutil - * Version 1.1.0 introduced a bug with reloading agents from disks using USR1 and mc-controller - -### Enhancements - - * New AES+RSA based security plugin was added - * Create a new single executable framework and port several mc scripts - * Security plugins have access to the callerid they are responding to - * The logging methods have been improved by removing the use of Singletons - * The STOMP connector can now Base64 encode all sent data to deal with en/decoding issues by the gem - * The rpcutil agent has a new _ping_ action - * the _mc ping_ client now supports standard filters - * DDL documentation has been updated to show you can disable type validations in the DDL - * Fact plugins can now force fact cache invalidation, the YAML plugin will immediately load new facts when mtime on the file change - * Improve _1.0.0_ compatibility for _foo=/bar/_ style fact matches at the expense of _1.1.0_ compatibility - -### Upgrading - -Upgrading should be mostly painless as most things are backward compatible. - -We discovered that we broke backward compatibility with _1.0.0_ and _0.4.x_ Fact filters. A filter in the form -_foo=/bar/_ would be treated as an equality filter and not a regular expression. - -This releases fixes this compatibility with older versions at the expense of compatibility with _1.1.0_. If you -are upgrading from _1.1.0_ keep this in mind and plan accordingly, once you've upgraded a client its requests that -contain these filters will not be correctly parsed on servers running _1.1.0_. - -The security plugins have changed slightly, if you wrote your own security plugin the interface to _encodereply_ -has changed slightly. All the bundled security plugins have been updated already and older ones will just -keep working. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2011/02/02|Load the DDL from disk once per printrpc call and not for every result|5958| -|2011/02/02|Include full Apache 2 license text|6113| -|2011/01/31|Create a new single executable application framework|5897| -|2011/01/30|Fix backward compatibility with old foo=/bar/ style fact searches|5985| -|2011/01/30|Documentation update to reflect correct default identity behavior|6073| -|2011/01/29|Let the YAML file force fact reloads when the files update|6057| -|2011/01/29|Add the ability for fact plugins to force fact invalidation|6057| -|2011/01/29|Document an approach to disable type validation in the DDL|6066| -|2011/01/19|Add basic filters to the mc-ping command|5933| -|2011/01/19|Add a ping action to the rpcutil agent|5937| -|2011/01/17|Allow MC::RPC#printrpc to print single results|5918| -|2011/01/16|Provide SimpleRPC style results when accessing the MC::Client results directly|5912| -|2011/01/11|Add an option to Base64 encode the STOMP payload|5815| -|2011/01/11|Fix a bug with forcing all facts to be strings|5832| -|2011/01/08|When using reload_agents or USR1 signal no agents would be reloaded|5808| -|2011/01/04|Use the LSB based init script on SUSE|5762| -|2011/01/04|Remove the use of a Singleton in the logging class|5749| -|2011/01/02|Add AES+RSA security plugin|5696| -|2010/12/31|Security plugins now have access to the callerid of the message they are replying to|5745| -|2010/12/30|Allow - in fact names|5727| -|2010/12/29|Treat machines that fail security validation like ones that did not respond|5700| - -## 1.1.0 - 2010/12/29 - -This is the first in a new development series, as such there will be rapid changes -and new features. We cannot guarantee the changes will be backward compatible but -we will as before try to keep these releases solid and production quality. - -Production users who do not wish to have rapid change should use release 1.0.0. - -This release focus mainly on getting all the community contributed code into a release -and addressing some issues I had but wasn't comfortable fixing them late in the -previous development series. - -Please read these notes carefully we are **removing** some old functionality and changing -some internals, you need to carefully review the text below. - -### Bug Fixes - - * The progress bar will now try hard to detect screen size and adjust itself, - failing back to a dumb mode if it can't work it out. - * rpcutil timeout was too short when considering slow facts and network latency - -### Improvements - - * libdir can now be multiple directories specified with : separation - Thanks to Richard Clamp - * Logging is now pluggable, 3 logger types are supported - file, syslog and console. Thanks to - Nicolas Szalay for the initial Syslog code - * A new experimental ssh agent based security system. Thanks to Jordan Sissel - * New fact matching operators <=, >=, <, >, !=, == and =~. Thanks to Mike Pountney - * SimpleRPC fact_filter method can now take any valid fact string as input in addition to the old format - * A message gets logged at startup showing mcollective version and logging level - * The fact plugin format has been changed, simplified, made thread safe and global caching added. - This breaks backward compatibility with old fact sources - * Creating options hashes has been simplified by adding a helper that creates them for you - * Calculating the client timeout has been improved by including for latency and fact source slowness - * Audit log lines are now on one line and include a ISO 8601 format date - -### Removed Functionality - - * The old MCOLLECTIVE_TIMEOUT and MCOLLECTIVE_DTIMEOUT were removed, a new MCOLLECTIVE_EXTRA_OPTS - was added which should allow much more flexibility. Supply any command line options in this var - -### Upgrading - -Upgrading should be easy the only backward incompatible change is the Facts format. If you only use -the included YAML plugin the upgrade will just work if you use the packages. If you use either the -facter or ohai plugins you will need to download new plugins from the community plugin page. - -If you wrote your own Facts plugin you will need to change it a bit: - - * The old get_facts method should now be load_facts_from_source - * The class for facts have to be in the form MCollective::Facts::Foo_facts and the filename should match - -This is all, your facts can now be much simpler as threading and caching is handled in the base class. - -You can place these new plugins into the plugindir before upgrading. The old mcollective will not use -these plugins and the new one will not touch the old ones. This will allow for a clean rollback. - -Once the new version is deployed you will immediately have caching on all fact types at 3000 seconds -you can tune this using the fact_cache_time setting in the configuration file. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2010/12/28|Adjust the logfile audit format to include local time and all on one line|5694| -|2010/12/26|Improve the SimpleRPC fact_filter helper to support new fact operators|5678| -|2010/12/25|Increase the rpcutil timeout to allow for slow facts|5679| -|2010/12/25|Allow for network and fact source latency when calculating client timeout|5676| -|2010/12/25|Remove MCOLLECTIVE_TIMEOUT and MCOLLECTIVE_DTIMEOUT environment vars in favor of MCOLLECTIVE_EXTRA_OPTS|5675| -|2010/12/25|Refactor the creation of the options hash so other tools don't need to know the internal formats|5672| -|2010/12/21|The fact plugin format has been changed and simplified, the base now provides caching and thread safety|5083| -|2010/12/20|Add parameters <=, >=, <, >, !=, == and =~ to fact selection|5084| -|2010/12/14|Add experimental sshkey security plugin|5085| -|2010/12/13|Log a startup message showing version and log level|5538| -|2010/12/13|Add a console logger|5537| -|2010/12/13|Logging is now pluggable and a syslog plugin was provided|5082| -|2010/12/13|Allow libdir to be an array of directories for agents and ddl files|5253| -|2010/12/13|The progress bar will now intelligently figure out the terminal dimensions|5524| - -## 1.0.0 - 2010/12/13 - -### Release Focus and Notes - -This is a bug fix and minor improvement release. - -We will maintain the 1.0.x branch as a stable supported branch. The features -currently in the branch will be frozen and we'll only do bug fixes. - -A new 1.1.x series of releases will be done where we will introduce new features. -Once the 1.1.x code base reaches a mature point it will become the new stable -release and so forth. - -### Bug Fixes - - * Settings like retry times were ignored in the Stomp connector - * The default init script had incorrect LSB comments - * The rpcutil DDL has better validation and will now match all facts - -### New Features and Enhancements - - * You can now send RPC requests to a subset of discovered nodes - * SimpleRPC custom_request can now be used to create fire and forget requests - * Clients can now cleanly disconnect from the middleware. Bundled clients have been - updated. This should cause fewer exceptions in ActiveMQ logs - * Rather than big exceptions many clients will now log errors only - * mc-facts has been reworked to be a SimpleRPC client, this speeds it up significantly - * Add get_config_item to rpcutil to retrieve a running config value from a server - * YAML facts are now forced to be all strings and is thread safe - * On RedHat based systems the requirement for the LSB packages has been removed - -The first feature is a major new feature in SimpleRPC. If you expose a service redundantly -on your network using MCollective you wouldn't always want to send requests to all the -nodes providing the service. You can now limit the requests to an arbitrary amount -using the new --limit-nodes option which will also take a percentage. A shortcut -1 has -been added that is the equivalent to --limit-nodes 1 - -### Backwards Compatibility - -This release will be backward compatible with older releases. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2010/12/04|Remove the LSB requirements for RedHat systems|5451| -|2010/11/23|Make the YAML fact source thread safe and force all facts to strings|5377| -|2010/11/23|Add get_config_item to rpcutil to retrieve a running config value from a server|5376| -|2010/11/20|Convert mc-facts into a SimpleRPC client|5371| -|2010/11/18|Added GPG signing to Rake packaging tasks (SIGNED=1)|5355| -|2010/11/17|Improve error messages from clients in the case of failure|5329| -|2010/11/17|Add helpers to disconnect from the middleware and update all bundled clients|5328| -|2010/11/16|Correct LSB provides and requires in default init script|5222| -|2010/11/16|Input validation on rpcutil has been improved to match all valid facts|5320| -|2010/11/16|Add the ability to limit the results to a subset of hosts|5306| -|2010/11/15|Add fire and forget mode to SimpleRPC custom_request|5305| -|2010/11/09|General connection settings to the Stomp connector was ignored|5245| - -## 0.4.10 - 2010/10/18 - -### Release Focus and Notes - -This is a bug fix and minor improvement release. - -### Bug Fixes - - * Multiple RPC proxy classes in the same script would not all share the same command line options - * Ruby 1.9.x compatibility has been improved - * A major bug in registration has been fixed, any exception in the registration logic would have - resulted in a high CPU consuming loop - -The last bug is a major issue it will result in the _mcollectived_ consuming lots of CPU, updating to -this version of MCollective is strongly suggested. Should you run into this problem on a large scale -you can use _mc-controller exit_ to exit all your _mcollectived_ processes at the same time. - -### New Features and Enhancements - - * The PSK security plugin can now be configured to set the callerid to a few different values - useful for cases where you want to do group based RPC Authorization for example. - * Info logging has been minimised by demoting the 'not targeted at us' message to debug - * Document the 'exit' option to mc-controller - -### Backwards Compatibility - -This release will be backward compatible with older releases. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2010/10/18|Document exit command to mc-controller|152| -|2010/10/13|Log messages that don't pass the filters at debug level|149| -|2010/10/03|Preserve options in cases where RPC::Client instances exist in the same program|148| -|2010/09/30|Add the ability to set different types of callerid in the PSK plugin|145| -|2010/09/30|Improve Ruby 1.9.x compatibility|142| -|2010/09/29|Improve error handling in registration to avoid high CPU usage loops|143| - - -## 0.4.9 - 2010/09/21 - -### Release Focus and Notes - -This is a bug fix and minor improvement release. - -### Bug Fixes - - * Internal data structure related to Agent meta data has been fixed, no user impact from this - * When using per-user config files the _rpc-help.erb_ template could not be found - * The log files will now rotate by default keeping 5 x 2MB files - * The config were parsed multiple times in complex scripts, this has been eliminated - * MCollective::RPC loaded a bunch of unneeded stuff into Object, this has been cleaned up - * Various packaging related tweaks were done - -### New Features - - * We ship a new agent called _rpcutil_ with the base system, you can use this agent to get inventory etc from your _mcollectived_. _mc-inventory_ has been rewritten to use this agent and should serve as a good reference for what you can get from the agent. - * The DDL now support :boolean style inputs, mc-rpc also turn true/false on the command line into booleans when needed - -### Backwards Compatibility - -This release will be backward compatible with older releases. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2010/09/20|Improve Debian packaging task|140| -|2010/09/20|Add :boolean type support to the DDL|138| -|2010/09/19|Refactor MCollective::RPC to add less unneeded stuff to Object|137| -|2010/09/18|Prevent duplicate config loading with multiple clients active|136| -|2010/09/18|Rotate the log file by default, keeping 5 x 2MB files|135| -|2010/09/18|Write a overview document detailing security of the collective|131| -|2010/09/18|Add MCollective.version, set it during packaging and include it in the rpcutil agent|134| -|2010/09/13|mc-inventory now use SimpleRPC and the rpcutil agent and display server stats|133| -|2010/09/13|Make the path to the rpc-help.erb configurable and set sane default|130| -|2010/09/13|Make the configfile used available in the Config class and add to rpcutil|132| -|2010/09/12|Rework internal statistics and add a rpcutil agent|129| -|2010/09/12|Fix internal memory structures related to agent meta data|128| -|2010/08/24|Update the OpenBSD port for changes in 0.4.8 tarball|125| -|2010/08/23|Fix indention/block error in M:R:Stats|124| -|2010/08/23|Fix permissions in the RPM for files in /etc|123| -|2010/08/23|Fix language in two error messages|122| - -## 0.4.8 - 2010/08/20 - -### Release Focus and Notes - -This is a bug fix and minor improvement release. - -### Bug Fixes - - * The RPM packages now require redhat-lsb since our RC scripts need it - * The rake tasks do not attempt to build rpms on all platforms - * Some plugin missing related exceptions are now handled gracefully - * The Rakefile had a few warnings cleaned up - -### Notable New Features - - * Users can now have a _~/.mcollective_ file which will be preferred over over _/etc/mcollective/client.cfg_ if it exists. You can still use _--config_ to override. - - * The SSL Security plugin can now use "either YAML or Marshal for serialization":/reference/plugins/security_ssl.html#serialization_method, this means other programming languages can be used as clients. A sample Perl client is included in the ext directory. Marshal remains the default for backwards compatibility - - * _mc-inventory_ can now be used to create "custom reports using a small reporting DSL":/reference/ui/nodereports.html, this enable you to build custom reports listing all your machines and gives you access to facts, agents and classes lists. - - * The log level for the _mcollectived_ can be adjusted during run time using the _USR2_ unix process signal. - -### Backwards Compatibility - -This release will be backward compatible with older releases. If you choose to use YAML in the SSL plugin you need matching versions on the client. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2010/08/19|Fix missing help template in debian packages|90| -|2010/08/18|Clean up some hardlink warnings in the Rakefile|117| -|2010/08/18|Include the website in the main repo and add a simple Rake task|118| -|2010/08/17|Handle exceptions for missing plugins better|115| -|2010/08/17|Add support for ~/.mcollective as a config file|114| -|2010/08/07|SSL security plugin can use either YAML or Marshal|94| -|2010/08/06|Multiple YAML files can now be used as fact source|112| -|2010/08/06|Allow log level to be adjusted at run time with USR2|113| -|2010/07/31|Add basic report scripting support to mc-inventory|111| -|2010/07/06|Removed 'rpm' from the default rake task|109| -|2010/07/06|Add redhat-lsb to the server RPM dependencies|108| - -## 0.4.7 - 2010/06/29 - -### Release Focus and Notes - -This is a bug fix and incremental improvement release focusing on small improvements in the DDL mostly. - -### Data Definition Language - -We've extended the use of the DDL in the RPC client. We've integrated the DDL into _printrpc_ helper. The output is dynamic showing field names in human readable format rather than hash dumps. - -We're also using color to improve the display of the results, the color display can be disabled with the new _color_ configuration option. - -A "screencast of the DDL integration":https://www.youtube.com/watch?v=xikjjXvN6nA and color usage has been recorded. - -### Bug Fixes - -A serious issue has been fixed with regard to complex agents. If you attempted to use multiple agents from the same script errors such as duplicate discovery results or simply not working. - -The default fact source has been changed to YAML, it was inadvertently set to Facter in the past. - -Some previously unhandled exceptions are now being handled correctly and passed onto the clients as failed requests rather than no responses at all. - -### Backwards Compatibility - -This release will be backward compatible with older releases. The change to YAML fact source by default might impact you if you did not previously specify a fact source in the configuration files. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -| 2010/06/27 | Change default factsource to YAML|106| -| 2010/06/27 | Added VIM snippets to create DDLs and Agents|102| -| 2010/06/26 | DDL based help now works better with Symbols in in/output|105| -| 2010/06/23 | Whitespace at the end of config lines are now stripped|100| -| 2010/06/22 | printrpc will now inject some colors into results|99| -| 2010/06/22 | Recover from syntax and other errors in agents|98| -| 2010/06/17 | The agent a MC::RPC::Client is working on is now available|97| -| 2010/06/17 | Integrate the DDL with data display helpers like printrpc|92| -| 2010/06/15 | Avoid duplicate topic subscribes in complex clients|95| -| 2010/06/15 | Catch some unhandled exceptions in RPC Agents|96| -| 2010/06/15 | Fix missing help template file from RPM|90| - -## 0.4.6 - 2010/06/14 - -### Release Focus and Notes - -This release is a major feature release. - -We're focusing mainly on the Stomp connector and on the SimpleRPC agents in this release though a few smaller additions were made. - -### Stomp Connector - -We've historically been stuck just using RubyGem Stomp 1.1 due to multi threading bugs in the newer releases. All attempts to contact the authors failed. Recently though I had some luck and these issues are resolved in the RubyGem Stomp 1.1.6 release. - -This means we can take advantage of a lot of new features such as connection pooling for failover/ha and also SSL TLS between nodes and ActiveMQ server. - -See "Stomp Connector":/reference/plugins/connector_stomp.html for details. - -### RPC Agent Data Description Language - -I've been working since around February on building introspection, automatically generated documentation and the ability for user interfaces to be auto generated for agents, even ones you write your self. - -This feature is documented in "DDL":/simplerpc/ddl.html but a quick example of a DDL document might help make it clear: - -### CLI Utilities changes - - * _mc-facts_ now take all the standard filters so you can make reports for just subsets of machines - * A new utility _mc-inventory_ has been added, it will show agents, facts and classes for a node - * _mc-rpc_ has a new option _--agent-help_ that will use the DDL and display auto generated documentation for an agent. - * _mc-facts_ output is sorted for better readability - -### Backwards Compatibility - -This release will be backward compatible with older releases, the new Stomp features though require the newer Ruby Gem. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -| 2010/06/12 | Qualify the Process class to avoid clashes in the discovery agent|88| -| 2010/06/12 | Add mc-inventory which shows agents, classes and facts for a node|87| -| 2010/06/11 | mc-facts now supports standard filters|86| -| 2010/06/11 | Add connection pool retry options and SSL for connection|85| -| 2010/06/11 | Add support for specifying multiple stomp hosts for failover|84| -| 2010/06/10 | Tighten up handling of filters to avoid nil's getting into them|83| -| 2010/06/09 | Sort the mc-facts output to be more readable|82| -| 2010/06/08 | Fix deprecation warnings in newer Stomp gems|81| - -## 0.4.5 - 2010/06/03 - -### Release Focus and Notes - -This release is a major feature release. - -The focus of this release is to finish up some of the more enterprise like features, we now have fine grained Authorization and Authentication and a new security model that uses SSL keys. - -### Security Plugin - -Vladimir Vuksan contributed the base code of a new "SSL based security plugin":/reference/plugins/security_ssl.html . This plugin builds on the old PSK plugin but gives each client a unique certificate pair. The nodes all share a certificate and only store client public keys. This means that should one node be compromised it cannot be used to control the rest of the network. - -### Authorization Plugin - -We've developed new hooks and plugins for SimpleRPC that enable you to write "fine grained authorization systems":/simplerpc/authorization.html . You can do authorization on every aspect of the request and you'll have access to the caller userid as provided by the security plugin. Combined with the above SSL plugin this can be used to build powerful and secure Authentication and Authorization systems. - -A sample plugin can be found "here":http://code.google.com/p/mcollective-plugins/wiki/ActionPolicy - -### Enhancements for Web Development - -Web apps doesn't tie in well with our discover, request, wait model. We've made it easier for web apps to maintain their own cached discovery data using the "Registration:/reference/plugins/registration.html system and then based on that do requests that would not require any discovery. - -### Fire and Forget requests - -It is often desirable to just send a request and not wait for any reply. We've made it easy to do requests like this] with the addition of a new request parameter on the SimpleRPC client class. - -Requests like this will not take any time to do discovery and you will not be able to get results back from the agents. - -### Reloading Agents - -To make it a bit easier to manage daemons and agents you can now send the _mcollectived_ a _USR1_ signal and it will re-read all it's agents from disk. - -### Backwards Compatibility - -This release when used with the old style PSK plugin should be perfectly backward compatible with your existing agents. To use some of the newer features like authorization will require config and/or agent changes. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -| 2010/06/01 | Improve the main discovery agent by adding facts and classes to its inventory action|79| -| 2010/05/30 | Add various helpers to get reports as text instead of printing them|43| -| 2010/05/30 | Add a custom_request method to call SimpleRPC agents with your own discovery|75| -| 2010/05/30 | Refactor RPC::Client to be more generic and easier to maintain|75| -| 2010/05/29 | Fix a small scoping issue in Security::Base|76| -| 2010/05/25 | Add option --no-progress to disable progress bar for SimpleRPC|74| -| 2010/05/23 | Add some missing dependencies to the RPMs|72| -| 2010/05/22 | Add an option _:process_results_ to the client|71| -| 2010/05/13 | Fix help output that still shows old branding|70| -| 2010/04/27 | The supplied generic stompclient now accepts STOMP_PORT in the environment|68| -| 2010/04/26 | Add a SimpleRPC Client helper to reset filters|64| -| 2010/04/26 | Listen for signal USR1 and reload all agents from disk|65| -| 2010/04/12 | Add SimpleRPC Authorization support|63| - - -## 0.4.4 - 2010/04/03 - -### Release Focus and Notes - -This release is mostly a bug fix release. - -We've cleaned up the logs a bit so you'll see fewer exceptions logged, also if you have Rails + Puppet on a node the logs will stay in the standard format. We are handling some exceptions better which has improved stability of the registration system. - -If you do some slow queries in your discovery agent you can bump the timeout up in the config using _plugin.discovery.timeout_. - -All the scripts now use _/usr/bin/env ruby_ rather than hardcoded paths to deal better with Ruby's in weird places. - -Several other small annoyances was fixed or improved. - -### mc-controller - -We've always had a tool that let you control a network of mcollective instances remotely, it lagged behind a bit with the core, we've fixed it up and documented it "here":/reference/basic/daemon.html . You can use it to reload agents from disk without restarting the daemon for example or get stats or shut down the entire mcollective network. - -### Backwards Compatibility - -No changes that impacts backward compatibility has been made. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -| 2010/03/27 | Make it easier to construct SimpleRPC requests to use with the standard client library|60| -| 2010/03/27 | Manipulating the filters via the helper methods will force rediscovery|59| -| 2010/03/23 | Prevent Activesupport when brought in by Facter from breaking our logs|57| -| 2010/03/23 | Clean up logging for messages not targeted at us|56| -| 2010/03/19 | Add exception handling to the registration base class|55| -| 2010/03/03 | Use /usr/bin/env ruby instead of hardcoded paths|54| -| 2010/02/17 | Improve mc-controller and document it|46| -| 2010/02/08 | Remove some close coupling with Stomp to easy creating of other connectors|49| -| 2010/02/01 | Made the discovery agent timeout configurable using plugin.discovery.timeout|48| -| 2010/01/25 | mc-controller now correctly loads/reloads agents.|45| -| 2010/01/25 | Building packages has been improved to ensure rdocs are always included|44| - - -## 0.4.3 - 2010/01/24 - -### Release Focus and Notes - -This release fixes a few bugs and introduce a major new SimpleRPC feature for auditing requests. - -### Auditing - -We've created an "auditing framework for SimpleRPC":/simplerpc/auditing.html, each request gets passed to an audit plugin for processing. We ship one that simply logs to a file on each node and there's a "community plugin":http://code.google.com/p/mcollective-plugins/wiki/AuditCentralRPCLog that logs everything on a central logging host. - -In future we might add auditing to the client libraries so requests will be logged where they are sent as well as auditing of replies being sent, this will be driven by requests from the community though. - -### New _fail!_ method for SimpleRPC - -Till now while writing agents you can use the _fail_ method to set statuses in the reply, this however did not also raise exceptions and terminate execution of the agent immediately. - -Often the existing behavior is required but it did lead to some awkward code when you did want to just exit the agent immediately as well as set a fail status. We've added a _fail!_ method that works just like _fail_ except it stops execution of your agent immediately. - -### Backwards Compatibility - -No changes that impacts backward compatibility has been made. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -| 2010/01/23 | Handle ctrl-c during discovery without showing exceptions to users|34| -| 2010/01/21 | Force all facts in the YAML fact source to be strings|41| -| 2010/01/19 | Add SimpleRPCAuditing audit logging to SimpleRPC clients and Agents| | -| 2010/01/18 | The SRPM we provide will now build outside of the Rake environment|40| -| 2010/01/18 | Add a _fail!_ method to RPC::Agent|37| -| 2010/01/18 | mc-rpc can now be used without supplying arguments|38| -| 2010/01/18 | Don't raise an error if no user/pass is given to the stomp connector, try unauthenticated mode|35| -| 2010/01/17 | Improve error message when Regex validation failed on SimpleRPC input|36| - - -## 0.4.2 - 2010/01/14 - -### Release Focus and Notes - -This release fixes a few bugs, add some command line improvements and brings major changes to the Debian packaging. - -### Packaging - -Firstly we've had some amazing work done by Riccardo Setti to make us Debian packages that complies with Debian and Ubuntu policy, this release use these new packages. It has some unfortunate changes to file layout detailed below but overall I think it's a big win to get us in line with Distribution policies and standards. - -The only major change is that in the past we used _/usr/libexec/mcollective_ as the libdir, but Debian does not have this directory and it is not in the LFHS anymore so we now use _/usr/share/mcollective/plugins_ as the lib dir. You need to move your plugins there and update both client and server configs. - -The RedHat packages will move to this convention too in the next release since I think it's the better location and complies with LFHS. - -### Command Line Improvements - -We've streamlined the command line a bit, nothings changed we've just added some flags. - -The _--with-class_, _--with-fact_, _--with-agent_ and _--with-identity_ now all have a short form _-C_, _-F_, _-A_ and _-I_ respectively. - -We've added a new filter option _--with_ and a short form _-W_ that combines _--with-class_ and _--with-fact_ into one filter type, use case would be: - -{% highlight console %} - % mc-find-hosts -W "/centreon/ country=de roles::dev_server/" -{% endhighlight %} - -This would find hosts with class regex matched _/centreon/_, class _roles::dev_server_ and fact matching _country=de_. Hopefully this saves on some typing. - -You can also now set the environment variables _MCOLLECTIVE_TIMEOUT_ and _MCOLLECTIVE_DTIMEOUT_ which saves you from typing _--timeout_ and _--discovery-timeout_ often, especially useful on very fast networks. - -### Other fixes and improvements - - * We've added the COPYING file to all the packages - * We've made the init script more LSB compliant - * A bug related to discovery in SimpleRPC was fixed - -### Backwards Compatibility - -The only backwards issue is the Debian packages. They've been tested to upgrade cleanly but you need to change the config as above. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -| 2010/01/13 | New packaging for Debian provided by Riccardo Setti|29| -| 2010/01/07 | Improved LSB compliance of the init script - thanks Riccardo Setti|32| -| 2010/01/07 | Multiple calls to SimpleRPC client would reset discovered hosts|31| -| 2010/01/04 | Timeouts can now be changed with MCOLLECTIVE_TIMEOUT and MCOLLECTIVE_DTIMEOUT environment vars|25| -| 2010/01/04 | Specify class and fact filters easier with the new -W or --with option|27 | -| 2010/01/04 | Added COPYING file to RPMs and tarball|28| -| 2010/01/04 | Make shorter filter options -C, -I, -A and -F|26| - -## 0.4.1 - 2010/01/02 - -### Release Focus and Notes - -This is a bug fix release to address some shortcomings and issues found in Simple RPC. - -The main issue is around handling of meta data in agents, the documented approach did not work, we've now solved this by adding a number of hooks into the processing of Simple RPC agents. - -We've also made logging and config retrieval a bit easier in agents - and documented this. - -You can now call the _mc-rpc_ command a bit easier: - -{% highlight console %} - % mc-rpc --agent helloworld --action echo --argument msg="hello world" - % mc-rpc helloworld echo msg="hello world" -{% endhighlight %} - -The 2 calls are the same, you can pass as many arguments in _key=val_ pairs as needed at the end. - -### Backwards Compatibility - -No issues with backward compatibility, should be a simple upgrade. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -| 2010/01/02 | Added hooks to plug into the processing of requests, also enabled setting meta data and timeouts|14| -| 2010/01/02 | Created readers for @config and @logger in the SimpleRPC agent|23| -| 2009/12/30 | Don't send out any requests if no nodes were discovered|17| -| 2009/12/30 | Added :discovered and :discovered_nodes to client stats|20| -| 2009/12/30 | Add a empty_filter? helper to the RPC mixin|18| -| 2009/12/30 | Fix formatting bug with progress bar|21| -| 2009/12/29 | Simplify mc-rpc command line|16| -| 2009/12/29 | Fix layout issue when printing hosts that did not respond|15| - - -## 0.4.0 - 2009/12/29 - -### Release Focus and Notes - -This release introduced a major new feature - Simple RPC - a framework for easily building clients and agents. More than that it's a set of conventions and standards that will help us build generic clients like web based ones capable of talking to all agents. - -We think this feature is ready for wide use, it's well documented and we've done extensive testing. We'll be porting some of our own code over to it once this release is out and we do anticipate there might be some _0.4.x_ releases to round off a few issues that might remain. We do not currently have any open tickets against Simple RPC. - -We've also added the ability to create more complex queries such as: - -{% highlight console %} ---with-class /dev_server/ --with-class /rails/ -{% endhighlight %} - -This does an _AND_ operation on the puppet classes on the node and finds only nodes with both _/dev_server/_ *AND* _/rails/_ classes. This new functionality applies to all types of filter. - -We've made the _--with-class_ filters more generic in comments, documentation etc with an eye to be more usable in Chef and other Configuration Management environments. - -### Backwards Compatibility - -Unfortunately introducing the new filtering methods has some backward compatibility issues, if you had clients/agents with code like: - -{% highlight ruby %} - options[:filter]["agent"] = "some agent" -{% endhighlight %} - -You should now change that to: - -{% highlight ruby %} - options[:filter]["agent"] << "some agent" -{% endhighlight %} - -As each filter is an array now. If you do not change the code it will still work as before but you will not be able to use the compound filtering feature on filter types that you've forced to be a string and there might be some other undesired side effects. We've tried though to at least not break old code, they just can't use the new features. - -You were also able to test easily in the past if you're running unfiltered using -something like: - -{% highlight ruby %} - if options[:filter] == {} -{% endhighlight %} - -Now that's much harder and we've added a helper to make this easier: - -{% highlight ruby %} - if MCollective::Util.empty_filter?(options[:filter]) -{% endhighlight %} - -This new method is compatible with both the old and new filter method so you can start using it before you finish the first issue mentioned here. - -We've also made the class filter more generic, in the past you did class filters like this: - -{% highlight ruby %} - options[:filter]["puppet_class"] << /apache/ -{% endhighlight %} - -Now you have to adjust it to: - -{% highlight ruby %} - options[:filter]["cf_class"] << /apache/ -{% endhighlight %} - -Old code will keep working but you should change to this name for filters to be consistent with the rest of the code base. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -| 2009/12/28 | Add support for other configuration management systems in the --with-class filters|13| -| 2009/12/28 | Add a `Util.empty_filter?` to test for an empty filter| | -| 2009/12/27 | Create a new client framework SimpleRPCIntroduction|6| -| 2009/12/27 | Add support for multiple filters of the same type|3| - -## 0.3.0 - 2009/12/17 - -### Release Focus and Notes - -Primarily a bug fix release. Only new feature is to allow the user to create `MCollective::Util::*` classes and put those in the plugins directory. This is useful for more complex agents and clients. - -### Backwards Compatibility - -This release should not break any older code, if it does it's a bug. - -### Changes - -|Date|Description|Ticket| -|----|-----------|------| -|2009/12/16|Improvements for newer versions of Ruby where TERM signal was not handled|7| -|2009/12/07|MCollective::Util is now a module and plugins can drop in util classes in the plugin dir| | -|2009/12/07|The Rakefile now works with rake provided on Debian 4 systems|2| -|2009/12/07|Improvements in the RC script for Debian and older Ubuntu systems|5| - -## 0.2.0 - 2009/12/02 - -### Release Focus and Notes - -First numbered release - -### Backwards Compatibility - -n/a - -### Changes - -n/a diff --git a/website/screencasts.md b/website/screencasts.md deleted file mode 100644 index 9619c1b0..00000000 --- a/website/screencasts.md +++ /dev/null @@ -1,108 +0,0 @@ ---- -layout: default -title: Screencasts -toc: false ---- -[YouTube]: https://www.youtube.com/playlist?list=PL74D254ABDFD6CA47&feature=view_all -[slideshare]: http://www.slideshare.net/mcollective -[Terminology]: /mcollective/terminology.html -[SimpleRPCIntroduction]: /mcollective/simplerpc/ -[DDL]: /mcollective/reference/plugins/ddl.html - -We believe screen casts give the best introduction to new concepts, so we've recorded -several to complement the documentation. - -There's a [YouTube][] playlist that has all the videos, you can subscribe and follow there. -There is also a [slideshare][] site where presentations will go that we do at conferences and events. - -## Introductions and Guides -
    -
  1. Introducing MCollective
  2. -
  3. EC2 Hosted Demo
  4. -
  5. Message Flow, Terminology and Components
  6. -
  7. Writing Agents
  8. -
  9. Detailed information about the DDL
  10. -
- -## Tools built using MCollective -
    -
  1. SimpleRPC DDL IRB
  2. -
  3. Software Deployer used by developers
  4. -
  5. Managing clusters of Exim Servers
  6. -
  7. Bootstrapping Puppet on EC2
  8. -
- -  - -### Introduction -[This video](http://youtu.be/0i7VpvC2vMM) introduces the basic concepts behind MCollective. It predates the -SimpleRPC framework but is still valid today. - - - -  - -### Message Flow, Terminology and Components -This video introduces the messaging concepts you need to know about when using MCollective. -It shows how the components talk with each other and what software needs to be installed where -on your network. Viewing this prior to starting your deployment is recommended. - -We also have a page detailing the [Terminology][] - - - -View more webinars from Marionette Collective. -  - -### How to write an Agent, DDL and Client -Writing agents are easy, we have good documentation that can be used as a reference, [this -video](http://youtu.be/2Xhq_UqnqRE) should show you how to tie it all together though. -See the [SimpleRPC Introduction][SimpleRPCIntroduction] for reference wiki pages after viewing this video. - - - -  - -### The SimpleRPC DDL -The Data Definition Language helps your clients produce more user friendly output, it ensures -input gets validated while showing online help, and it enables dynamic generation of user interfaces. -After you have watched the video, you can refer to the [DDL][] wiki page for more information. - - - -  - -### SimpleRPC DDL enhanced IRB -Based on the SimpleRPC DDL, this custom IRB shell supports command completion. - - - - - -### Software Deployer using MCollective -If you ever do commissioned work based on MCollective, this deployer written using SimpleRPC may be of use. -It can be used by developers to deploy applications live into production using a defined -API and process. - - - -  - -### Managing Exim Clusters -A command line and dialog-based UI written to manage clusters of Exim Servers. - -The code for this is [open source](http://github.com/puppetlabs/mcollective-plugins/tree/master/agent/exim/). -Unfortunately it predates SimpleRPC; we hope to port it soon. - - - -  - -### Bootstrapping Puppet on EC2 with MCollective -Modern cloud environments present a lot of challenges to automation. However, with MCollective and -some existing open source agents and plugins you can completely automate the entire process -of provisioning EC2 nodes using Puppet. - -More detail is available [on this blog post](http://www.devco.net/archives/2010/07/14/bootstrapping_puppet_on_ec2_with_mcollective.php) - - diff --git a/website/security.md b/website/security.md deleted file mode 100644 index d19454a9..00000000 --- a/website/security.md +++ /dev/null @@ -1,192 +0,0 @@ ---- -layout: default -title: Security Overview -toc: false ---- -[broadcast paradigm]: /mcollective/reference/basic/messageflow.html -[SimpleRPC]: /mcollective/simplerpc/ -[Authorization]: /mcollective/simplerpc/authorization.html -[Auditing]: /mcollective/simplerpc/auditing.html -[SSL security plugin]: /mcollective/reference/plugins/security_ssl.html -[AES security plugin]: /mcollective/reference/plugins/security_aes.html -[ActiveMQ Security]: /mcollective/reference/integration/activemq_security.html -[ActiveMQ TLS]: http://activemq.apache.org/how-do-i-use-ssl.html -[ActiveMQ SSL]: /mcollective/reference/integration/activemq_ssl.html -[ActiveMQ STOMP]: http://activemq.apache.org/stomp.html -[MCollective STOMP Connector]: /mcollective/reference/plugins/connector_stomp.html -[ActionPolicy]: https://docs.puppetlabs.com/mcollective/plugin_directory/authorization_action_policy.html -[CentralAudit]: https://docs.puppetlabs.com/mcollective/plugin_directory/central_rpc_log.html -[Subcollectives]: reference/basic/subcollectives.html - - -Due to the [broadcast paradigm][] of mcollective security is a complex topic to -discuss. - -This discussion will focus on strong SSL and AES+RSA based security plugins, -these are not the default or only option but is currently the most secure. -Both the [SSL security plugin][] and [AES security plugin][] provide strong caller -identification, this is used by the [SimpleRPC][] framework for [Authorization][] -and [Auditing][]. - -As every organisation has its own needs almost all aspects of the security -system is pluggable. This is an overview of the current state of SSL based -Authentication, Authorization and Auditing. - -
- -The image above is a reference to use in the following pages, it shows a -MCollective Setup and indicates the areas of discussion. - -The focus here is on ActiveMQ, some of the details and capabilities will -differ between middleware systems. - -## Client Connections and Credentials - -Every STOMP connection has a username and password, this is used to gain basic -access to the ActiveMQ system. We have a [ActiveMQ Security][] sample setup -documented. - -ActiveMQ can use LDAP and other security providers, details of this is out of -scope here, you should use their documentation or the recently released book -for details of that. - -### MCollective Protocol Security -The main protocol used by MCollective keeps track of message creation time and -a per message TTL. Messages that are older than the TTL are not accepted in -future we will also do full replay protection. - -Both the AES+RSA and the SSL plugin secures these 2 properties cryptographically -to make sure they are not tampered with. - -### The AES+RSA security plugin -When using the [AES security plugin][] each user also gets a private and public -key, like with SSH you need to ensure that the private keys remain private -and not shared between users. - -This plugin can be configured to distribute public keys at the cost of some -security, you can also manually distribute keys for the most secure setup. - -The public / private key pair is used to encrypt using AES and then to encrypt -the key used during the AES phase using RSA. This provides encrypted payloads -securing the reply strucutres. - -The client embeds a _caller_ structure in each request, if RSA decryption -pass the rest of the MCollective agents, auditing etc can securely know who -initiated a request. - -This caller is used later during Authorization and Auditing. - -The plugin secures the TTL and Message Time properties of the message making sure -someone cannot intercept, tamper and replay these messages. - -This plugin comes with a significant setup, maintenance and performance overhead -if all you need is to securely identify users use the SSL security plugin instead. - -### The SSL security plugin -When using the [SSL security plugin][] each user also gets a private and public -certificate, like with SSH you need to ensure that the private keys remain -private and not be shared between users. The public part needs to be -distributed to all nodes. - -The private key is used to cryptographically sign each request being made by a -client, later the public key will be used to validate the signature for -authenticity. - -The client embeds a _caller_ structure in each request, if SSL signature -validation pass the rest of the MCollective agents, auditing etc can securely -know who initiated a request. - -This caller is used later during Authorization and Auditing. - -The plugin secures the TTL and Message Time properties of the message making sure -someone cannot intercept, tamper and replay these messages. - -## Connection to Middleware - -By default the connections between Middleware and Nodes are not encrypted, just -signed using the SSL keys above. [ActiveMQ supports TLS][ActiveMQ TLS] and the -[stomp connector][ActiveMQ STOMP] supports this including full CA based -certificate verification. - -The [MCollective STOMP Connector][] also supports TLS, to configure MCollective -to speak TLS to your nodes please follow our notes about [ActiveMQ SSL][]. - -Enabling TLS throughout will secure your connections from any kind of sniffing -and Man in The Middle attacks. - -## Middleware Authorization and Authentication - -As mentioned above ActiveMQ has it's own users and every node and client -authenticates using these. - -In addition to this you can on the middleware layer restrict access to topics, -you can for example run a development and production collective on the same -ActiveMQ infrastructure and allow your developers access to just the development -collective using these controls. They are not very fine grained but should be a -import step to configure for any real setup. - -We have a sample [ActiveMQ Security][] setup documented that has this kind of -control. - -By combining this topic level restrictions with [Subcollectives][] you can create -virtually isolated groups of nodes and give certain users access to only those -subcollectives. Effectively partitioning out a subset of machines and giving -secure access to just those. - -## Node connections and credentials - -As with the client the node needs a username and password to connect to the -middleware and can also use TLS. - -It's not a problem if all the nodes share a username and password for the -connection since generally nodes do not make new requests. You can enable -registration features that will see your nodes make connections, you should -restrict this as outlined in the previous section. - -When using the [SSL security plugin][] all the nodes share a same SSL private -and public key, all replies are signed using this key. It would not be -impossible to add a per node certificate setup but I do not think this will -add a significant level of security over what we have today. - -When using the [AES security plugin][] nodes can have their own sets of keys -and registration data can be secured. Replies are encrypted using the clients -key and so only the client who made the request can read the replies. - -## SimpleRPC Authorization - -The RPC framework has a pluggable [Authorization][] system, you can create very -fine grain control over requests, for example using the [ActionPolicy][] setup you -can create a policy like this: - -{% highlight text %} -policy default deny -allow cert=rip * * * -allow cert=john * customer=acme acme::devserver -allow cert=john enable disable status customer=acme * -{% endhighlight %} - -This applied to the service agent will allow different level of access to -actions to different people. The caller id is based directly on the SSL Private -Key in use and subject to validation on every node. - -As with other aspects of mcollective authorization is tied closely with meta -data like facts and classes so you can use these to structure your authorization -as can be seen above. - -You can provide your own authorization layers to fit your ogranizational needs, -they can be specific to an agent or apply to the entire collective. - -## SimpleRPC Auditing - -The RPC layer can keep detailed [Auditing][] records of every request received, -the audit log shows the - SSL signature or RSA verified - caller, what agent, action -and any arguments that was sent for every request. - -The audit layer is a plugin based system, we provide one that logs to a file on -every node and there are [community plugins][CentralAudit] that keeps a centralized -log both in log files and in MongoDB NoSQL database. - -Which to use depends on your usecase, obviously a centralized auditing system -for thousands of nodes is very complex and will require a special plugin to be -developed the community centralized audit log is ok for roughly 100 nodes or -so. diff --git a/website/simplerpc/agents.md b/website/simplerpc/agents.md deleted file mode 100644 index 15da3683..00000000 --- a/website/simplerpc/agents.md +++ /dev/null @@ -1,521 +0,0 @@ ---- -layout: default -title: Writing SimpleRPC Agents ---- -[WritingAgents]: /mcollective/reference/basic/basic_agent_and_client.html -[SimpleRPCClients]: /mcollective/simplerpc/clients.html -[ResultsandExceptions]: /mcollective/simplerpc/clients.html#Results_and_Exceptions -[SimpleRPCAuditing]: /mcollective/simplerpc/auditing.html -[SimpleRPCAuthorization]: /mcollective/simplerpc/authorization.html -[DDL]: /mcollective/reference/plugins/ddl.html -[WritingAgentsScreenCast]: https://www.youtube.com/watch?v=2Xhq_UqnqRE -[RPCUtil]: /mcollective/reference/plugins/rpcutil.html -[ValidatorPlugins]: /mcollective/reference/plugins/validator.html - -Simple RPC works because it makes a lot of assumptions about how you write agents, we'll try to capture those assumptions here and show you how to apply them to our Echo agent. - -We've recorded a [tutorial that will give you a quick look at what is involved in writing agents][WritingAgentsScreenCast]. - -## Conventions regarding Incoming Data - -As you've seen in [SimpleRPCClients][] our clients will send requests like: - -{% highlight ruby %} -mc.echo(:msg => "Welcome to MCollective Simple RPC") -{% endhighlight %} - -A more complex example might be: - -{% highlight ruby %} -exim.setsender(:msgid => "1NOTVx-00028U-7G", :sender => "foo@bar.com") -{% endhighlight %} - -Effectively this creates a hash with the members `:msgid` and -`:sender` and invokes the `setsender` action. - -You cannot use the a data item called `:process_results` as this -has special meaning to the agent and client. This will indicate -to the agent that the client isn't going to be waiting to process -results. You might choose not to send back a reply based on this. - -## Sample Agent -Here's our sample *Echo* agent: - -{% highlight ruby linenos %} -# $libdir/mcollective/agent/echo.ddl -metadata :name => 'echo', - :description => 'Echo service for MCollective', - :author => 'R.I.Pienaar', - :license => 'GPLv2', - :version => '1.1', - :url => 'https://docs.puppetlabs.com/mcollective/simplerpc/agents.html', - :timeout => 60 - -action 'echo', :description => 'Echos back any message it receives' do - display :always - - input :msg, - :prompt => 'Message', - :description => 'Your message', - :type => :string, - :validation => '.*', - :optional => false, - :maxlength => 1024 - - output :msg, - :description => 'Your message', - :display_as => 'Message', - :default => '' -end -{% endhighlight %} - - -{% highlight ruby linenos %} -# $libdir/mcollective/agent/echo.rb -module MCollective - module Agent - class Echo 'Echos back any message it receives' do - display :always - - input :msg, - :prompt => 'Message', - :description => 'Your message', - :type => :string, - :validation => '.*', - :optional => false, - :maxlength => 1024 - - output :msg, - :description => 'Your message', - :display_as => 'Message', - :default => '' -end -{% endhighlight %} - - -### Implementing Actions - -The implementation of an action is added to the agent class: - -{% highlight ruby linenos %} -# $libdir/mcollective/agent/echo.rb -module MCollective - module Agent - class Echo :out, :stderr => :err) -{% endhighlight %} - -Here you will have set `reply[:out]`, `reply[:err]` and `reply[:status]` based -on the output from the command. - -You can append the output of the command to any string: - -{% highlight ruby %} -out = [] -err = "" -status = run("echo 'hello world'", :stdout => out, :stderr => err) -{% endhighlight %} - -Here the STDOUT of the command will be saved in the variable `out` and not sent -back to the caller. The only caveat is that the variables `out` and `err` should -have the `<<` method, so if you supplied an array each line of output will be a -single member of the array. In the example `out` would be an array of lines -while `err` would just be a big multi line string. - -By default any trailing new lines will be included in the output and error: - -{% highlight ruby %} -reply[:status] = run("echo 'hello world'", :stdout => :out, :stderr => :err) -reply[:stdout].chomp! -reply[:stderr].chomp! -{% endhighlight %} - -You can shorten this to: - -{% highlight ruby %} -reply[:status] = run("echo 'hello world'", :stdout => :out, :stderr => :err, :chomp => true) -{% endhighlight %} - -This will remove a trailing new line from the `reply[:out]` and `reply[:err]`. - -If you wanted this command to run from the `/tmp` directory: - -{% highlight ruby %} -reply[:status] = run("echo 'hello world'", :stdout => :out, :stderr => :err, :cwd => "/tmp") -{% endhighlight %} - -Or if you wanted to include a shell Environment variable: - -{% highlight ruby %} -reply[:status] = run("echo 'hello world'", :stdout => :out, :stderr => :err, :environment => {"FOO" => "BAR"}) -{% endhighlight %} - -The status returned will be the exit code from the program you ran, if the program -completely failed to run in the case where the file doesn't exist, resources were -not available etc the exit code will be -1 - -You have to set the cwd and environment through these options, do not simply -call `chdir` or adjust the `ENV` hash in an agent as that will not be safe in -the context of a multi threaded Ruby application. - -### Actions in external scripts - -Actions can also be implemented using other programming languages as -long as they support JSON. - -{% highlight ruby %} -action "test" do - implemented_by "/some/external/script" -end -{% endhighlight %} - -The script `/some/external/script` will be called with 2 arguments: - - * The path to a file with the request in JSON format - * The path to a file where you should write your response as a JSON hash - -You can also access these 2 file paths in the `MCOLLECTIVE_REPLY_FILE` and `MCOLLECTIVE_REQUEST_FILE` environment variables - -Simply write your reply as a JSON hash into the reply file. - -The exit code of your script should correspond to the ones in [ResultsandExceptions][]. Any text in STDERR will be -logged on the server at `error` level and used in the text for the fail text. - -Any text to STDOUT will be logged on the server at level `info`. - -These scripts can be placed in a standard location: - -{% highlight ruby %} -action "test" do - implemented_by "script.py" -end -{% endhighlight %} - -This will search each configured libdir for -`mcollective/agent/$agent_name/script.py` and -`agent/$agent_name/script.py`, and will use the former if found. If you -specified a full path it will not try to find the file in libdirs. - - -## Constructing Replies - -### Reply Data -The reply data is in the *reply* variable and is an instance of *MCollective::RPC::Reply*. - -{% highlight ruby %} -reply[:msg] = request[:msg] -{% endhighlight %} - -### Reply Status -As pointed out in the [ResultsandExceptions][] page results all include status messages and the reply object has a helper to create those. - -{% highlight ruby %} -def rmmsg_action - validate :msg, String - validate :msg, /[a-zA-Z]+-[a-zA-Z]+-[a-zA-Z]+-[a-zA-Z]+/ - reply.fail "No such message #{request[:msg]}", 1 unless have_msg?(request[:msg]) - - # check all the validation passed before doing any work - return unless reply.statuscode == 0 - - # now remove the message from the queue -end - -{% endhighlight %} - -The number in `reply.fail` corresponds to the codes in [ResultsandExceptions][] it would default to `1` so you could just say: - -{% highlight ruby %} -reply.fail "No such message #{request[:msg]}" unless have_msg?(request[:msg]) -{% endhighlight %} - -This is hypothetical action that is supposed to remove a message from some queue, if we do have a String as input that matches our message id's we then check that we do have such a message and if we don't we fail with a helpful message. - -Technically this will just set `statuscode` and `statusmsg` fields in the reply to appropriate values. - -It won't actually raise exceptions or exit your action though you should do that yourself as in the example here. - -There is also a `fail!` instead of just `fail` it does the same basic function but also raises exceptions. This lets you abort processing of the agent immediately without performing your own checks on `statuscode` as above later on. - - -## Sharing code between agents -Sometimes you have code that is needed by multiple agents or shared between the agent and client. MCollective has -name space called `MCollective::Util` for this kind of code and the packagers and so forth supports it. - -Create a class with your shared code given a name like `MCollective::Util::Yourco` and save this file in the libdir in `util/yourco.rb` - -A sample class can be seen here: - -{% highlight ruby %} -module MCollective - module Util - class Yourco - def dosomething - end - end - end -end -{% endhighlight %} - -You can now use it in your agent or clients by first loading it from the MCollective lib directories: - -{% highlight ruby %} -MCollective::Util.loadclass("MCollective::Util::Yourco") - -helpers = MCollective::Util::Yourco.new -helpers.dosomething -{% endhighlight %} - -## Authorization -You can write a fine grained Authorization system to control access to actions and agents, please see [SimpleRPCAuthorization][] for full details. - -## Auditing -The actions that agents perform can be Audited by code you provide, potentially creating a centralized audit log of all actions. See [SimpleRPCAuditing][] for full details. - -## Logging -You can write to the server log file using the normal logger class: - -{% highlight ruby %} -Log.debug("Hello from your agent") -{% endhighlight %} - -You can log at levels `info`, `warn`, `debug`, `fatal` or `error`. - -## Data Caching -As of version 2.2.0 there is a system wide Cache you can use to store data that might be costly to create on each request. - -The Cache is thread safe and can be used even with multiple concurrent requests for the same agent. - -Imagine your agent interacts with a customer database on the node that is slow to read data from but this data does not -change often. Using the cache you can arrange for this be read only every 10 minutes: - -{% highlight ruby %} -action "get_customer_data" do - # Create a new cache called 'customer' with a 600 second TTL, - # noop if it already exist - Cache.setup(:customer, 600) - - begin - customer = Cache.read(:customer, request[:customerid]) - rescue - customer = Cache.write(:customer, request[:customerid], get_customer(request[:customerid]) - end - - # do something with the customer data -end -{% endhighlight %} - -Here we setup a new cache table called `:customer` if it does not already exist, the cache has a 10 minute validity. -We then try to read a cached customer record for `request[:customerid]` and if it's not been put in the cache -before or if it expired I create a new customer record using a method called `get_customer` and then save it -into the cache. - -If you have critical code in an agent that is not reentrant you can use the Mutex from the same cache -to synchronize the code: - -{% highlight ruby %} -action "get_customer_data" do - # Create a new cache called 'customer' with a 600 second TTL, - # noop if it already exist - Cache.setup(:customer, 600) - - Cache(:customer).synchronize do - # Update customer record - end -end -{% endhighlight %} - -Here we are using the same Cache that was previously setup and just gaining access to the Mutex protecting the -cache data. The code inside the synchronize block will only be run once so you won't get competing updates to -your customer data. - -If the lock is held too long by anyone the mcollectived will kill the threads in line with the Agent timeout. - -## Processing Hooks -We provide a few hooks into the processing of a message, you've already used this earlier to set metadata. - -You'd use these hooks to add some functionality into the processing chain of agents, maybe you want to add extra logging for audit purposes of the raw incoming message and replies, these hooks will let you do that. - -Hook Function Name | Description -------------------------------------------|------------------------------------------------------------------------------------------------------ -`startup_hook` | Called at the end of the initialize method of the `RPC::Agent` base class -`before_processing_hook(msg, connection)` | Before processing of a message starts, pass in the raw message and the `MCollective::Connector` class -`after_processing_hook` | Just before the message is dispatched to the client - -### `startup_hook` -Called at the end of the `RPC::Agent` standard initialize method. Use this to adjust meta parameters, timeouts and any setup you need to do. - -This will not be called right when the daemon starts up, we use lazy loading and initialization so it will only be called the first time a request for this agent arrives. - -### `before_processing_hook` -Called just after a message was received from the middleware before it gets passed to the handlers. `request` and `reply` will already be set, the msg passed is the message as received from the normal MCollective runner and the connection is the actual connector. - -You can in theory send off new messages over the connector maybe for auditing or something, probably limited use case in simple agents. - -### `after_processing_hook` -Called at the end of processing just before the response gets sent to the middleware. - -This gets run outside of the main exception handling block of the agent so you should handle any exceptions you could raise yourself. The reason it is outside of the block is so you'll have access to even status codes set by the exception handlers. If you do raise an exception it will just be passed onto the runner and processing will fail. - -## Agent Configuration - -You can save configuration for your agents in the main server config file: - -{% highlight ini %} -plugin.helloworld.setting = foo -{% endhighlight %} - -In your code you can retrieve the config setting like this: - -{% highlight ruby %} - setting = config.pluginconf.fetch("helloworld.setting", "") -{% endhighlight %} - -This will set the setting to whatever is in the config file or "" if unset. - - -## Agent Activation -In the past you had to copy an agent only to machines that they should be running on as -all agents were activated regardless of dependencies. - -To make deployment simpler agents support the ability to determine if they should run -on a particular platform. By default SimpleRPC agents can be configured to activate -or not with the `activate_agents` and plugin specific `activate_agent` setting: - -{% highlight ini %} -plugin.helloworld.activate_agent = false -{% endhighlight %} - -You can also place the following in `/etc/mcollective/plugin.d/helloworld.cfg`: - -{% highlight ini %} -activate_agent = false -{% endhighlight %} - -This is a simple way to enable or disable an agent on your machine, agents can also -declare their own logic that will get called each time an agent gets loaded from disk. - -{% highlight ruby %} -module MCollective - module Agent - class Echotrue, :command=>"check_mailq"} -{% endhighlight %} - -Other plugins can be found on the community site like [a centralized logging plugin][AuditCentralRPCLog]. diff --git a/website/simplerpc/authorization.md b/website/simplerpc/authorization.md deleted file mode 100644 index e65ac433..00000000 --- a/website/simplerpc/authorization.md +++ /dev/null @@ -1,61 +0,0 @@ ---- -layout: default -title: SimpleRPC Authorization -toc: false ---- -[SimpleRPCIntroduction]: index.html -[SecurityWithActiveMQ]: /mcollective/reference/integration/activemq_security.html -[SimpleRPCAuditing]: /mcollective/simplerpc/auditing.html -[ActionPolicy]: https://docs.puppetlabs.com/mcollective/plugin_directory/authorization_action_policy.html - -As part of the [SimpleRPC][SimpleRPCIntroduction] framework we've added an authorization system that you can use to exert fine grained control over who can call agents and actions. - -Combined with [Connection Security][SecurityWithActiveMQ], [Centralized Auditing][SimpleRPCAuditing] and Crypto signed messages this rounds out a series of extremely important features for large companies that in combination allow for very precise control over your MCollective Cluster. - -The clients will include the `uid` of the process running the client library in the requests and the authorization function will have access to that on the requests. - -There is a sample full featured plugin called [ActionPolicy][] that you can use or get some inspiration from. - -## Writing Authorization Plugins - -Writing an Authorization plugin is pretty simple, the below example will only allow RPC calls from Unix UID 500. - -{% highlight ruby linenos %} -module MCollective::Util - class AuthorizeIt - def self.authorize(request) - if request.caller != "uid=500" - raise("Not authorized") - end - end - end -end -{% endhighlight %} - -Any exception thrown by your class will just result in the message not being processed or audited. - -You'd install this in your libdir where you should already have a Util directory for these kinds of classes. - -To use your authorization plugin in an agent simply do something like this: - -{% highlight ruby linenos %} -module MCollective::Agent - class Service "Welcome to MCollective Simple RPC") - -printrpcstats - -mc.disconnect -{% endhighlight %} - -Save this into `hello.rb` and run it with `--help`, you should see the standard basic help including filters for discovery. - -If you've set up the Agent and run the client you should see something along these lines: - -{% highlight ruby %} -$ hello.rb - -Finished processing 44 hosts in 375.57 ms -{% endhighlight %} - -While it ran you would have seen a little progress bar and then just the summary line. The idea is that if you're talking to a 1000 machine there's no point in seeing a thousand `OK`, you only want to see what failed and this is exactly what happens here, you're only seeing errors. - -If you run it with `--verbose` you'll see a line of text for every host and also a larger summary of results. - -I'll explain each major line in the code below then add some more features from there: - -{% highlight ruby %} -include MCollective::RPC - -mc = rpcclient("helloworld") -{% endhighlight %} - -The first line pulls in the various helper functions that we provide, this is the Mixin we mentioned earlier. - -We then create a new client to the agent "helloworld" that you access through the `mc` variable. - -{% highlight ruby %} -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC") - -printrpcstats -{% endhighlight %} - -To call a specific action you simply have to do `mc.echo` this calls the `echo` action, we pass a `:msg` parameter into it with the string we want echo'd back. The parameters will differ from action to action. It returns a simple array of the results that you can print any way you want, we'll show that later. - -`printrpc` and `printrpcstats` are functions used to print the results and stats respectively. - -{% highlight ruby %} -mc.disconnect -{% endhighlight %} - -This cleanly disconnects the client from the middleware, some middleware tools like ActiveMQ will log confusing exceptions if you do not do this. It's good form to always disconnect but isn't strictly required. - -## Adjusting the output - -### Verbosely displaying results -As you see there's no indication that discovery is happening and as pointed out we do not display results that are ok, you can force verbosity as below on individual requests: - -{% highlight ruby %} -mc = rpcclient("helloworld") - -mc.discover :verbose => true - -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC"), :verbose => true -{% endhighlight %} - -Here we've added a `:verbose` flag and we've specifically called the discover method. Usually you don't need to call discover it will do it on demand. Doing it this way you'll always see the line: - -{% highlight console %} -Determining the amount of hosts matching filter for 2 seconds .... 44 -{% endhighlight %} - -Passing verbose to `printrpc` forces it to print all the results, failures or not. - -If you just wanted to force verbose on for all client interactions, do: - -{% highlight ruby %} -mc = rpcclient("helloworld") -mc.verbose = true - -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC") -{% endhighlight %} - -In this case everything will be verbose, regardless of command line options. - -### Disabling the progress indicator -You can disable the twirling progress indicator easily: - -{% highlight ruby %} -mc = rpcclient("helloworld") -mc.progress = false -{% endhighlight %} - -Now whenever you call an action you will not see the progress indicator. - -### Saving the reports in variables without printing -You can retrieve the stats from the clients and also get text of reports without printing them: - -{% highlight ruby %} -stats = mc.echo(:msg => "Welcome to MCollective Simple RPC").stats - -report = stats.report -{% endhighlight %} - -`report` will now have the text that would have been displayed by 'printrpcstats' you can also use `no_response_report` to get report text for just the list of hosts that didnt respond. - -If you didn't want to just print the results out to STDOUT you can also get them back as just text: - -{% highlight ruby %} -report = rpcresults mc.echo(:msg => "Welcome to MCollective Simple RPC") -{% endhighlight %} - - -## Applying filters programatically -You can pass filters on the command line using the normal `--with-*` options but you can also do it programatically. Here's a new version of the client that only calls machines with the configuration management class `/dev_server/` and the fact `country=uk` - -{% highlight ruby %} -mc = rpcclient("helloworld") - -mc.class_filter /dev_server/ -mc.fact_filter "country", "uk" - -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC") -{% endhighlight %} - -You can set other filters like `agent_filter`, `identity_filter` and `compound_filter`. - -The `fact_filter` method supports a few other forms in adition to above: - -{% highlight ruby %} -mc.fact_filter "country=uk" -mc.fact_filter "physicalprocessorcount", "4", ">=" -{% endhighlight %} - -This will limit it to all machines in the UK with more than 3 processors. - -## Resetting filters to empty -If while using the client you wish to reset the filters to an empty set of filters - containing only the agent name that you're busy addressing you can do it as follows: - -{% highlight ruby %} -mc = rpcclient("helloworld") - -mc.class_filter /dev_server/ - -mc.reset_filter -{% endhighlight %} - -After this code snippet the filter will only have an agent filter of `helloworld` set. - -## Processing Agents in Batches -By default the client will communicate with all machines at the same time. -This might not be desired as you might affect a DOS on related components. - -You can instruct the client to communicate with remote agents in batches -and sleep between each batch. - -Any client application has this capability using the `--batch` and `--batch-sleep-time` -command line options. - -You can also enable this programatically either per client or per request: - -{% highlight ruby %} -mc = rpcclient("helloworld") -mc.batch_size = 10 -mc.batch_sleep_time = 5 - -mc.echo(:msg => "hello world") -{% endhighlight %} - -{% highlight ruby %} -mc = rpcclient("helloworld") - -mc.echo(:msg => "hello world", :batch_size => 10, :batch_sleep_time => 5) -{% endhighlight %} - -By default batching is disabled and sleep time is 1 - -Setting the `batch_size` to 0 will disable batch mode in both examples above, -effectively overriding what was supplied on the command line. - -## Forcing Rediscovery -By default it will only do discovery once per script and then re-use the results, you can though force rediscovery if you had to adjust filters mid run for example. - -{% highlight ruby %} -mc = rpcclient("helloworld") - -mc.class_filter /dev_server/ -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC") - -mc.reset - -mc.fact_filter "country", "uk" -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC") -{% endhighlight %} - -Here we make one `echo` call - which would do a discovery - we then reset the client, adjust filters and call it again. The 2nd call would do a new discovery and have new client lists etc. - -## Supplying your own discovery information - -A new core messaging mode has been introduced that enables direct non filtered communicatin to specific nodes. This has enabled us to provide an discovery-optional -mode but only if the collective is configured to support direct messaging. - -{%highlight ruby %} -mc = rpcclient("helloworld") - -mc.discover(:nodes => ["host1", "host2", "host3"] - -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC") -{% endhighlight %} - -This will immediately, without doing discovery, communicate just with these 3 hosts. It will do normal failure reporting as with normal discovery based -requests but will just be much faster as the 2 second discovery overhead is avoided. - -The goal with this feature is for cases such as deployment tools where you have a known expectation of which machines to deploy to and you always want -to know if that fails. In that use case a discovery based approach is not 100% suitable as you won't know about down machines. This way you can provide -your own source of truth. - -When using the direct mode messages have a TTL associated with them that defaults to 60 seconds. Since 1.3.2 you can set the TTL globally in the configuration -file but you can also set it on the client: - -{%highlight ruby %} -mc = rpcclient("helloworld") -mc.ttl = 3600 - -mc.discover(:nodes => ["host1", "host2", "host3"] - -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC") -{% endhighlight %} - -With the TTL set to 3600 if any of the hosts are down at the time of the request the request will wait on the middleware and should they come back up -before 3600 has passed since request time they will then perform the requested action. - -## Only sending requests to a subset of discovered nodes -By default all nodes that get discovered will get the request. This isn't always desirable maybe you want to deploy only to a random subset of hosts or maybe you have a service exposed over MCollective that you want to treat as a HA service and so only speak with one host that provides the functionality. - -You can limit the hosts to talk to either using a number or a percentage, the code below shows both: - -{%highlight ruby %} -mc = rpcclient("helloworld") - -mc.limit_targets = "10%" -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC") -{% endhighlight %} - -This will pick 10% of the discovered hosts - or 1 if 10% is less than 1 - and only target those nodes with your request. You can also set it to an integer. - -There are two possible modes for choosing the targets. You can configure a global default method but also set it on your client: - -{%highlight ruby %} -mc = rpcclient("helloworld") - -mc.limit_targets = "10%" -mc.limit_method = :random -printrpc mc.echo(:msg => "Welcome to MCollective Simple RPC") -{% endhighlight %} - -The above code will force a `:random` selection, you can also set it to `:first` - -## Gaining access to the full MCollective::Client -If you wanted to work with the Client directly as in [WritingAgents][] after perhaps setting up some queries or gathering data first you can gain access to the client, you might also need access to the options array that was parsed out from the command line and any subsequent filters that you added. - -{% highlight ruby %} -mc = rpcclient("helloworld") - -client = mc.client -options = mc.options -{% endhighlight %} - -The first call will set up the CLI option parsing, create clients etc, you can then just grab the client and options and go on as per [WritingAgents][]. This is a much quicker way to write full power clients, by just by short-circuiting the options parsing etc. - -## Dealing with the results directly -The biggest reason that you'd write custom clients is probably if you wanted to do custom processing of the results, there are 2 options to do it. - - - -### Results and Exceptions -Results have a set structure and depending on how you access the results you will either get Exceptions or result codes. - -|Status Code|Description|Exception Class| -|-----------|-----------|---------------| -|0|OK| | -|1|OK, failed. All the data parsed ok, we have a action matching the request but the requested action could not be completed.|RPCAborted| -|2|Unknown action|UnknownRPCAction| -|3|Missing data|MissingRPCData| -|4|Invalid data|InvalidRPCData| -|5|Other error|UnknownRPCError| - -Just note these now, I'll reference them later down. - -### Simple RPC style results -Simple RPC provides a trimmed down version of results from the basic Client library. You'd choose to use this if you just want to do simple things or maybe you're just learning Ruby. You'll get to process the results _after_ the call is either done or timed out completely. - -This is an important difference between the two approaches, in one you can parse the results as it comes in, in the other you will only get results after processing is done. This would be the main driving facter for choosing one over the other. - -Here's an example that will print out results in a custom way. - -{% highlight ruby %} -mc.echo(:msg => "hello world").each do |resp| - printf("%-40s: %s\n", resp[:sender], resp[:data][:msg]) -end -{% endhighlight %} - -This will produce a result something like this: - -{% highlight console %} -dev1.you.net : hello world -dev2.you.net : hello world -dev3.you.net : hello world -{% endhighlight %} - -The `each` in the above code just loops through the array of results. Results are an array of Hashes, the data you got for above has the following structure: - -{% highlight console %} -[{:statusmsg=>"OK", - :sender=>"dev1.your.net", - :data=>{:msg => "hello world"}, - :statuscode=>0}, -{:statusmsg=>"OK", - :sender=>"dev2.your.net", - :data=>{:msg => "hello world"}, - :statuscode=>0}] -{% endhighlight %} - -The `:statuscode` matches the table above so you can make decisions based on each result's status. - -### Gaining access to MCollective::Client#req results -You can get access to each result in real time, in this case you will supply a block that will be invoked for each result as it comes in. The result set will be exactly as from the full blown client. - -In this mode there will be no progress indicator, you'll deal with results as and when they come in not after the fact as in the previous example. - -{% highlight ruby %} -mc.echo(:msg => "hello world") do |resp| - if resp[:body][:statuscode] == 0 - printf("%-40s: %s\n", resp[:senderid], resp[:body][:data]) - else - puts "The RPC agent returned an error: #{resp[:body][:statusmsg]}" - end -end -{% endhighlight %} - -The output will be the same as above - -In this mode the results you get will be like this: - -{% highlight ruby %} -{:msgtarget=>"/topic/mcollective.helloworld.reply", - :senderid=>"dev2.your.net", - :msgtime=>1261696663, - :hash=>"2d37daf690c4bcef5b5380b1e0c55f0c", - :body=>{:statusmsg=>"OK", :statuscode=>0, :data=>{:msg => "hello world"}}, - :requestid=>"2884afb0b52cb38ea4d4a3146d18ef5f", - :senderagent=>"helloworld"} -{% endhighlight %} - -You can additionally gain access to a SimpleRPC style result in addition to the more complex native client results: - -{% highlight ruby %} -mc.echo(:msg => "hello world") do |resp, simpleresp| - if resp[:body][:statuscode] == 0 - printf("%-40s: %s\n", simpleresp[:sender], simpleresp[:data][:msg]) - else - puts "The RPC agent returned an error: #{resp[:body][:statusmsg]}" - end -end -{% endhighlight %} - -You can still use printrpc to print these style of results and gain advantage of the DDL and so forth: - -{% highlight ruby %} -mc.echo(:msg => "hello world") do |resp, simpleresp| - printrpc simpleresp -end -{% endhighlight %} - -This should give you a simpler result set to deal with - -## Adding custom command line options -You can look at the `mco rpc` script for a big sample, here I am just adding a simple `--msg` option to our script so you can customize the message that will be sent and received. - -{% highlight ruby linenos %} -#!/usr/bin/ruby - -require 'mcollective' - -include MCollective::RPC - -options = rpcoptions do |parser, options| - parser.define_head "Generic Echo Client" - parser.banner = "Usage: hello [options] [filters] --msg MSG" - - parser.on('-m', '--msg MSG', 'Message to pass') do |v| - options[:msg] = v - end -end - -unless options.include?(:msg) - puts("You need to specify a message with --msg") - exit! 1 -end - -mc = rpcclient("helloworld", :options => options) - -mc.echo(:msg => options[:msg]).each do |resp| - printf("%-40s: %s\n", resp[:sender], resp[:data][:msg]) -end -{% endhighlight %} - -This version of the code should be run like this: - -{% highlight console %} -% test.rb --msg foo -dev1.you.net : foo -dev2.you.net : foo -dev3.you.net : foo -{% endhighlight %} - -Documentation for the Options Parser can be found [in its code][OptionParser]. - -And finally if you add options as above rather than try to parse it yourself you will get help integration for free: - -{% highlight console %} -% hello.rb --help -Usage: hello [options] [filters] --msg MSG -Generic Echo Client - -m, --msg MSG Message to pass - -Common Options - -c, --config FILE Load configuratuion from file rather than default - --dt SECONDS Timeout for doing discovery - --discovery-timeout - -t, --timeout SECONDS Timeout for calling remote agents - -q, --quiet Do not be verbose - -v, --verbose Be verbose - -h, --help Display this screen - -Host Filters - --wf, --with-fact fact=val Match hosts with a certain fact - --wc, --with-class CLASS Match hosts with a certain configuration management class - --wa, --with-agent AGENT Match hosts with a certain agent - --wi, --with-identity IDENT Match hosts with a certain configured identity -{% endhighlight %} - -## Disabling command line parsing and supplying your options programatically - -Sometimes, perhaps when embedding an MCollective client into another tool like Puppet, you do not want MCollective to do any command line parsing as there might be conflicting command line options etc. - -This can be achieved by supplying an options hash to the SimpleRPC client: - -{% highlight ruby %} -include MCollective::RPC - -options = MCollective::Util.default_options - -client = rpcclient("test", {:options => options}) -{% endhighlight %} - -This will create a RPC client for the agent test without any options parsing at all. - -To set options like discovery timeout and so forth you will need use either the client utilities or manipulate the hash upfront, the client utility methods is the best. The code below sets the discovery timeout in a way that does not require you to know any internal structures or the content of the options hash. - -{% highlight ruby %} -options = MCollective::Util.default_options - -client = rpcclient("test", {:options => options}) -client.discovery_timeout = 4 -{% endhighlight %} - -Using this method of creating custom options hashes mean we can make internal changes to MCollective without affecting your code in the future. - -## Sending SimpleRPC requests without discovery and blocking - -Usually this section will not apply to you. The client libraries support sending a request without waiting for a reply. This could be useful if you want to clean yum caches but don't really care if it actually happens everywhere. - -You will loose these abilities: - - * Knowing if your request was received by any agents - * Any stats about processing times etc - * Any information about the success or failure of your request - -The above should make it clear already that this is a limited use case, it's essentially a broadcast request with no feedback loop. - -The code below will send a request to the `runonce` action for an agent `puppetd`, once the request is dispatched I will have no idea if it got handled etc, my code will just continue onwards. - -{% highlight ruby %} -p = rpcclient("puppetd") - -p.identity_filter "your.box.com" -reqid = p.runonce(:forcerun => true, :process_results => false) -{% endhighlight %} - -This will honor any attached filters set either programatically or through the command line, it will send the request but will -just not handle any responses. All it will do is return the request id. - -## Doing your own discovery -For web applications you'd probably use cached copied of Registration data to do discovery rather than wait for MC to do discovery between each request. - -To do this, you'll need to construct a filter and a list of expected hosts and then do a custom call: - -{% highlight ruby %} -puppet = rpcclient("puppetd") - -printrpc puppet.custom_request("runonce", {:forcerun => true}, "your.box.com", {"identity" => "your.box.com"}) -{% endhighlight %} - -This will do a call with exactly the same stats, block and other semantics as a normal call like: - -{% highlight ruby %} -printrpc puppet.runonce(:forcerun => true) -{% endhighlight %} - -But instead of doing any discovery it will use the host list and filter you supplied in the call. - -## The `rpcutil` Helper Agent - -A helper agent called [`rpcutil`][RPCUtil] is included that helps you gather stats, inventory etc about the running daemon. diff --git a/website/simplerpc/index.md b/website/simplerpc/index.md deleted file mode 100644 index 9febae5c..00000000 --- a/website/simplerpc/index.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -layout: default -title: SimpleRPC Introduction -toc: false ---- -[WritingAgents]: /mcollective/reference/basic/basic_agent_and_client.html -[SimpleRPCAgents]: /mcollective/simplerpc/agents.html -[SimpleRPCClients]: /mcollective/simplerpc/clients.html -[SimpleRPCAuditing]: /mcollective/simplerpc/auditing.html -[SimpleRPCAuthorization]: /mcollective/simplerpc/authorization.html -[DDL]: /mcollective/reference/plugins/ddl.html -[SimpleRPCMessageFormat]: /mcollective/simplerpc/messageformat.html -[RPCUtil]: /mcollective/reference/plugins/rpcutil.html -[WritingAgentsScreenCast]: https://www.youtube.com/watch?v=2Xhq_UqnqRE -[RestGateway]: http://github.com/puppetlabs/marionette-collective/blob/master/ext/mc-rpc-restserver.rb - -MCollective is a framework for writing feature full agents and clients and provides a [rich system to do that][WritingAgents]. MCollective's native Client though is very low level, a bit like TCP/IP is to HTTP. Like TCP/IP the native client does not provide any Authentication, Authorization etc. - -MCollective Simple RPC is a framework on top of the standard client that abstracts away a lot of the complexity and provides a lot of convention and standards. It's a bit like using HTTP ontop of TCP/IP to create REST services. - -SimpleRPC is a framework that provides the following: - -* Provide simple conventions for writing [agents][SimpleRPCAgents] and [clients][SimpleRPCClients], favoring convention over custom design -* Very easy to write agents including input validation and a sensible feedback mechanism in case of error -* Provide [audit logging][SimpleRPCAuditing] abilities of calls to agents -* Provide the ability to do [fine grain Authorization][SimpleRPCAuthorization] of calls to agents and actions. -* Has a [Data Definition Language][DDL] used to describe agents and assist in giving hints to auto generating user interfaces. -* The provided generic calling tool should be able to speak to most compliant agents -* Should you need to you can still write your own clients, this should be very easy too -* Return data should be easy to print, in most cases the framework should be able to print a sensible output with a single, provided, function. The [DDL][] is used here to improve the standard one-size-fits-all methods. -* The full capabilities of the standard Client classes should still be exposed in case you want to write advanced agents and clients -* A documented [standard message format][SimpleRPCMessageFormat] built ontop of the core format. - - -We've provided full tutorials on [Writing Simple RPC Agents][SimpleRPCAgents] and [Clients][SimpleRPCClients]. There is also a [screencast that will give you a quick look at what is involved in writing agents][WritingAgentsScreenCast]. - - -A bit of code probably says more than lots of English, so here's a simple hello world Agent, it just echo's back everything you send it in the _:msg_ argument: - -{% highlight ruby linenos %} -module MCollective - module Agent - class Helloworld "Welcome to MCollective Simple RPC") - -printrpcstats -{% endhighlight %} - -With a standard interface come a lot of possibilities, just like the standard one-size-fits-all CLI client above you can make web interfaces, there's a [simple MCollective <-> REST bridge in the ext directory][RestGateway]. - -A helper agent called [_rpcutil_][RPCUtil] is included that helps you gather stats, inventory etc about the running daemon. diff --git a/website/simplerpc/messageformat.md b/website/simplerpc/messageformat.md deleted file mode 100644 index e4b16a91..00000000 --- a/website/simplerpc/messageformat.md +++ /dev/null @@ -1,92 +0,0 @@ ---- -layout: default -title: SimpleRPC Message Format ---- -[MessageFormat]: ../reference/basic/messageformat.html -[ErrorCodes]: clients.html#dealing-with-the-results-directly - -SimpleRPC has a specific message structure that builds on the core -[MessageFormat][]. As such SimpleRPC is simply a plugin developed -ontop of The Marionette Collective rather than an integrated part. - -The core messages has a _:body_ structure where agents and clients -can send any data between nodes and clients. All the SimpleRPC -structures below goes in this body. Filters, targets etc all use the -standard core [MessageFormat][]. - -# Requests - -A basic SimpleRPC message can be seen below: - -{% highlight ruby %} -{:agent => "echo", - :action => "echo", - :caller => "cert=rip", - :data => {:message => "hello world"}, - :process_results => true} -{% endhighlight %} - -This structure will be sent as the _:body_ of the core message, you might create -this request using the command below: - -{% highlight ruby %} - e = rpcclient("echo") - e.echo(:message => "hello world") -{% endhighlight %} - -## :agent - -Records the agent that this message is targetted at. - -## :action - -The action being called. As the core protocol has no concept of actions per -agent this provides the needed data to route the request to the right code in -the SimpleRPC agent - -## :caller - -The callerid initiating the request. This is redundant and might be removed -later since the core message format also includes this information - the core -did not always include it. Removing it will only break backwards compatability -with really old versions. - -## :data - -The data being sent to the SimpleRPC action as its _request_ structure, -technically this can be any data but by SimpleRPC convention this would be a -hash with keys being of the Symbol type as per the example above - -## :process_results - -Indicates the client preference to receive a result or not, the SimpleRPC agent -should not send a response at all if this is true. - -# Replies - -As with requests the replies just build on the core [MessageFormat][] and would be -in the body of the standard message. - -A typical rely would look like: - -{% highlight ruby %} -{:statuscode => 0 - :statusmsg => "OK", - :data => {:message => "hello world"}} -{% endhighlight %} - -## :statuscode and :statusmsg - -The statuscode and statusmsg are related and is used for error propagation -through the collective. - -These are the [documented errors clients receive][ErrorCodes] and will result -in exceptions raised on the client in some cases. - -The agent's _fail_ and _fail!_ methods will manipulate these structures. - -## :data - -This is a freeform variable for any data being returned by agents. Technically -it can be anything but by SimpleRPC convention it's a hash with keys being of -type Symbol. diff --git a/website/terminology.md b/website/terminology.md deleted file mode 100644 index 578362d5..00000000 --- a/website/terminology.md +++ /dev/null @@ -1,101 +0,0 @@ ---- -layout: default -title: Terminology -toc: false ---- -[Software_agent]: http://en.wikipedia.org/wiki/Software_agent -[Plugin]: http://en.wikipedia.org/wiki/Plugin -[Publish_subscribe]: http://en.wikipedia.org/wiki/Publish_subscribe -[Apache ActiveMQ]: http://activemq.apache.org/ -[SimpleRPCAgents]: /mcollective/simplerpc/agents.html -[SimpleRPCIntroduction]: /mcollective/simplerpc/ -[WritingFactsPlugins]: /mcollective/reference/plugins/facts.html -[Subcollective]: /mcollective/reference/basic/subcollectives.html -[Registration]: /mcollective/reference/plugins/registration.html -[SimpleRPCAuthorization]: /mcollective/simplerpc/authorization.html - -This page documents the various terms used in relation to mcollective. - -## Server -The mcollective daemon, an app server for hosting Agents and managing -the connection to your Middleware. - -## Node -The Computer or Operating System that the Server runs on. - -## Agent -A block of Ruby code that performs a specific role, the main reason for -mcollective's existence is to host agents. Agents can perform tasks like -manipulate firewalls, services, packages etc. See [Wikipedia][Software_agent]. - -Docs to write your own can be seen in [SimpleRPCAgents][] - -## Plugins -Ruby code that lives inside the server and takes on roles like security, connection -handling, agents and so forth. See [Wikipedia][Plugin] - -## Middleware -A [publish subscribe][Publish_subscribe] based system like [Apache ActiveMQ][]. - -## Connector -A plugin of the type *MCollective::Connector* that handles the communication with your chosen Middleware. - -## Name Space -Currently messages are sent to the middleware directed at topics named */topic/mcollective.package.command* -and replies come back on */topic/mcollective.package.reply*. - -In this example the namespace is "mcollective" and all servers and clients who wish to form part of the same -Collective must use the same name space. - -Middleware can generally carry several namespaces and therefore several Collectives. - -## Collective -A combination of Servers, Nodes and Middleware all operating in the same Namespace. - -Multiple collectives can be built sharing the same Middleware but kept separate by using different Namespaces. - -## Subcollective -A server can belong to many Namespaces. A [Subcollective][] is a Namespace that only a subset of a full collectives nodes belong to. - -Subcolllectives are used to partition networks and to control broadcast domains in high traffic networks. - -## Simple RPC -A Remote Procedure Call system built ontop of MCollective that makes it very simple to write feature -full agents and clients. See [SimpleRPCIntroduction][]. - -## Action -Agents expose tasks, we call these tasks actions. Each agent like a exim queue management agent might -expose many tasks like *mailq*, *rm*, *retry* etc. These are al actions provided by an agent. - -## Facts -Discreet bits of information about your nodes. Examples could be the domain name, country, -role, operating system release etc. - -Facts are provided by plugins of the type *MCollective::Facts*, you can read about writing -your own in [WritingFactsPlugins][] - -## Registration -Servers can send regular messages to an agent called *registration*. The code that sends the -registration messages are plugins of the type *MCollective::Registration*. See [Registration][]. - -## Security -A plugin of the type *MCollective::Security* that takes care of encryption, authentication -and encoding of messages on which will then be passed on to the Connector for delivery to the Collective. - -## Client -Software that produce commands for agents to process, typically this would be a computer with -the client package installed and someone using the commands like *mc-package* to interact with Agents. - -Often clients will use the *MCollective::Client* library to communicate to the Collective - -## User -Servers and Clients all authenticate to the Middleware, user would generally refer to the username -used to authenticate against the middleware. - -## Audit -In relation to SimpleRPC an audit action is a step requests go through where they can get -logged to disk or other similar action - -## Authorization -In relation to SimpleRPC authorization is a processes whereby requests get allowed or denied -based on some identifying information of the requester. See [SimpleRPCAuthorization][].