From afd33e89dc7bc2a18717e33d4d167f29c81e8898 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=A1gr?= Date: Fri, 31 Oct 2014 09:50:27 +0100 Subject: [PATCH 1/3] Removed git submodules --- .gitmodules | 102 ---------------------------------------------------- apache | 1 - ceilometer | 1 - certmonger | 1 - cinder | 1 - common | 1 - concat | 1 - firewall | 1 - glance | 1 - gluster | 1 - haproxy | 1 - heat | 1 - horizon | 1 - inifile | 1 - keystone | 1 - memcached | 1 - mongodb | 1 - mysql | 1 - neutron | 1 - nova | 1 - nssdb | 1 - openstack | 1 - pacemaker | 1 - puppet | 1 - qpid | 1 - rsync | 1 - ssh | 1 - stdlib | 1 - swift | 1 - sysctl | 1 - tempest | 1 - vcsrepo | 1 - vlan | 1 - vswitch | 1 - xinetd | 1 - 35 files changed, 136 deletions(-) delete mode 160000 apache delete mode 160000 ceilometer delete mode 160000 certmonger delete mode 160000 cinder delete mode 160000 common delete mode 160000 concat delete mode 160000 firewall delete mode 160000 glance delete mode 160000 gluster delete mode 160000 haproxy delete mode 160000 heat delete mode 160000 horizon delete mode 160000 inifile delete mode 160000 keystone delete mode 160000 memcached delete mode 160000 mongodb delete mode 160000 mysql delete mode 160000 neutron delete mode 160000 nova delete mode 160000 nssdb delete mode 160000 openstack delete mode 160000 pacemaker delete mode 160000 puppet delete mode 160000 qpid delete mode 160000 rsync delete mode 160000 ssh delete mode 160000 stdlib delete mode 160000 swift delete mode 160000 sysctl delete mode 160000 tempest delete mode 160000 vcsrepo delete mode 160000 vlan delete mode 160000 vswitch delete mode 160000 xinetd diff --git a/.gitmodules b/.gitmodules index d6ed0c439..e69de29bb 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,102 +0,0 @@ -[submodule "apache"] - path = apache - url = https://github.com/puppetlabs/puppetlabs-apache.git -[submodule "ceilometer"] - path = ceilometer - url = https://github.com/stackforge/puppet-ceilometer.git -[submodule "certmonger"] - path = certmonger - url = https://github.com/rcritten/puppet-certmonger.git -[submodule "cinder"] - path = cinder - url = https://github.com/stackforge/puppet-cinder.git -[submodule "concat"] - path = concat - url = https://github.com/puppetlabs/puppetlabs-concat.git -[submodule "firewall"] - path = firewall - url = https://github.com/puppetlabs/puppetlabs-firewall.git -[submodule "glance"] - path = glance - url = https://github.com/stackforge/puppet-glance.git -[submodule "haproxy"] - path = haproxy - url = https://github.com/puppetlabs/puppetlabs-haproxy.git -[submodule "heat"] - path = heat - url = https://github.com/stackforge/puppet-heat.git -[submodule "horizon"] - path = horizon - url = https://github.com/stackforge/puppet-horizon.git -[submodule "inifile"] - path = inifile - url = https://github.com/puppetlabs/puppetlabs-inifile.git -[submodule "keystone"] - path = keystone - url = https://github.com/stackforge/puppet-keystone.git -[submodule "memcached"] - path = memcached - url = https://github.com/saz/puppet-memcached.git -[submodule "mongodb"] - path = mongodb - url = https://github.com/puppetlabs/puppetlabs-mongodb.git -[submodule "mysql"] - path = mysql - url = https://github.com/packstack/puppetlabs-mysql.git -[submodule "neutron"] - path = neutron - url = https://github.com/stackforge/puppet-neutron.git -[submodule "nova"] - path = nova - url = https://github.com/stackforge/puppet-nova.git -[submodule "nssdb"] - path = nssdb - url = https://github.com/rcritten/puppet-nssdb.git -[submodule "openstack"] - path = openstack - url = https://github.com/stackforge/puppet-openstack.git -[submodule "pacemaker"] - path = pacemaker - url = https://github.com/radez/puppet-pacemaker.git -[submodule "qpid"] - path = qpid - url = https://github.com/dprince/puppet-qpid -[submodule "rsync"] - path = rsync - url = https://github.com/puppetlabs/puppetlabs-rsync.git -[submodule "ssh"] - path = ssh - url = https://github.com/saz/puppet-ssh.git -[submodule "stdlib"] - path = stdlib - url = https://github.com/puppetlabs/puppetlabs-stdlib.git -[submodule "swift"] - path = swift - url = https://github.com/stackforge/puppet-swift.git -[submodule "sysctl"] - path = sysctl - url = https://github.com/puppetlabs/puppetlabs-sysctl.git -[submodule "tempest"] - path = tempest - url = https://github.com/stackforge/puppet-tempest.git -[submodule "vcsrepo"] - path = vcsrepo - url = https://github.com/puppetlabs/puppetlabs-vcsrepo.git -[submodule "vlan"] - path = vlan - url = https://github.com/derekhiggins/puppet-vlan.git -[submodule "vswitch"] - path = vswitch - url = https://github.com/stackforge/puppet-vswitch.git -[submodule "xinetd"] - path = xinetd - url = https://github.com/packstack/puppetlabs-xinetd.git -[submodule "gluster"] - path = gluster - url = https://github.com/purpleidea/puppet-gluster.git -[submodule "common"] - path = common - url = https://github.com/purpleidea/puppet-common.git -[submodule "puppet"] - path = puppet - url = https://github.com/purpleidea/puppet-puppet.git diff --git a/apache b/apache deleted file mode 160000 index a9017af01..000000000 --- a/apache +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a9017af0190bbfaf56cffbac042ca2a081198e89 diff --git a/ceilometer b/ceilometer deleted file mode 160000 index a7d368dc4..000000000 --- a/ceilometer +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a7d368dc4d2e85103fc058149d45140431b53743 diff --git a/certmonger b/certmonger deleted file mode 160000 index 5fbf10fbb..000000000 --- a/certmonger +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 5fbf10fbbff4aed4db30e839c63c99b195e8425a diff --git a/cinder b/cinder deleted file mode 160000 index f3e4aeeb9..000000000 --- a/cinder +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f3e4aeeb92d300b3c05b07d3c79a812c129a95e7 diff --git a/common b/common deleted file mode 160000 index 2c0ed2844..000000000 --- a/common +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2c0ed2844c606fd806bde0c02e47e79c88fab4a9 diff --git a/concat b/concat deleted file mode 160000 index 031bf2612..000000000 --- a/concat +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 031bf261289dcbb32e63b053ed5b3a82117698c0 diff --git a/firewall b/firewall deleted file mode 160000 index c147a624f..000000000 --- a/firewall +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c147a624fb3dba7df625d0d7571b1b6669bcfca5 diff --git a/glance b/glance deleted file mode 160000 index 951a66d53..000000000 --- a/glance +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 951a66d5351b5bb5effe90cfddc5cad85eb7f383 diff --git a/gluster b/gluster deleted file mode 160000 index 80c2b1344..000000000 --- a/gluster +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 80c2b13448c97c70a4b4bc0e402e00ecb5d681d5 diff --git a/haproxy b/haproxy deleted file mode 160000 index f381510e9..000000000 --- a/haproxy +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f381510e940ee11feb044c1c728ba2e5af807c79 diff --git a/heat b/heat deleted file mode 160000 index a64082e4f..000000000 --- a/heat +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a64082e4f8ec75ecadaf20a02678647a7af22fb6 diff --git a/horizon b/horizon deleted file mode 160000 index 3f8cb8320..000000000 --- a/horizon +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3f8cb83207c398b9870d74693b825573bccffa20 diff --git a/inifile b/inifile deleted file mode 160000 index fe9b0d522..000000000 --- a/inifile +++ /dev/null @@ -1 +0,0 @@ -Subproject commit fe9b0d5229ea37179a08c4b49239da9bc950acd1 diff --git a/keystone b/keystone deleted file mode 160000 index ec5e802d6..000000000 --- a/keystone +++ /dev/null @@ -1 +0,0 @@ -Subproject commit ec5e802d642cc1502f98bc53621dd0f61198a586 diff --git a/memcached b/memcached deleted file mode 160000 index 49dbf102f..000000000 --- a/memcached +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 49dbf102fb6eee90297b2ed6a1fa463a8c5ccee7 diff --git a/mongodb b/mongodb deleted file mode 160000 index 3f3929257..000000000 --- a/mongodb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 3f392925710f1758a95f1775d700b5fb787a003d diff --git a/mysql b/mysql deleted file mode 160000 index 83abc4556..000000000 --- a/mysql +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 83abc4556bbf6745708c08375649c9d71b6f66db diff --git a/neutron b/neutron deleted file mode 160000 index 16682d963..000000000 --- a/neutron +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 16682d963580397fa526be4ba296f9e1a62c08eb diff --git a/nova b/nova deleted file mode 160000 index 6b6445e0e..000000000 --- a/nova +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6b6445e0e7c3f717150bfd5a8c0a1314fbbaf289 diff --git a/nssdb b/nssdb deleted file mode 160000 index b3799a9a7..000000000 --- a/nssdb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b3799a9a7c62c3b5b7968f9860220a885b45fb8a diff --git a/openstack b/openstack deleted file mode 160000 index 8a3e0d569..000000000 --- a/openstack +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 8a3e0d5696bcd7c68d5bf321ff2c9e62c0a6e534 diff --git a/pacemaker b/pacemaker deleted file mode 160000 index 21950c9b5..000000000 --- a/pacemaker +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 21950c9b5e619a3cb02f01f66cd4cb33d209a690 diff --git a/puppet b/puppet deleted file mode 160000 index 07ec49d1f..000000000 --- a/puppet +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 07ec49d1f67a498b31b4f164678a76c464e129c4 diff --git a/qpid b/qpid deleted file mode 160000 index 953028ba9..000000000 --- a/qpid +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 953028ba9abdf563bd95970ccf890237711072fb diff --git a/rsync b/rsync deleted file mode 160000 index 357d51f3a..000000000 --- a/rsync +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 357d51f3a6a22bc3da842736176c3510e507b4fb diff --git a/ssh b/ssh deleted file mode 160000 index d6571f8c4..000000000 --- a/ssh +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d6571f8c43ac55d20a6afd8a8ce3f86ac4b0d7a4 diff --git a/stdlib b/stdlib deleted file mode 160000 index 4d2558f38..000000000 --- a/stdlib +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 4d2558f383e18bbe322dd0feb073555491216ab4 diff --git a/swift b/swift deleted file mode 160000 index 033a8bedf..000000000 --- a/swift +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 033a8bedf60f4e4942ece42bb71679f2ffcb3f95 diff --git a/sysctl b/sysctl deleted file mode 160000 index c4486acc2..000000000 --- a/sysctl +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c4486acc2d66de857dbccd8b4b945ea803226705 diff --git a/tempest b/tempest deleted file mode 160000 index 2cfece45a..000000000 --- a/tempest +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 2cfece45a39cfea162111bea15f597e38ca04e98 diff --git a/vcsrepo b/vcsrepo deleted file mode 160000 index 6f7507a2a..000000000 --- a/vcsrepo +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 6f7507a2a48ff0a58c7db026760a2eb84e382a77 diff --git a/vlan b/vlan deleted file mode 160000 index c937de75c..000000000 --- a/vlan +++ /dev/null @@ -1 +0,0 @@ -Subproject commit c937de75c28e63fba8d8738ad6a5f2ede517e53d diff --git a/vswitch b/vswitch deleted file mode 160000 index b642b844d..000000000 --- a/vswitch +++ /dev/null @@ -1 +0,0 @@ -Subproject commit b642b844db6d7df1ced6f11abd641a0982e06363 diff --git a/xinetd b/xinetd deleted file mode 160000 index bba48fad9..000000000 --- a/xinetd +++ /dev/null @@ -1 +0,0 @@ -Subproject commit bba48fad94c6538384173e60900a17c6f7ef7ca3 From da86f0b21115124220f794c6121d7d450ce6e2c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=A1gr?= Date: Fri, 31 Oct 2014 12:55:07 +0100 Subject: [PATCH 2/3] Created Puppetfile --- Puppetfile | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 Puppetfile diff --git a/Puppetfile b/Puppetfile new file mode 100644 index 000000000..e707b96c6 --- /dev/null +++ b/Puppetfile @@ -0,0 +1,135 @@ +mod 'apache', + :commit => 'a9017af0190bbfaf56cffbac042ca2a081198e89', + :git => 'https://github.com/puppetlabs/puppetlabs-apache.git' + +mod 'ceilometer', + :commit => 'a7d368dc4d2e85103fc058149d45140431b53743', + :git => 'https://github.com/stackforge/puppet-ceilometer.git' + +mod 'certmonger', + :commit => '5fbf10fbbff4aed4db30e839c63c99b195e8425a', + :git => 'https://github.com/rcritten/puppet-certmonger.git' + +mod 'cinder', + :commit => 'f3e4aeeb92d300b3c05b07d3c79a812c129a95e7', + :git => 'https://github.com/stackforge/puppet-cinder.git' + +mod 'common', + :commit => '2c0ed2844c606fd806bde0c02e47e79c88fab4a9', + :git => 'https://github.com/purpleidea/puppet-common.git' + +mod 'concat', + :commit => '031bf261289dcbb32e63b053ed5b3a82117698c0', + :git => 'https://github.com/puppetlabs/puppetlabs-concat.git' + +mod 'firewall', + :commit => 'c147a624fb3dba7df625d0d7571b1b6669bcfca5', + :git => 'https://github.com/puppetlabs/puppetlabs-firewall.git' + +mod 'glance', + :commit => '951a66d5351b5bb5effe90cfddc5cad85eb7f383', + :git => 'https://github.com/stackforge/puppet-glance.git' + +mod 'gluster', + :commit => '80c2b13448c97c70a4b4bc0e402e00ecb5d681d5', + :git => 'https://github.com/purpleidea/puppet-gluster.git' + +mod 'haproxy', + :commit => 'f381510e940ee11feb044c1c728ba2e5af807c79', + :git => 'https://github.com/puppetlabs/puppetlabs-haproxy.git' + +mod 'heat', + :commit => 'a64082e4f8ec75ecadaf20a02678647a7af22fb6', + :git => 'https://github.com/stackforge/puppet-heat.git' + +mod 'horizon', + :commit => '3f8cb83207c398b9870d74693b825573bccffa20', + :git => 'https://github.com/stackforge/puppet-horizon.git' + +mod 'inifile', + :commit => 'fe9b0d5229ea37179a08c4b49239da9bc950acd1', + :git => 'https://github.com/puppetlabs/puppetlabs-inifile.git', + +mod 'keystone', + :commit => 'ec5e802d642cc1502f98bc53621dd0f61198a586', + :git => 'https://github.com/stackforge/puppet-keystone.git' + +mod 'memcached', + :commit => '49dbf102fb6eee90297b2ed6a1fa463a8c5ccee7', + :git => 'https://github.com/saz/puppet-memcached.git' + +mod 'mongodb', + :commit => '3f392925710f1758a95f1775d700b5fb787a003d', + :git => 'https://github.com/puppetlabs/puppetlabs-mongodb.git' + +mod 'mysql', + :commit => '83abc4556bbf6745708c08375649c9d71b6f66db', + :git => 'https://github.com/packstack/puppetlabs-mysql.git' + +mod 'neutron', + :commit => '16682d963580397fa526be4ba296f9e1a62c08eb', + :git => 'https://github.com/stackforge/puppet-neutron.git', + +mod 'nova', + :commit => '6b6445e0e7c3f717150bfd5a8c0a1314fbbaf289', + :git => 'https://github.com/stackforge/puppet-nova.git', + +mod 'nssdb', + :commit => 'b3799a9a7c62c3b5b7968f9860220a885b45fb8a', + :git => 'https://github.com/rcritten/puppet-nssdb.git' + +mod 'openstack', + :commit => '8a3e0d5696bcd7c68d5bf321ff2c9e62c0a6e534', + :git => 'https://github.com/stackforge/puppet-openstack.git' + +mod 'pacemaker', + :commit => '21950c9b5e619a3cb02f01f66cd4cb33d209a690', + :git => 'https://github.com/radez/puppet-pacemaker.git' + +mod 'puppet', + :commit => '07ec49d1f67a498b31b4f164678a76c464e129c4', + :git => 'https://github.com/purpleidea/puppet-puppet.git' + +mod 'qpid', + :commit => '953028ba9abdf563bd95970ccf890237711072fb', + :git => 'https://github.com/dprince/puppet-qpid' + +mod 'rsync', + :commit => '357d51f3a6a22bc3da842736176c3510e507b4fb', + :git => 'https://github.com/puppetlabs/puppetlabs-rsync.git' + +mod 'ssh', + :commit => 'd6571f8c43ac55d20a6afd8a8ce3f86ac4b0d7a4', + :git => 'https://github.com/saz/puppet-ssh.git' + +mod 'stdlib', + :commit => '4d2558f383e18bbe322dd0feb073555491216ab4', + :git => 'https://github.com/puppetlabs/puppetlabs-stdlib.git' + +mod 'swift', + :commit => '033a8bedf60f4e4942ece42bb71679f2ffcb3f95', + :git => 'https://github.com/stackforge/puppet-swift.git' + +mod 'sysctl', + :commit => 'c4486acc2d66de857dbccd8b4b945ea803226705', + :git => 'https://github.com/puppetlabs/puppetlabs-sysctl.git' + +mod 'tempest', + :commit => '2cfece45a39cfea162111bea15f597e38ca04e98', + :git => 'https://github.com/stackforge/puppet-tempest.git' + +mod 'vcsrepo', + :commit => '6f7507a2a48ff0a58c7db026760a2eb84e382a77', + :git => 'https://github.com/puppetlabs/puppetlabs-vcsrepo.git' + +mod 'vlan', + :commit => 'c937de75c28e63fba8d8738ad6a5f2ede517e53d', + :git => 'https://github.com/derekhiggins/puppet-vlan.git' + +mod 'vswitch', + :commit => 'b642b844db6d7df1ced6f11abd641a0982e06363', + :git => 'https://github.com/stackforge/puppet-vswitch.git' + +mod 'xinetd', + :commit => 'bba48fad94c6538384173e60900a17c6f7ef7ca3', + :git => 'https://github.com/packstack/puppetlabs-xinetd.git' From f001bf6bfb6527d7268c6693925dcb6f03ab7803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20M=C3=A1gr?= Date: Fri, 31 Oct 2014 13:19:44 +0100 Subject: [PATCH 3/3] Automatic update This module update commit was generated by Bade. For more info please check https://github.com/paramite/bade This commit is setting modules to following state: apache - initial commit: a9017af0190bbfaf56cffbac042ca2a081198e89 ceilometer - initial commit: a7d368dc4d2e85103fc058149d45140431b53743 certmonger - initial commit: 5fbf10fbbff4aed4db30e839c63c99b195e8425a cinder - initial commit: f3e4aeeb92d300b3c05b07d3c79a812c129a95e7 common - initial commit: 2c0ed2844c606fd806bde0c02e47e79c88fab4a9 concat - initial commit: 031bf261289dcbb32e63b053ed5b3a82117698c0 firewall - initial commit: c147a624fb3dba7df625d0d7571b1b6669bcfca5 glance - initial commit: 951a66d5351b5bb5effe90cfddc5cad85eb7f383 gluster - initial commit: 80c2b13448c97c70a4b4bc0e402e00ecb5d681d5 haproxy - initial commit: f381510e940ee11feb044c1c728ba2e5af807c79 heat - initial commit: a64082e4f8ec75ecadaf20a02678647a7af22fb6 horizon - initial commit: 3f8cb83207c398b9870d74693b825573bccffa20 inifile - initial commit: fe9b0d5229ea37179a08c4b49239da9bc950acd1 keystone - initial commit: ec5e802d642cc1502f98bc53621dd0f61198a586 memcached - initial commit: 49dbf102fb6eee90297b2ed6a1fa463a8c5ccee7 mongodb - initial commit: 3f392925710f1758a95f1775d700b5fb787a003d mysql - initial commit: 83abc4556bbf6745708c08375649c9d71b6f66db neutron - initial commit: 16682d963580397fa526be4ba296f9e1a62c08eb nova - initial commit: 6b6445e0e7c3f717150bfd5a8c0a1314fbbaf289 nssdb - initial commit: b3799a9a7c62c3b5b7968f9860220a885b45fb8a openstack - initial commit: 8a3e0d5696bcd7c68d5bf321ff2c9e62c0a6e534 pacemaker - initial commit: 21950c9b5e619a3cb02f01f66cd4cb33d209a690 puppet - initial commit: 07ec49d1f67a498b31b4f164678a76c464e129c4 qpid - initial commit: 953028ba9abdf563bd95970ccf890237711072fb rsync - initial commit: 357d51f3a6a22bc3da842736176c3510e507b4fb ssh - initial commit: d6571f8c43ac55d20a6afd8a8ce3f86ac4b0d7a4 stdlib - initial commit: 4d2558f383e18bbe322dd0feb073555491216ab4 swift - initial commit: 033a8bedf60f4e4942ece42bb71679f2ffcb3f95 sysctl - initial commit: c4486acc2d66de857dbccd8b4b945ea803226705 tempest - initial commit: 2cfece45a39cfea162111bea15f597e38ca04e98 vcsrepo - initial commit: 6f7507a2a48ff0a58c7db026760a2eb84e382a77 vlan - initial commit: c937de75c28e63fba8d8738ad6a5f2ede517e53d vswitch - initial commit: b642b844db6d7df1ced6f11abd641a0982e06363 xinetd - initial commit: bba48fad94c6538384173e60900a17c6f7ef7ca3 --- apache/.fixtures.yml | 6 + apache/.gitignore | 7 + apache/.nodeset.yml | 31 + apache/.travis.yml | 40 + apache/CHANGELOG.md | 172 ++ apache/CONTRIBUTING.md | 234 ++ apache/Gemfile | 25 + apache/LICENSE | 15 + apache/Modulefile | 12 + apache/README.md | 1536 ++++++++++ apache/README.passenger.md | 93 + apache/Rakefile | 9 + apache/files/httpd | 24 + apache/lib/puppet/provider/a2mod.rb | 34 + apache/lib/puppet/provider/a2mod/a2mod.rb | 35 + apache/lib/puppet/provider/a2mod/gentoo.rb | 116 + apache/lib/puppet/provider/a2mod/modfix.rb | 12 + apache/lib/puppet/provider/a2mod/redhat.rb | 60 + apache/lib/puppet/type/a2mod.rb | 30 + apache/manifests/balancer.pp | 80 + apache/manifests/balancermember.pp | 52 + apache/manifests/confd/no_accf.pp | 10 + apache/manifests/default_confd_files.pp | 15 + apache/manifests/default_mods.pp | 111 + apache/manifests/default_mods/load.pp | 8 + apache/manifests/dev.pp | 11 + apache/manifests/init.pp | 331 +++ apache/manifests/listen.pp | 9 + apache/manifests/mod.pp | 120 + apache/manifests/mod/alias.pp | 18 + apache/manifests/mod/auth_basic.pp | 3 + apache/manifests/mod/auth_kerb.pp | 5 + apache/manifests/mod/authnz_ldap.pp | 19 + apache/manifests/mod/autoindex.pp | 12 + apache/manifests/mod/cache.pp | 3 + apache/manifests/mod/cgi.pp | 4 + apache/manifests/mod/cgid.pp | 23 + apache/manifests/mod/dav.pp | 3 + apache/manifests/mod/dav_fs.pp | 20 + apache/manifests/mod/dav_svn.pp | 5 + apache/manifests/mod/deflate.pp | 12 + apache/manifests/mod/dev.pp | 5 + apache/manifests/mod/dir.pp | 21 + apache/manifests/mod/disk_cache.pp | 24 + apache/manifests/mod/event.pp | 54 + apache/manifests/mod/expires.pp | 3 + apache/manifests/mod/fastcgi.pp | 24 + apache/manifests/mod/fcgid.pp | 3 + apache/manifests/mod/headers.pp | 3 + apache/manifests/mod/include.pp | 3 + apache/manifests/mod/info.pp | 14 + apache/manifests/mod/itk.pp | 64 + apache/manifests/mod/ldap.pp | 12 + apache/manifests/mod/mime.pp | 21 + apache/manifests/mod/mime_magic.pp | 14 + apache/manifests/mod/negotiation.pp | 12 + apache/manifests/mod/nss.pp | 25 + apache/manifests/mod/passenger.pp | 48 + apache/manifests/mod/perl.pp | 3 + apache/manifests/mod/peruser.pp | 73 + apache/manifests/mod/php.pp | 26 + apache/manifests/mod/prefork.pp | 74 + apache/manifests/mod/proxy.pp | 15 + apache/manifests/mod/proxy_ajp.pp | 4 + apache/manifests/mod/proxy_balancer.pp | 10 + apache/manifests/mod/proxy_html.pp | 28 + apache/manifests/mod/proxy_http.pp | 4 + apache/manifests/mod/python.pp | 5 + apache/manifests/mod/reqtimeout.pp | 12 + apache/manifests/mod/rewrite.pp | 4 + apache/manifests/mod/rpaf.pp | 20 + apache/manifests/mod/setenvif.pp | 12 + apache/manifests/mod/ssl.pp | 26 + apache/manifests/mod/status.pp | 42 + apache/manifests/mod/suphp.pp | 14 + apache/manifests/mod/userdir.pp | 17 + apache/manifests/mod/vhost_alias.pp | 3 + apache/manifests/mod/worker.pp | 75 + apache/manifests/mod/wsgi.pp | 21 + apache/manifests/mod/xsendfile.pp | 4 + apache/manifests/namevirtualhost.pp | 9 + apache/manifests/package.pp | 48 + apache/manifests/params.pp | 213 ++ apache/manifests/peruser/multiplexer.pp | 17 + apache/manifests/peruser/processor.pp | 17 + apache/manifests/php.pp | 18 + apache/manifests/proxy.pp | 15 + apache/manifests/python.pp | 18 + apache/manifests/service.pp | 35 + apache/manifests/ssl.pp | 18 + apache/manifests/vhost.pp | 523 ++++ .../spec/acceptance/apache_parameters_spec.rb | 408 +++ apache/spec/acceptance/apache_ssl_spec.rb | 87 + apache/spec/acceptance/basic_spec.rb | 12 + apache/spec/acceptance/class_spec.rb | 58 + apache/spec/acceptance/default_mods_spec.rb | 97 + apache/spec/acceptance/itk_spec.rb | 35 + apache/spec/acceptance/mod_php_spec.rb | 95 + apache/spec/acceptance/mod_suphp_spec.rb | 44 + .../acceptance/nodesets/centos-59-x64.yml | 11 + .../acceptance/nodesets/centos-64-x64-pe.yml | 13 + .../acceptance/nodesets/centos-64-x64.yml | 11 + .../acceptance/nodesets/debian-607-x64.yml | 11 + .../acceptance/nodesets/debian-70rc1-x64.yml | 11 + apache/spec/acceptance/nodesets/default.yml | 1 + .../acceptance/nodesets/fedora-18-x64.yml | 11 + .../acceptance/nodesets/sles-11sp1-x64.yml | 11 + .../nodesets/ubuntu-server-10044-x64.yml | 11 + .../nodesets/ubuntu-server-12042-x64.yml | 11 + apache/spec/acceptance/prefork_worker_spec.rb | 81 + apache/spec/acceptance/service_spec.rb | 19 + apache/spec/acceptance/vhost_spec.rb | 974 +++++++ apache/spec/classes/apache_spec.rb | 464 +++ apache/spec/classes/dev_spec.rb | 38 + apache/spec/classes/mod/auth_kerb_spec.rb | 41 + apache/spec/classes/mod/authnz_ldap_spec.rb | 65 + apache/spec/classes/mod/dav_svn_spec.rb | 41 + apache/spec/classes/mod/dev_spec.rb | 23 + apache/spec/classes/mod/dir_spec.rb | 88 + apache/spec/classes/mod/event_spec.rb | 17 + apache/spec/classes/mod/fastcgi_spec.rb | 32 + apache/spec/classes/mod/fcgid_spec.rb | 41 + apache/spec/classes/mod/info_spec.rb | 99 + apache/spec/classes/mod/itk_spec.rb | 31 + apache/spec/classes/mod/mime_magic_spec.rb | 93 + apache/spec/classes/mod/passenger_spec.rb | 129 + apache/spec/classes/mod/perl_spec.rb | 41 + apache/spec/classes/mod/peruser_spec.rb | 17 + apache/spec/classes/mod/php_spec.rb | 99 + apache/spec/classes/mod/prefork_spec.rb | 47 + apache/spec/classes/mod/proxy_html_spec.rb | 45 + apache/spec/classes/mod/python_spec.rb | 41 + apache/spec/classes/mod/rpaf_spec.rb | 77 + apache/spec/classes/mod/ssl_spec.rb | 53 + apache/spec/classes/mod/status_spec.rb | 166 ++ apache/spec/classes/mod/suphp_spec.rb | 27 + apache/spec/classes/mod/worker_spec.rb | 44 + apache/spec/classes/mod/wsgi_spec.rb | 54 + apache/spec/classes/params_spec.rb | 21 + apache/spec/classes/service_spec.rb | 109 + apache/spec/defines/mod_spec.rb | 105 + apache/spec/defines/vhost_spec.rb | 1175 ++++++++ .../site_apache/templates/fake.conf.erb | 1 + apache/spec/spec.opts | 4 + apache/spec/spec_helper.rb | 1 + apache/spec/spec_helper_acceptance.rb | 38 + .../spec/unit/provider/a2mod/gentoo_spec.rb | 184 ++ apache/templates/confd/no-accf.conf.erb | 4 + apache/templates/httpd.conf.erb | 92 + apache/templates/listen.erb | 6 + apache/templates/mod/alias.conf.erb | 9 + apache/templates/mod/authnz_ldap.conf.erb | 5 + apache/templates/mod/autoindex.conf.erb | 56 + apache/templates/mod/cgid.conf.erb | 1 + apache/templates/mod/dav_fs.conf.erb | 1 + apache/templates/mod/deflate.conf.erb | 4 + apache/templates/mod/dir.conf.erb | 1 + apache/templates/mod/disk_cache.conf.erb | 8 + apache/templates/mod/event.conf.erb | 9 + apache/templates/mod/fastcgi.conf.erb | 6 + apache/templates/mod/info.conf.erb | 6 + apache/templates/mod/itk.conf.erb | 8 + apache/templates/mod/ldap.conf.erb | 7 + apache/templates/mod/mime.conf.erb | 36 + apache/templates/mod/mime_magic.conf.erb | 1 + apache/templates/mod/mpm_event.conf.erb | 9 + apache/templates/mod/negotiation.conf.erb | 2 + apache/templates/mod/nss.conf.erb | 228 ++ apache/templates/mod/passenger.conf.erb | 34 + apache/templates/mod/peruser.conf.erb | 12 + apache/templates/mod/php5.conf.erb | 30 + apache/templates/mod/prefork.conf.erb | 8 + apache/templates/mod/proxy.conf.erb | 23 + apache/templates/mod/proxy_html.conf.erb | 24 + apache/templates/mod/reqtimeout.conf.erb | 2 + apache/templates/mod/rpaf.conf.erb | 15 + apache/templates/mod/setenvif.conf.erb | 34 + apache/templates/mod/ssl.conf.erb | 24 + apache/templates/mod/status.conf.erb | 12 + apache/templates/mod/suphp.conf.erb | 19 + apache/templates/mod/userdir.conf.erb | 19 + apache/templates/mod/worker.conf.erb | 9 + apache/templates/mod/wsgi.conf.erb | 13 + apache/templates/namevirtualhost.erb | 8 + apache/templates/ports_header.erb | 5 + apache/templates/vhost.conf.erb | 64 + apache/templates/vhost/_aliases.erb | 12 + apache/templates/vhost/_block.erb | 10 + apache/templates/vhost/_custom_fragment.erb | 5 + apache/templates/vhost/_directories.erb | 146 + apache/templates/vhost/_error_document.erb | 7 + apache/templates/vhost/_fastcgi.erb | 18 + apache/templates/vhost/_header.erb | 10 + apache/templates/vhost/_itk.erb | 28 + apache/templates/vhost/_php_admin.erb | 12 + apache/templates/vhost/_proxy.erb | 20 + apache/templates/vhost/_rack.erb | 7 + apache/templates/vhost/_redirect.erb | 24 + apache/templates/vhost/_requestheader.erb | 10 + apache/templates/vhost/_rewrite.erb | 40 + apache/templates/vhost/_scriptalias.erb | 24 + apache/templates/vhost/_serveralias.erb | 7 + apache/templates/vhost/_setenv.erb | 12 + apache/templates/vhost/_ssl.erb | 41 + apache/templates/vhost/_suphp.erb | 11 + apache/templates/vhost/_wsgi.erb | 15 + apache/tests/apache.pp | 6 + apache/tests/dev.pp | 1 + apache/tests/init.pp | 1 + apache/tests/mod_load_params.pp | 11 + apache/tests/mods.pp | 9 + apache/tests/mods_custom.pp | 16 + apache/tests/php.pp | 4 + apache/tests/vhost.pp | 237 ++ apache/tests/vhost_directories.pp | 44 + apache/tests/vhost_ip_based.pp | 25 + apache/tests/vhost_ssl.pp | 23 + apache/tests/vhosts_without_listen.pp | 53 + ceilometer/.fixtures.yml | 15 + ceilometer/.gitreview | 5 + ceilometer/.travis.yml | 28 + ceilometer/Gemfile | 18 + ceilometer/LICENSE | 174 ++ ceilometer/Modulefile | 14 + ceilometer/README.md | 114 + ceilometer/Rakefile | 6 + ceilometer/examples/site.pp | 66 + .../provider/ceilometer_config/ini_setting.rb | 22 + .../puppet/provider/file_line_after/ruby.rb | 83 + .../lib/puppet/type/ceilometer_config.rb | 19 + ceilometer/lib/puppet/type/file_line_after.rb | 79 + ceilometer/manifests/agent/auth.pp | 62 + ceilometer/manifests/agent/central.pp | 36 + ceilometer/manifests/agent/compute.pp | 77 + ceilometer/manifests/alarm/evaluator.pp | 64 + ceilometer/manifests/alarm/notifier.pp | 74 + ceilometer/manifests/api.pp | 115 + ceilometer/manifests/client.pp | 20 + ceilometer/manifests/collector.pp | 37 + ceilometer/manifests/db.pp | 71 + ceilometer/manifests/db/mysql.pp | 63 + ceilometer/manifests/db/mysql/host_access.pp | 30 + ceilometer/manifests/expirer.pp | 74 + ceilometer/manifests/init.pp | 211 ++ ceilometer/manifests/keystone/auth.pp | 143 + ceilometer/manifests/params.pp | 75 + .../classes/ceilometer_agent_auth_spec.rb | 39 + .../classes/ceilometer_agent_central_spec.rb | 68 + .../classes/ceilometer_agent_compute_spec.rb | 111 + .../ceilometer_alarm_evaluator_spec.rb | 116 + .../classes/ceilometer_alarm_notifier_spec.rb | 109 + .../spec/classes/ceilometer_api_spec.rb | 127 + .../spec/classes/ceilometer_client_spec.rb | 40 + .../spec/classes/ceilometer_collector_spec.rb | 58 + .../spec/classes/ceilometer_db_mysql_spec.rb | 110 + ceilometer/spec/classes/ceilometer_db_spec.rb | 168 ++ .../spec/classes/ceilometer_expirer_spec.rb | 87 + .../spec/classes/ceilometer_init_spec.rb | 253 ++ .../classes/ceilometer_keystone_auth_spec.rb | 166 ++ ceilometer/spec/spec_helper.rb | 5 + certmonger/LICENSE | 13 + certmonger/Modulefile | 10 + certmonger/README.md | 34 + .../lib/facter/ipa_client_configured.rb | 9 + certmonger/manifests/request_ipa_cert.pp | 164 ++ certmonger/manifests/server.pp | 11 + certmonger/tests/nss_ipa.pp | 6 + certmonger/tests/openssl_ipa.pp | 8 + cinder/.fixtures.yml | 21 + cinder/.gitignore | 5 + cinder/.gitreview | 5 + cinder/Gemfile | 18 + cinder/LICENSE | 201 ++ cinder/Modulefile | 15 + cinder/README.md | 235 ++ cinder/Rakefile | 6 + .../cinder_api_paste_ini/ini_setting.rb | 27 + .../provider/cinder_config/ini_setting.rb | 27 + .../lib/puppet/type/cinder_api_paste_ini.rb | 42 + cinder/lib/puppet/type/cinder_config.rb | 42 + cinder/manifests/api.pp | 182 ++ cinder/manifests/backend/iscsi.pp | 56 + cinder/manifests/backend/netapp.pp | 157 ++ cinder/manifests/backend/nfs.pp | 35 + cinder/manifests/backend/rbd.pp | 109 + cinder/manifests/backend/vmdk.pp | 88 + cinder/manifests/backends.pp | 21 + cinder/manifests/backup.pp | 84 + cinder/manifests/backup/ceph.pp | 76 + cinder/manifests/backup/swift.pp | 64 + cinder/manifests/base.pp | 30 + cinder/manifests/ceilometer.pp | 22 + cinder/manifests/client.pp | 20 + cinder/manifests/config.pp | 39 + cinder/manifests/db/mysql.pp | 39 + cinder/manifests/db/mysql/host_access.pp | 16 + cinder/manifests/db/postgresql.pp | 21 + cinder/manifests/db/sync.pp | 14 + cinder/manifests/glance.pp | 82 + cinder/manifests/init.pp | 161 ++ cinder/manifests/keystone/auth.pp | 93 + cinder/manifests/params.pp | 43 + cinder/manifests/qpid.pp | 35 + cinder/manifests/quota.pp | 34 + cinder/manifests/rabbitmq.pp | 52 + cinder/manifests/scheduler.pp | 43 + cinder/manifests/setup_test_volume.pp | 47 + cinder/manifests/type.pp | 67 + cinder/manifests/type_set.pp | 51 + cinder/manifests/vmware.pp | 53 + cinder/manifests/volume.pp | 38 + cinder/manifests/volume/glusterfs.pp | 59 + cinder/manifests/volume/iscsi.pp | 46 + cinder/manifests/volume/netapp.pp | 149 + cinder/manifests/volume/nexenta.pp | 53 + cinder/manifests/volume/nfs.pp | 26 + cinder/manifests/volume/rbd.pp | 104 + cinder/manifests/volume/san.pp | 74 + cinder/manifests/volume/solidfire.pp | 59 + cinder/manifests/volume/vmdk.pp | 82 + cinder/spec/classes/cinder_api_spec.rb | 155 + .../spec/classes/cinder_backup_ceph_spec.rb | 89 + cinder/spec/classes/cinder_backup_spec.rb | 101 + .../spec/classes/cinder_backup_swift_spec.rb | 85 + cinder/spec/classes/cinder_ceilometer_spec.rb | 11 + cinder/spec/classes/cinder_client_spec.rb | 14 + cinder/spec/classes/cinder_db_mysql_spec.rb | 76 + .../spec/classes/cinder_db_postgresql_spec.rb | 26 + cinder/spec/classes/cinder_db_sync_spec.rb | 16 + cinder/spec/classes/cinder_glance_spec.rb | 82 + .../spec/classes/cinder_keystone_auth_spec.rb | 75 + cinder/spec/classes/cinder_params_spec.rb | 12 + cinder/spec/classes/cinder_qpid_spec.rb | 51 + cinder/spec/classes/cinder_quota_spec.rb | 35 + cinder/spec/classes/cinder_rabbitmq_spec.rb | 81 + cinder/spec/classes/cinder_scheduler_spec.rb | 71 + .../classes/cinder_setup_test_volume_spec.rb | 15 + cinder/spec/classes/cinder_spec.rb | 179 ++ cinder/spec/classes/cinder_vmware_spec.rb | 35 + .../classes/cinder_volume_glusterfs_spec.rb | 34 + .../spec/classes/cinder_volume_iscsi_spec.rb | 48 + .../spec/classes/cinder_volume_netapp_spec.rb | 60 + .../classes/cinder_volume_nexenta_spec.rb | 38 + cinder/spec/classes/cinder_volume_nfs_spec.rb | 37 + cinder/spec/classes/cinder_volume_rbd_spec.rb | 82 + cinder/spec/classes/cinder_volume_san_spec.rb | 39 + .../classes/cinder_volume_solidfire_spec.rb | 28 + cinder/spec/classes/cinder_volume_spec.rb | 18 + .../spec/classes/cinder_volume_vmdk_spec.rb | 57 + .../spec/defines/cinder_backend_iscsi_spec.rb | 47 + .../defines/cinder_backend_netapp_spec.rb | 64 + .../spec/defines/cinder_backend_nfs_spec.rb | 42 + .../spec/defines/cinder_backend_rbd_spec.rb | 98 + .../spec/defines/cinder_backend_vmdk_spec.rb | 60 + cinder/spec/defines/cinder_type_set_spec.rb | 29 + cinder/spec/defines/cinder_type_spec.rb | 31 + cinder/spec/spec_helper.rb | 5 + common/.gitignore | 3 + common/COPYING | 661 +++++ common/COPYRIGHT | 16 + common/INSTALL | 18 + common/Makefile | 146 + common/README | 22 + common/VERSION | 1 + common/examples/again-delta-example.pp | 23 + common/examples/again-example.pp | 17 + common/manifests/again.pp | 86 + common/manifests/again/delta.pp | 52 + common/manifests/frag/frag.pp | 124 + common/manifests/init.pp | 23 + common/manifests/vardir.pp | 52 + common/puppet-common.spec.in | 40 + common/templates/again/again.py.erb | 189 ++ concat/CHANGELOG | 29 + concat/README.markdown | 103 + concat/files/concatfragments.sh | 130 + concat/files/null/.gitignore | 0 concat/manifests/fragment.pp | 51 + concat/manifests/init.pp | 176 ++ concat/manifests/setup.pp | 37 + firewall/.fixtures.yml | 3 + firewall/.gitignore | 9 + firewall/.nodeset.yml | 31 + firewall/.travis.yml | 29 + firewall/CHANGELOG.md | 390 +++ firewall/CONTRIBUTING.md | 292 ++ firewall/Gemfile | 18 + firewall/LICENSE | 25 + firewall/Modulefile | 8 + firewall/README.markdown | 429 +++ firewall/Rakefile | 14 + firewall/lib/facter/ip6tables_version.rb | 11 + .../lib/facter/iptables_persistent_version.rb | 15 + firewall/lib/facter/iptables_version.rb | 11 + firewall/lib/puppet/provider/firewall.rb | 34 + .../lib/puppet/provider/firewall/ip6tables.rb | 131 + .../lib/puppet/provider/firewall/iptables.rb | 494 ++++ .../provider/firewallchain/iptables_chain.rb | 178 ++ firewall/lib/puppet/type/firewall.rb | 1017 +++++++ firewall/lib/puppet/type/firewallchain.rb | 222 ++ firewall/lib/puppet/util/firewall.rb | 225 ++ firewall/lib/puppet/util/ipcidr.rb | 42 + firewall/manifests/init.pp | 36 + firewall/manifests/linux.pp | 51 + firewall/manifests/linux/archlinux.pp | 41 + firewall/manifests/linux/debian.pp | 44 + firewall/manifests/linux/redhat.pp | 40 + firewall/metadata.json | 64 + .../spec/acceptance/change_source_spec.rb | 77 + firewall/spec/acceptance/class_spec.rb | 27 + firewall/spec/acceptance/firewall_spec.rb | 1617 +++++++++++ .../spec/acceptance/firewallchain_spec.rb | 125 + firewall/spec/acceptance/ip6_fragment_spec.rb | 114 + firewall/spec/acceptance/isfragment_spec.rb | 92 + .../acceptance/nodesets/centos-59-x64-pe.yml | 12 + .../acceptance/nodesets/centos-59-x64.yml | 10 + .../nodesets/centos-64-x64-fusion.yml | 10 + .../acceptance/nodesets/centos-64-x64-pe.yml | 12 + .../acceptance/nodesets/centos-64-x64.yml | 10 + .../acceptance/nodesets/debian-607-x64.yml | 10 + .../acceptance/nodesets/debian-70rc1-x64.yml | 10 + firewall/spec/acceptance/nodesets/default.yml | 10 + .../acceptance/nodesets/fedora-18-x64.yml | 10 + .../acceptance/nodesets/sles-11sp1-x64.yml | 10 + .../nodesets/ubuntu-server-10044-x64.yml | 10 + .../nodesets/ubuntu-server-12042-x64.yml | 10 + firewall/spec/acceptance/params_spec.rb | 154 + firewall/spec/acceptance/purge_spec.rb | 124 + firewall/spec/acceptance/resource_cmd_spec.rb | 93 + firewall/spec/acceptance/rules_spec.rb | 252 ++ firewall/spec/acceptance/socket_spec.rb | 97 + .../spec/acceptance/standard_usage_spec.rb | 55 + .../fixtures/ip6tables/conversion_hash.rb | 107 + .../spec/fixtures/iptables/conversion_hash.rb | 871 ++++++ firewall/spec/spec_helper.rb | 29 + firewall/spec/spec_helper_acceptance.rb | 44 + .../classes/firewall_linux_archlinux_spec.rb | 32 + .../classes/firewall_linux_debian_spec.rb | 19 + .../classes/firewall_linux_redhat_spec.rb | 22 + .../spec/unit/classes/firewall_linux_spec.rb | 24 + firewall/spec/unit/classes/firewall_spec.rb | 25 + .../iptables_persistent_version_spec.rb | 35 + firewall/spec/unit/facter/iptables_spec.rb | 23 + .../puppet/provider/iptables_chain_spec.rb | 227 ++ .../unit/puppet/provider/iptables_spec.rb | 410 +++ .../spec/unit/puppet/type/firewall_spec.rb | 650 +++++ .../unit/puppet/type/firewallchain_spec.rb | 185 ++ .../spec/unit/puppet/util/firewall_spec.rb | 196 ++ firewall/spec/unit/puppet/util/ipcidr_spec.rb | 67 + glance/.fixtures.yml | 19 + glance/.gitignore | 4 + glance/.gitreview | 5 + glance/Gemfile | 18 + glance/LICENSE | 201 ++ glance/Modulefile | 13 + glance/README.md | 163 ++ glance/Rakefile | 6 + glance/ext/glance.rb | 86 + glance/ext/glance.sh | 8 + glance/lib/puppet/provider/glance.rb | 158 ++ .../provider/glance_api_config/ini_setting.rb | 27 + .../glance_api_paste_ini/ini_setting.rb | 27 + .../glance_cache_config/ini_setting.rb | 27 + .../puppet/provider/glance_image/glance.rb | 106 + .../glance_registry_config/ini_setting.rb | 27 + .../glance_registry_paste_ini/ini_setting.rb | 27 + glance/lib/puppet/type/glance_api_config.rb | 19 + .../lib/puppet/type/glance_api_paste_ini.rb | 19 + glance/lib/puppet/type/glance_cache_config.rb | 19 + glance/lib/puppet/type/glance_image.rb | 75 + .../lib/puppet/type/glance_registry_config.rb | 19 + .../puppet/type/glance_registry_paste_ini.rb | 19 + glance/manifests/api.pp | 370 +++ glance/manifests/backend/cinder.pp | 97 + glance/manifests/backend/file.pp | 19 + glance/manifests/backend/rbd.pp | 34 + glance/manifests/backend/swift.pp | 44 + glance/manifests/cache/cleaner.pp | 42 + glance/manifests/cache/pruner.pp | 42 + glance/manifests/client.pp | 18 + glance/manifests/config.pp | 56 + glance/manifests/db/mysql.pp | 45 + glance/manifests/db/mysql/host_access.pp | 16 + glance/manifests/db/postgresql.pp | 21 + glance/manifests/init.pp | 24 + glance/manifests/keystone/auth.pp | 67 + glance/manifests/notify/qpid.pp | 22 + glance/manifests/notify/rabbitmq.pp | 31 + glance/manifests/params.pp | 29 + glance/manifests/registry.pp | 297 ++ glance/spec/classes/glance_api_spec.rb | 289 ++ .../classes/glance_backend_cinder_spec.rb | 99 + .../spec/classes/glance_backend_file_spec.rb | 34 + .../spec/classes/glance_backend_rbd_spec.rb | 37 + .../spec/classes/glance_backend_swift_spec.rb | 69 + .../spec/classes/glance_cache_cleaner_spec.rb | 21 + .../spec/classes/glance_cache_pruner_spec.rb | 21 + glance/spec/classes/glance_client_spec.rb | 27 + glance/spec/classes/glance_db_mysql_spec.rb | 101 + .../spec/classes/glance_db_postgresql_spec.rb | 26 + .../spec/classes/glance_keystone_auth_spec.rb | 119 + .../spec/classes/glance_notify_qpid_spec.rb | 36 + .../classes/glance_notify_rabbitmq_spec.rb | 44 + glance/spec/classes/glance_registry_spec.rb | 269 ++ glance/spec/classes/glance_spec.rb | 37 + glance/spec/shared_examples.rb | 5 + glance/spec/spec.opts | 6 + glance/spec/spec_helper.rb | 7 + glance/spec/unit/provider/glance_spec.rb | 59 + glance/tests/api.pp | 4 + glance/tests/init.pp | 1 + glance/tests/registry.pp | 4 + glance/tests/site.pp | 57 + gluster/.gitignore | 8 + gluster/.gitmodules | 21 + gluster/COPYING | 661 +++++ gluster/COPYRIGHT | 16 + gluster/DOCUMENTATION.md | 853 ++++++ gluster/INSTALL | 18 + gluster/Makefile | 146 + gluster/Modulefile | 12 + gluster/README | 33 + gluster/README.md | 1 + gluster/VERSION | 1 + gluster/builder/Makefile | 148 + gluster/builder/README | 13 + gluster/builder/Vagrantfile | 57 + gluster/builder/files/cleanup.sh | 7 + .../builder/files/epel-release-6-8.noarch.rpm | Bin 0 -> 14540 bytes gluster/builder/files/network.sh | 40 + gluster/builder/files/password | 1 + .../files/puppetlabs-release-el-6.noarch.rpm | Bin 0 -> 5712 bytes gluster/builder/files/selinux | 10 + gluster/builder/files/ssh.sh | 58 + gluster/builder/files/user.sh | 16 + gluster/builder/versions/template.sh | 13 + .../examples/distributed-replicate-example.pp | 154 + .../filesystem-backed-bricks-example.pp | 52 + gluster/examples/gluster-nfs-ipa-example.pp | 47 + gluster/examples/gluster-simple-example.pp | 17 + .../gluster-simple-physical-example-best.pp | 28 + .../gluster-simple-physical-example.pp | 40 + gluster/examples/mount-example.pp | 21 + gluster/examples/wrapper-example.pp | 152 + gluster/files/groups/small-file-perf | 2 + gluster/files/groups/virt | 8 + gluster/files/sponge.py | 44 + gluster/files/xml.py | 261 ++ gluster/lib/facter/gluster_bricks.rb | 104 + gluster/lib/facter/gluster_fsm.rb | 162 ++ gluster/lib/facter/gluster_fsuuid.rb | 148 + gluster/lib/facter/gluster_host.rb | 35 + gluster/lib/facter/gluster_ports.rb | 80 + gluster/lib/facter/gluster_property.rb | 103 + gluster/lib/facter/gluster_uuid.rb | 140 + gluster/lib/facter/gluster_version.rb | 32 + gluster/lib/facter/gluster_vrrp.rb | 201 ++ .../parser/functions/brick_layout_chained.rb | 137 + .../parser/functions/brick_layout_simple.rb | 111 + gluster/manifests/again.pp | 39 + gluster/manifests/api.pp | 39 + gluster/manifests/brick.pp | 520 ++++ gluster/manifests/brick/base.pp | 41 + gluster/manifests/brick/ext4.pp | 24 + gluster/manifests/brick/xfs.pp | 24 + gluster/manifests/host.pp | 384 +++ gluster/manifests/host/data.pp | 26 + gluster/manifests/init.pp | 63 + gluster/manifests/mount.pp | 171 ++ gluster/manifests/mount/base.pp | 86 + gluster/manifests/repo.pp | 119 + gluster/manifests/rulewrapper.pp | 47 + gluster/manifests/server.pp | 182 ++ gluster/manifests/simple.pp | 189 ++ gluster/manifests/vardir.pp | 52 + gluster/manifests/volume.pp | 441 +++ gluster/manifests/volume/base.pp | 32 + gluster/manifests/volume/fsm.pp | 32 + gluster/manifests/volume/ping.pp | 25 + gluster/manifests/volume/property.pp | 182 ++ gluster/manifests/volume/property/data.pp | 338 +++ gluster/manifests/volume/property/group.pp | 73 + .../manifests/volume/property/group/data.pp | 43 + gluster/manifests/wrapper.pp | 135 + gluster/manifests/xml.pp | 47 + gluster/puppet-gluster-documentation.pdf | Bin 0 -> 229212 bytes gluster/puppet-gluster.spec.in | 49 + gluster/templates/glusterd.info.erb | 4 + gluster/templates/glusterd.vol.erb | 16 + gluster/vagrant/README | 31 + gluster/vagrant/gluster/.gitignore | 3 + gluster/vagrant/gluster/Vagrantfile | 554 ++++ gluster/vagrant/gluster/puppet/files/README | 2 + gluster/vagrant/gluster/puppet/hiera.yaml | 7 + .../gluster/puppet/hieradata/common.yaml | 3 + .../vagrant/gluster/puppet/manifests/site.pp | 158 ++ .../vagrant/gluster/puppet/modules/.gitignore | 1 + .../vagrant/gluster/puppet/modules/Makefile | 65 + gluster/vagrant/gluster/puppet/modules/README | 22 + gluster/vagrant/gluster/puppet/modules/apt | 1 + gluster/vagrant/gluster/puppet/modules/common | 1 + .../vagrant/gluster/puppet/modules/keepalived | 1 + gluster/vagrant/gluster/puppet/modules/puppet | 1 + .../vagrant/gluster/puppet/modules/shorewall | 1 + gluster/vagrant/gluster/puppet/modules/stdlib | 1 + gluster/vagrant/gluster/puppet/modules/yum | 1 + haproxy/.fixtures.yml | 5 + haproxy/.gemfile | 5 + haproxy/.travis.yml | 23 + haproxy/CHANGELOG | 18 + haproxy/Modulefile | 12 + haproxy/README.md | 93 + haproxy/Rakefile | 1 + haproxy/manifests/backend.pp | 76 + haproxy/manifests/balancermember.pp | 105 + haproxy/manifests/frontend.pp | 76 + haproxy/manifests/init.pp | 150 + haproxy/manifests/listen.pp | 95 + haproxy/manifests/params.pp | 65 + haproxy/spec/classes/haproxy_spec.rb | 138 + haproxy/spec/defines/backend_spec.rb | 25 + haproxy/spec/defines/balancermember_spec.rb | 117 + haproxy/spec/defines/frontend_spec.rb | 53 + haproxy/spec/defines/listen_spec.rb | 53 + haproxy/spec/spec.opts | 6 + haproxy/spec/spec_helper.rb | 1 + haproxy/templates/haproxy-base.cfg.erb | 21 + haproxy/templates/haproxy_backend_block.erb | 7 + haproxy/templates/haproxy_balancermember.erb | 3 + haproxy/templates/haproxy_frontend_block.erb | 13 + haproxy/templates/haproxy_listen_block.erb | 13 + haproxy/tests/init.pp | 69 + heat/.fixtures.yml | 15 + heat/.gitreview | 5 + heat/Gemfile | 15 + heat/LICENSE | 174 ++ heat/Modulefile | 13 + heat/README.md | 106 + heat/Rakefile | 7 + heat/examples/site.pp | 29 + .../provider/heat_config/ini_setting.rb | 22 + heat/lib/puppet/type/heat_config.rb | 19 + heat/manifests/api-cfn.pp | 36 + heat/manifests/api-cloudwatch.pp | 36 + heat/manifests/api.pp | 43 + heat/manifests/api_cfn.pp | 42 + heat/manifests/api_cloudwatch.pp | 42 + heat/manifests/client.pp | 19 + heat/manifests/db/mysql.pp | 62 + heat/manifests/db/mysql/host_access.pp | 30 + heat/manifests/engine.pp | 75 + heat/manifests/init.pp | 285 ++ heat/manifests/keystone/auth.pp | 99 + heat/manifests/keystone/auth_cfn.pp | 95 + heat/manifests/params.pp | 52 + heat/spec/classes/heat_api_cfn_spec.rb | 26 + heat/spec/classes/heat_api_cloudwatch_spec.rb | 26 + heat/spec/classes/heat_api_spec.rb | 26 + heat/spec/classes/heat_db_mysql_spec.rb | 84 + heat/spec/classes/heat_engine_spec.rb | 85 + heat/spec/classes/heat_init_spec.rb | 305 ++ .../classes/heat_keystone_auth_cfn_spec.rb | 81 + heat/spec/classes/heat_keystone_auth_spec.rb | 89 + heat/spec/shared_examples.rb | 5 + heat/spec/spec_helper.rb | 7 + horizon/.fixtures.yml | 10 + horizon/.gitignore | 1 + horizon/.gitreview | 5 + horizon/Gemfile | 15 + horizon/LICENSE | 15 + horizon/Modulefile | 12 + horizon/README.md | 124 + horizon/Rakefile | 6 + horizon/manifests/init.pp | 263 ++ horizon/manifests/params.pp | 42 + horizon/spec/classes/horizon_init_spec.rb | 236 ++ .../fixtures/override_local_settings.py.erb | 18 + horizon/spec/spec_helper.rb | 1 + horizon/templates/local_settings.py.erb | 536 ++++ inifile/.fixtures.yml | 3 + inifile/.gitignore | 4 + inifile/.travis.yml | 40 + inifile/CHANGELOG | 89 + inifile/Gemfile | 17 + inifile/LICENSE | 201 ++ inifile/Modulefile | 8 + inifile/README.markdown | 107 + inifile/Rakefile | 1 + .../lib/puppet/provider/ini_setting/ruby.rb | 102 + .../puppet/provider/ini_subsetting/ruby.rb | 70 + inifile/lib/puppet/type/ini_setting.rb | 47 + inifile/lib/puppet/type/ini_subsetting.rb | 55 + inifile/lib/puppet/util/external_iterator.rb | 28 + inifile/lib/puppet/util/ini_file.rb | 300 ++ inifile/lib/puppet/util/ini_file/section.rb | 103 + inifile/lib/puppet/util/setting_value.rb | 93 + inifile/spec/classes/inherit_test1_spec.rb | 10 + .../inherit_ini_setting/ini_setting.rb | 17 + .../lib/puppet/type/inherit_ini_setting.rb | 5 + .../modules/inherit_test1/manifests/init.pp | 5 + inifile/spec/fixtures/tmp/.empty | 0 inifile/spec/spec_helper.rb | 13 + .../provider/ini_setting/inheritance_spec.rb | 67 + .../puppet/provider/ini_setting/ruby_spec.rb | 970 +++++++ .../provider/ini_subsetting/ruby_spec.rb | 135 + .../puppet/util/external_iterator_spec.rb | 35 + .../spec/unit/puppet/util/ini_file_spec.rb | 138 + .../unit/puppet/util/setting_value_spec.rb | 69 + inifile/tests/ini_setting.pp | 25 + inifile/tests/ini_subsetting.pp | 18 + keystone/.fixtures.yml | 15 + keystone/.gitignore | 3 + keystone/.gitreview | 5 + keystone/Gemfile | 15 + keystone/LICENSE | 17 + keystone/Modulefile | 13 + keystone/README.md | 219 ++ keystone/Rakefile | 7 + keystone/examples/apache_dropin.pp | 49 + keystone/examples/apache_with_paths.pp | 54 + keystone/ext/keystone_test.rb | 55 + keystone/files/httpd/keystone.py | 54 + keystone/lib/puppet/provider/keystone.rb | 190 ++ .../provider/keystone_config/ini_setting.rb | 27 + .../provider/keystone_endpoint/keystone.rb | 125 + .../puppet/provider/keystone_role/keystone.rb | 65 + .../provider/keystone_service/keystone.rb | 97 + .../provider/keystone_tenant/keystone.rb | 120 + .../puppet/provider/keystone_user/keystone.rb | 158 ++ .../provider/keystone_user_role/keystone.rb | 229 ++ keystone/lib/puppet/type/keystone_config.rb | 19 + keystone/lib/puppet/type/keystone_endpoint.rb | 43 + keystone/lib/puppet/type/keystone_role.rb | 25 + keystone/lib/puppet/type/keystone_service.rb | 31 + keystone/lib/puppet/type/keystone_tenant.rb | 38 + keystone/lib/puppet/type/keystone_user.rb | 69 + .../lib/puppet/type/keystone_user_role.rb | 51 + keystone/manifests/client.pp | 17 + keystone/manifests/config.pp | 30 + keystone/manifests/cron/token_flush.pp | 57 + keystone/manifests/db/mysql.pp | 75 + keystone/manifests/db/mysql/host_access.pp | 20 + keystone/manifests/db/postgresql.pp | 47 + keystone/manifests/db/sync.pp | 12 + keystone/manifests/dev/install.pp | 64 + keystone/manifests/endpoint.pp | 83 + keystone/manifests/init.pp | 371 +++ keystone/manifests/ldap.pp | 34 + keystone/manifests/params.pp | 31 + keystone/manifests/python.pp | 15 + keystone/manifests/roles/admin.pp | 62 + keystone/manifests/wsgi/apache.pp | 204 ++ keystone/spec/classes/keystone_client_spec.rb | 16 + .../spec/classes/keystone_cron_token_flush.rb | 21 + .../spec/classes/keystone_db_mysql_spec.rb | 106 + .../classes/keystone_db_postgresql_spec.rb | 26 + .../spec/classes/keystone_endpoint_spec.rb | 99 + keystone/spec/classes/keystone_ldap_spec.rb | 20 + keystone/spec/classes/keystone_python_spec.rb | 17 + .../spec/classes/keystone_roles_admin_spec.rb | 77 + keystone/spec/classes/keystone_spec.rb | 417 +++ .../spec/classes/keystone_wsgi_apache_spec.rb | 219 ++ keystone/spec/shared_examples.rb | 5 + keystone/spec/spec.opts | 6 + keystone/spec/spec_helper.rb | 8 + .../keystone_endpoint/keystone_spec.rb | 74 + keystone/spec/unit/provider/keystone_spec.rb | 157 ++ .../provider/keystone_tenant/keystone_spec.rb | 55 + .../provider/keystone_user/keystone_spec.rb | 44 + .../spec/unit/type/keystone_endpoint_spec.rb | 9 + keystone/tests/site.pp | 68 + memcached/.fixtures.yml | 3 + memcached/.gemfile | 5 + memcached/.gitignore | 3 + memcached/.travis.yml | 17 + memcached/LICENSE | 13 + memcached/Modulefile | 8 + memcached/README-DEVELOPER | 9 + memcached/README.md | 39 + memcached/Rakefile | 1 + .../parser/functions/memcached_max_memory.rb | 38 + memcached/manifests/init.pp | 34 + memcached/manifests/params.pp | 21 + memcached/spec/classes/memcached_spec.rb | 126 + memcached/spec/spec.opts | 6 + memcached/spec/spec_helper.rb | 1 + memcached/templates/memcached.conf.erb | 47 + memcached/templates/memcached_sysconfig.erb | 21 + memcached/tests/init.pp | 1 + mongodb/.fixtures.yml | 6 + mongodb/.gitignore | 9 + mongodb/.nodeset.yml | 31 + mongodb/.travis.yml | 33 + mongodb/CHANGELOG | 27 + mongodb/Gemfile | 17 + mongodb/LICENSE | 15 + mongodb/Modulefile | 12 + mongodb/README.md | 473 ++++ mongodb/Rakefile | 2 + .../parser/functions/mongodb_password.rb | 14 + .../provider/mongodb_database/mongodb.rb | 32 + .../puppet/provider/mongodb_user/mongodb.rb | 48 + mongodb/lib/puppet/type/mongodb_database.rb | 20 + mongodb/lib/puppet/type/mongodb_user.rb | 56 + mongodb/manifests/db.pp | 43 + mongodb/manifests/globals.pp | 28 + mongodb/manifests/init.pp | 136 + mongodb/manifests/params.pp | 88 + mongodb/manifests/repo.pp | 31 + mongodb/manifests/repo/apt.pp | 25 + mongodb/manifests/repo/yum.pp | 20 + mongodb/manifests/server.pp | 76 + mongodb/manifests/server/config.pp | 89 + mongodb/manifests/server/install.pp | 30 + mongodb/manifests/server/service.pp | 22 + mongodb/spec/classes/repo_spec.rb | 31 + mongodb/spec/classes/server_config_spec.rb | 97 + mongodb/spec/classes/server_install_spec.rb | 16 + mongodb/spec/classes/server_spec.rb | 23 + mongodb/spec/defines/db_spec.rb | 43 + mongodb/spec/spec_helper.rb | 1 + mongodb/spec/spec_helper_system.rb | 34 + mongodb/spec/system/basic_spec.rb | 10 + mongodb/spec/system/server_10gen_spec.rb | 77 + mongodb/spec/system/server_distro_spec.rb | 71 + mongodb/spec/unit/mongodb_password_spec.rb | 27 + .../provider/mongodb_database/mongodb_spec.rb | 35 + .../provider/mongodb_user/mongodb_spec.rb | 66 + .../unit/puppet/type/mongodb_database_spec.rb | 24 + .../unit/puppet/type/mongodb_user_spec.rb | 67 + mongodb/templates/mongodb.conf.erb | 164 ++ mongodb/tests/globals.pp | 3 + mongodb/tests/init.pp | 1 + mongodb/tests/server.pp | 2 + mysql/.fixtures.yml | 5 + mysql/.gemfile | 5 + mysql/.gitignore | 2 + mysql/.travis.yml | 17 + mysql/CHANGELOG | 128 + mysql/LICENSE | 201 ++ mysql/Modulefile | 9 + mysql/README.md | 129 + mysql/Rakefile | 1 + mysql/TODO | 8 + mysql/files/mysqltuner.pl | 966 +++++++ .../puppet/parser/functions/mysql_password.rb | 15 + mysql/lib/puppet/provider/database/mysql.rb | 42 + .../puppet/provider/database_grant/mysql.rb | 198 ++ .../puppet/provider/database_user/mysql.rb | 42 + mysql/lib/puppet/type/database.rb | 17 + mysql/lib/puppet/type/database_grant.rb | 75 + mysql/lib/puppet/type/database_user.rb | 25 + mysql/manifests/backup.pp | 71 + mysql/manifests/config.pp | 140 + mysql/manifests/db.pp | 83 + mysql/manifests/init.pp | 24 + mysql/manifests/java.pp | 24 + mysql/manifests/params.pp | 123 + mysql/manifests/python.pp | 26 + mysql/manifests/ruby.pp | 28 + mysql/manifests/server.pp | 53 + mysql/manifests/server/account_security.pp | 19 + mysql/manifests/server/config.pp | 111 + mysql/manifests/server/monitor.pp | 19 + mysql/manifests/server/mysqltuner.pp | 22 + mysql/spec/classes/mysql_backup_spec.rb | 58 + mysql/spec/classes/mysql_config_spec.rb | 237 ++ mysql/spec/classes/mysql_init_spec.rb | 54 + mysql/spec/classes/mysql_java_spec.rb | 54 + mysql/spec/classes/mysql_python_spec.rb | 54 + mysql/spec/classes/mysql_ruby_spec.rb | 62 + .../mysql_server_account_security_spec.rb | 40 + .../spec/classes/mysql_server_monitor_spec.rb | 18 + mysql/spec/classes/mysql_server_spec.rb | 98 + mysql/spec/defines/mysql_db_spec.rb | 42 + .../spec/defines/mysql_server_config_spec.rb | 37 + mysql/spec/spec.opts | 6 + mysql/spec/spec_helper.rb | 1 + mysql/spec/unit/mysql_password_spec.rb | 30 + .../provider/database_grant/mysql_spec.rb | 86 + mysql/templates/my.cnf.erb | 47 + mysql/templates/my.cnf.pass.erb | 6 + mysql/templates/my.conf.cnf.erb | 17 + mysql/templates/mysqlbackup.sh.erb | 23 + mysql/tests/backup.pp | 8 + mysql/tests/init.pp | 1 + mysql/tests/java.pp | 1 + mysql/tests/mysql_database.pp | 12 + mysql/tests/mysql_grant.pp | 3 + mysql/tests/mysql_user.pp | 23 + mysql/tests/python.pp | 1 + mysql/tests/ruby.pp | 1 + mysql/tests/server.pp | 3 + mysql/tests/server/account_security.pp | 4 + mysql/tests/server/config.pp | 11 + neutron/.fixtures.yml | 16 + neutron/.gitignore | 5 + neutron/.gitreview | 5 + neutron/Gemfile | 18 + neutron/LICENSE | 176 ++ neutron/Modulefile | 16 + neutron/README.md | 182 ++ neutron/Rakefile | 6 + neutron/examples/base_provision.pp | 54 + neutron/examples/create_network.sh | 8 + neutron/examples/neutron.pp | 68 + .../functions/validate_network_vlan_ranges.rb | 47 + .../functions/validate_tunnel_id_ranges.rb | 44 + .../parser/functions/validate_vni_ranges.rb | 47 + .../functions/validate_vxlan_udp_port.rb | 32 + neutron/lib/puppet/provider/neutron.rb | 175 ++ .../neutron_api_config/ini_setting.rb | 22 + .../provider/neutron_config/ini_setting.rb | 22 + .../neutron_dhcp_agent_config/ini_setting.rb | 22 + .../ini_setting.rb | 22 + .../neutron_l3_agent_config/ini_setting.rb | 22 + .../provider/neutron_l3_ovs_bridge/neutron.rb | 54 + .../neutron_lbaas_agent_config/ini_setting.rb | 22 + .../ini_setting.rb | 22 + .../ini_setting.rb | 22 + .../provider/neutron_network/neutron.rb | 137 + .../neutron_plugin_cisco/ini_setting.rb | 22 + .../ini_setting.rb | 22 + .../ini_setting.rb | 22 + .../ini_setting.rb | 22 + .../neutron_plugin_linuxbridge/ini_setting.rb | 22 + .../neutron_plugin_ml2/ini_setting.rb | 22 + .../neutron_plugin_nvp/ini_setting.rb | 22 + .../neutron_plugin_ovs/ini_setting.rb | 22 + .../puppet/provider/neutron_port/neutron.rb | 191 ++ .../puppet/provider/neutron_router/neutron.rb | 138 + .../neutron_router_interface/neutron.rb | 93 + .../puppet/provider/neutron_subnet/neutron.rb | 200 ++ .../ini_setting.rb | 22 + neutron/lib/puppet/type/neutron_api_config.rb | 18 + neutron/lib/puppet/type/neutron_config.rb | 18 + .../puppet/type/neutron_dhcp_agent_config.rb | 18 + .../type/neutron_fwaas_service_config.rb | 18 + .../puppet/type/neutron_l3_agent_config.rb | 18 + .../lib/puppet/type/neutron_l3_ovs_bridge.rb | 26 + .../puppet/type/neutron_lbaas_agent_config.rb | 18 + .../type/neutron_metadata_agent_config.rb | 18 + .../type/neutron_metering_agent_config.rb | 18 + neutron/lib/puppet/type/neutron_network.rb | 90 + .../lib/puppet/type/neutron_plugin_cisco.rb | 22 + .../type/neutron_plugin_cisco_credentials.rb | 22 + .../type/neutron_plugin_cisco_db_conn.rb | 22 + .../type/neutron_plugin_cisco_l2network.rb | 22 + .../puppet/type/neutron_plugin_linuxbridge.rb | 18 + neutron/lib/puppet/type/neutron_plugin_ml2.rb | 20 + neutron/lib/puppet/type/neutron_plugin_nvp.rb | 18 + neutron/lib/puppet/type/neutron_plugin_ovs.rb | 18 + neutron/lib/puppet/type/neutron_port.rb | 98 + neutron/lib/puppet/type/neutron_router.rb | 91 + .../puppet/type/neutron_router_interface.rb | 51 + neutron/lib/puppet/type/neutron_subnet.rb | 111 + .../type/neutron_vpnaas_agent_config.rb | 18 + neutron/manifests/agents/dhcp.pp | 130 + neutron/manifests/agents/l3.pp | 128 + neutron/manifests/agents/lbaas.pp | 101 + neutron/manifests/agents/linuxbridge.pp | 72 + neutron/manifests/agents/metadata.pp | 95 + neutron/manifests/agents/metering.pp | 102 + neutron/manifests/agents/ovs.pp | 153 + neutron/manifests/agents/vpnaas.pp | 111 + neutron/manifests/client.pp | 22 + neutron/manifests/config.pp | 111 + neutron/manifests/db/mysql.pp | 36 + neutron/manifests/db/mysql/host_access.pp | 16 + neutron/manifests/init.pp | 298 ++ neutron/manifests/keystone/auth.pp | 104 + neutron/manifests/params.pp | 120 + neutron/manifests/plugins/cisco.pp | 179 ++ neutron/manifests/plugins/linuxbridge.pp | 73 + neutron/manifests/plugins/ml2.pp | 177 ++ neutron/manifests/plugins/ml2/driver.pp | 90 + neutron/manifests/plugins/nvp.pp | 73 + neutron/manifests/plugins/ovs.pp | 105 + neutron/manifests/plugins/ovs/bridge.pp | 11 + neutron/manifests/plugins/ovs/port.pp | 9 + neutron/manifests/quota.pp | 88 + neutron/manifests/server.pp | 352 +++ neutron/manifests/services/fwaas.pp | 54 + .../spec/classes/neutron_agents_dhcp_spec.rb | 148 + .../spec/classes/neutron_agents_l3_spec.rb | 100 + .../spec/classes/neutron_agents_lbaas_spec.rb | 126 + .../neutron_agents_linuxbridge_spec.rb | 80 + .../classes/neutron_agents_metadata_spec.rb | 84 + .../classes/neutron_agents_metering_spec.rb | 129 + .../spec/classes/neutron_agents_ovs_spec.rb | 180 ++ .../classes/neutron_agents_vpnaas_spec.rb | 153 + neutron/spec/classes/neutron_client_spec.rb | 20 + neutron/spec/classes/neutron_db_mysql_spec.rb | 83 + neutron/spec/classes/neutron_init_spec.rb | 223 ++ .../classes/neutron_keystone_auth_spec.rb | 119 + .../classes/neutron_plugins_cisco_spec.rb | 154 + .../neutron_plugins_linuxbridge_spec.rb | 77 + .../spec/classes/neutron_plugins_ml2_spec.rb | 236 ++ .../spec/classes/neutron_plugins_nvp_spec.rb | 112 + .../spec/classes/neutron_plugins_ovs_spec.rb | 214 ++ neutron/spec/classes/neutron_quota_spec.rb | 58 + neutron/spec/classes/neutron_server_spec.rb | 292 ++ .../classes/neutron_services_fwaas_spec.rb | 72 + .../neutron_db_mysql_host_access_spec.rb | 25 + neutron/spec/shared_examples.rb | 5 + neutron/spec/spec_helper.rb | 7 + .../neutron_l3_ovs_bridge/neutron_spec.rb | 52 + .../provider/neutron_network/neutron_spec.rb | 64 + .../provider/neutron_router/neutron_spec.rb | 53 + .../neutron_router_interface/neutron_spec.rb | 36 + neutron/spec/unit/provider/neutron_spec.rb | 197 ++ .../provider/neutron_subnet/neutron_spec.rb | 80 + nova/.fixtures.yml | 23 + nova/.gitignore | 3 + nova/.gitreview | 5 + nova/Gemfile | 18 + nova/LICENSE | 201 ++ nova/Modulefile | 19 + nova/README.md | 153 + nova/Rakefile | 7 + nova/files/compute.py | 193 ++ nova/files/connection.py | 2488 +++++++++++++++++ nova/files/nova-novncproxy.init | 85 + .../puppet/provider/nova_admin/nova_manage.rb | 20 + .../puppet/provider/nova_cells/nova_manage.rb | 87 + .../provider/nova_config/ini_setting.rb | 32 + .../provider/nova_floating/nova_manage.rb | 23 + .../provider/nova_network/nova_manage.rb | 66 + .../nova_paste_api_ini/ini_setting.rb | 27 + .../provider/nova_project/nova_manage.rb | 19 + nova/lib/puppet/type/nova_admin.rb | 11 + nova/lib/puppet/type/nova_cells.rb | 115 + nova/lib/puppet/type/nova_config.rb | 55 + nova/lib/puppet/type/nova_floating.rb | 12 + nova/lib/puppet/type/nova_network.rb | 51 + nova/lib/puppet/type/nova_paste_api_ini.rb | 43 + nova/lib/puppet/type/nova_project.rb | 21 + nova/manifests/api.pp | 174 ++ nova/manifests/cells.pp | 239 ++ nova/manifests/cert.pp | 18 + nova/manifests/client.pp | 12 + nova/manifests/compute.pp | 65 + nova/manifests/compute/file_hack.pp | 31 + nova/manifests/compute/libvirt.pp | 115 + nova/manifests/compute/neutron.pp | 10 + nova/manifests/compute/rbd.pp | 85 + nova/manifests/compute/spice.pp | 65 + nova/manifests/compute/vmware.pp | 80 + nova/manifests/compute/xenserver.pp | 22 + nova/manifests/conductor.pp | 18 + nova/manifests/config.pp | 33 + nova/manifests/consoleauth.pp | 21 + nova/manifests/db/mysql.pp | 41 + nova/manifests/db/mysql/host_access.pp | 14 + nova/manifests/db/postgresql.pp | 22 + nova/manifests/generic_service.pp | 59 + nova/manifests/init.pp | 407 +++ nova/manifests/keystone/auth.pp | 69 + nova/manifests/manage/cells.pp | 89 + nova/manifests/manage/floating.pp | 13 + nova/manifests/manage/network.pp | 30 + nova/manifests/migration/libvirt.pp | 73 + nova/manifests/network.pp | 111 + nova/manifests/network/bridge.pp | 32 + nova/manifests/network/flat.pp | 23 + nova/manifests/network/flatdhcp.pp | 34 + nova/manifests/network/neutron.pp | 108 + nova/manifests/network/vlan.pp | 32 + nova/manifests/objectstore.pp | 16 + nova/manifests/params.pp | 103 + nova/manifests/qpid.pp | 35 + nova/manifests/quota.pp | 40 + nova/manifests/rabbitmq.pp | 70 + nova/manifests/scheduler.pp | 18 + nova/manifests/scheduler/filter.pp | 92 + nova/manifests/spicehtml5proxy.pp | 48 + nova/manifests/utilities.pp | 21 + nova/manifests/vncproxy.pp | 36 + nova/spec/classes/nova_api_spec.rb | 162 ++ nova/spec/classes/nova_cells_spec.rb | 159 ++ nova/spec/classes/nova_cert_spec.rb | 56 + nova/spec/classes/nova_client_spec.rb | 12 + .../spec/classes/nova_compute_libvirt_spec.rb | 181 ++ .../spec/classes/nova_compute_neutron_spec.rb | 14 + nova/spec/classes/nova_compute_rbd_spec.rb | 110 + nova/spec/classes/nova_compute_spec.rb | 104 + nova/spec/classes/nova_compute_spice_spec.rb | 23 + nova/spec/classes/nova_compute_vmware_spec.rb | 52 + .../classes/nova_compute_xenserver_spec.rb | 29 + nova/spec/classes/nova_conductor_spec.rb | 53 + nova/spec/classes/nova_config_spec.rb | 30 + nova/spec/classes/nova_consoleauth_spec.rb | 25 + nova/spec/classes/nova_db_mysql_spec.rb | 125 + nova/spec/classes/nova_db_postgresql_spec.rb | 50 + nova/spec/classes/nova_init_spec.rb | 297 ++ .../classes/nova_keystone_endpoint_spec.rb | 138 + .../classes/nova_migration_libvirt_spec.rb | 60 + nova/spec/classes/nova_network_flat_spec.rb | 37 + .../classes/nova_network_flatdhcp_spec.rb | 49 + .../spec/classes/nova_network_neutron_spec.rb | 82 + nova/spec/classes/nova_network_spec.rb | 215 ++ nova/spec/classes/nova_network_vlan_spec.rb | 47 + nova/spec/classes/nova_objectstore_spec.rb | 56 + nova/spec/classes/nova_qpid_spec.rb | 50 + nova/spec/classes/nova_quota_spec.rb | 17 + nova/spec/classes/nova_rabbitmq_spec.rb | 105 + .../classes/nova_scheduler_filter_spec.rb | 33 + nova/spec/classes/nova_scheduler_spec.rb | 56 + .../classes/nova_spicehtml5_proxy_spec.rb | 83 + nova/spec/classes/nova_utilities_spec.rb | 19 + nova/spec/classes/nova_vnc_proxy_spec.rb | 76 + .../spec/defines/nova_generic_service_spec.rb | 32 + .../spec/defines/nova_manage_networks_spec.rb | 56 + nova/spec/spec_helper.rb | 5 + .../provider/nova_config/ini_setting_spec.rb | 38 + nova/spec/unit/type/nova_config_spec.rb | 49 + nova/spec/unit/type/nova_network_spec.rb | 11 + nova/templates/secret.xml-compute.erb | 6 + nova/tests/all.pp | 215 ++ nova/tests/multi.pp | 233 ++ nova/tests/site.pp | 173 ++ nssdb/CHANGELOG | 2 + nssdb/LICENSE | 13 + nssdb/Modulefile | 10 + nssdb/README.md | 4 + nssdb/manifests/add_cert_and_key.pp | 57 + nssdb/manifests/create.pp | 92 + nssdb/tests/create.pp | 27 + openstack/.fixtures.yml | 50 + openstack/.gitignore | 6 + openstack/.gitreview | 5 + openstack/.mailmap | 4 + openstack/.travis.yml | 33 + openstack/Gemfile | 15 + openstack/LICENSE | 201 ++ openstack/Modulefile | 18 + openstack/Puppetfile | 51 + openstack/README.md | 585 ++++ openstack/Rakefile | 5 + openstack/files/RPM-GPG-KEY-EPEL-6 | 31 + openstack/files/RPM-GPG-KEY-RDO-Grizzly | 52 + openstack/files/RPM-GPG-KEY-RDO-Havana | 52 + openstack/manifests/all.pp | 557 ++++ openstack/manifests/auth_file.pp | 58 + openstack/manifests/cinder/all.pp | 116 + openstack/manifests/cinder/controller.pp | 83 + openstack/manifests/cinder/storage.pp | 69 + openstack/manifests/client.pp | 57 + openstack/manifests/compute.pp | 290 ++ openstack/manifests/controller.pp | 617 ++++ openstack/manifests/db/mysql.pp | 179 ++ openstack/manifests/glance.pp | 151 + openstack/manifests/horizon.pp | 110 + openstack/manifests/keystone.pp | 431 +++ openstack/manifests/neutron.pp | 289 ++ openstack/manifests/nova/controller.pp | 268 ++ openstack/manifests/provision.pp | 252 ++ openstack/manifests/repo.pp | 27 + openstack/manifests/repo/epel.pp | 26 + openstack/manifests/repo/rdo.pp | 35 + openstack/manifests/repo/uca.pp | 19 + openstack/manifests/repo/yum_refresh.pp | 8 + openstack/manifests/swift/device_endpoint.pp | 17 + openstack/manifests/swift/proxy.pp | 119 + openstack/manifests/swift/storage-node.pp | 56 + openstack/manifests/test_file.pp | 30 + openstack/spec/classes/openstack_all_spec.rb | 525 ++++ .../spec/classes/openstack_auth_file_spec.rb | 82 + .../spec/classes/openstack_cinder_all_spec.rb | 142 + .../openstack_cinder_controller_spec.rb | 81 + .../classes/openstack_cinder_storage_spec.rb | 87 + .../spec/classes/openstack_client_spec.rb | 47 + .../spec/classes/openstack_compute_spec.rb | 332 +++ .../spec/classes/openstack_controller_spec.rb | 896 ++++++ .../spec/classes/openstack_glance_spec.rb | 177 ++ .../spec/classes/openstack_horizon_spec.rb | 77 + .../spec/classes/openstack_keystone_spec.rb | 192 ++ .../spec/classes/openstack_neutron_spec.rb | 153 + .../classes/openstack_nova_controller_spec.rb | 131 + .../spec/classes/openstack_provision_spec.rb | 101 + openstack/spec/classes/openstack_repo_spec.rb | 135 + .../spec/classes/openstack_repo_uca_spec.rb | 47 + .../classes/openstack_swift_storage-node.rb | 51 + .../spec/classes/openstack_test_file_spec.rb | 9 + openstack/spec/spec_helper.rb | 1 + openstack/templates/openrc.erb | 17 + openstack/templates/test_nova.sh.erb | 108 + openstack/tests/all.pp | 20 + openstack/tests/auth_file.pp | 5 + openstack/tests/cloudcontroller.pp | 20 + openstack/tests/compute.pp | 12 + openstack/tests/site.pp | 162 ++ openstack/tests/testfile.pp | 1 + pacemaker/LICENSE | 201 ++ pacemaker/Modulefile | 13 + pacemaker/README.md | 87 + .../provider/pcmk_constraint/default.rb | 64 + .../puppet/provider/pcmk_resource/default.rb | 118 + pacemaker/lib/puppet/type/pcmk_constraint.rb | 26 + pacemaker/lib/puppet/type/pcmk_resource.rb | 28 + pacemaker/manifests/constraint/base.pp | 65 + pacemaker/manifests/constraint/colocation.pp | 14 + pacemaker/manifests/constraint/location.pp | 14 + pacemaker/manifests/corosync.pp | 40 + pacemaker/manifests/init.pp | 38 + pacemaker/manifests/install.pp | 8 + pacemaker/manifests/params.pp | 17 + pacemaker/manifests/resource/filesystem.pp | 24 + pacemaker/manifests/resource/ip.pp | 21 + pacemaker/manifests/resource/lsb.pp | 15 + pacemaker/manifests/resource/mysql.pp | 28 + .../manifests/resource/qpid_clustered.pp | 50 + pacemaker/manifests/service.pp | 16 + pacemaker/manifests/stonith.pp | 15 + pacemaker/manifests/stonith/fence_xvm.pp | 54 + pacemaker/manifests/stonith/ipmilan.pp | 38 + pacemaker/spec/spec_helper.rb | 17 + pacemaker/tests/init.pp | 88 + puppet/.gitignore | 3 + puppet/COPYING | 661 +++++ puppet/COPYRIGHT | 16 + puppet/INSTALL | 18 + puppet/Makefile | 146 + puppet/README | 26 + puppet/VERSION | 1 + puppet/lib/facter/puppet_bug.rb | 28 + puppet/lib/facter/puppet_vardirtmp.rb | 33 + puppet/manifests/client.pp | 45 + puppet/manifests/common.pp | 122 + puppet/manifests/deploy.pp | 273 ++ puppet/manifests/init.pp | 34 + puppet/manifests/params.pp | 24 + puppet/manifests/params/package.pp | 35 + puppet/manifests/params/path.pp | 39 + puppet/manifests/params/repo.pp | 33 + puppet/manifests/params/repo/apt.pp | 32 + puppet/manifests/params/repo/yum.pp | 55 + puppet/manifests/puppetdb.pp | 123 + puppet/manifests/server.pp | 190 ++ puppet/manifests/vardir.pp | 50 + puppet/puppet-puppet.spec.in | 40 + puppet/templates/auth.conf.erb | 116 + puppet/templates/autosign.conf.erb | 5 + puppet/templates/fileserver.conf.erb | 75 + puppet/templates/puppet.conf.erb | 52 + puppet/templates/puppetdb.conf.erb | 4 + puppet/templates/routes.yaml.erb | 6 + qpid/CHANGELOG | 9 + qpid/LICENSE | 201 ++ qpid/Modulefile | 10 + qpid/README | 32 + qpid/lib/puppet/provider/qpid_user/default.rb | 22 + .../puppet/provider/qpid_user/saslpasswd2.rb | 38 + qpid/lib/puppet/type/qpid_user.rb | 39 + qpid/manifests/server.pp | 126 + qpid/templates/qpidd.conf.erb | 27 + rsync/.fixtures.yml | 5 + rsync/.gemfile | 5 + rsync/.gitignore | 2 + rsync/CHANGELOG | 2 + rsync/LICENSE | 17 + rsync/Modulefile | 9 + rsync/README.markdown | 59 + rsync/Rakefile | 2 + rsync/files/motd | 9 + rsync/manifests/get.pp | 75 + rsync/manifests/init.pp | 10 + rsync/manifests/repo.pp | 23 + rsync/manifests/server.pp | 59 + rsync/manifests/server/module.pp | 56 + rsync/spec/classes/server_spec.rb | 61 + rsync/spec/defines/get_spec.rb | 117 + rsync/spec/defines/server_module_spec.rb | 79 + rsync/spec/fixtures/manifests/site.pp | 0 rsync/spec/spec.opts | 6 + rsync/spec/spec_helper.rb | 1 + rsync/templates/header.erb | 12 + rsync/templates/module.erb | 21 + rsync/tests/init.pp | 1 + rsync/tests/repo.pp | 1 + rsync/tests/server_with_motd.pp | 3 + ssh/.gitignore | 3 + ssh/LICENSE | 13 + ssh/Modulefile | 8 + ssh/README.markdown | 28 + ssh/files/ssh_config | 54 + ssh/files/sshd_config | 77 + .../puppet/parser/functions/ipaddresses.rb | 40 + ssh/manifests/client.pp | 6 + ssh/manifests/client/config.pp | 14 + ssh/manifests/client/install.pp | 5 + ssh/manifests/hostkeys.pp | 14 + ssh/manifests/init.pp | 4 + ssh/manifests/knownhosts.pp | 5 + ssh/manifests/params.pp | 27 + ssh/manifests/server.pp | 8 + ssh/manifests/server/config.pp | 12 + ssh/manifests/server/configline.pp | 39 + ssh/manifests/server/install.pp | 6 + ssh/manifests/server/service.pp | 12 + ssh/spec/spec.opts | 6 + ssh/spec/spec_helper.rb | 18 + ssh/tests/init.pp | 1 + stdlib/.gemspec | 40 + stdlib/.gitignore | 8 + stdlib/.project | 23 + stdlib/.rspec | 4 + stdlib/.travis.yml | 25 + stdlib/CHANGELOG | 278 ++ stdlib/CONTRIBUTING.md | 65 + stdlib/Gemfile | 42 + stdlib/LICENSE | 19 + stdlib/Modulefile | 11 + stdlib/README.markdown | 1236 ++++++++ stdlib/README_DEVELOPER.markdown | 35 + stdlib/README_SPECS.markdown | 7 + stdlib/RELEASE_PROCESS.markdown | 24 + stdlib/Rakefile | 2 + stdlib/lib/facter/facter_dot_d.rb | 202 ++ stdlib/lib/facter/pe_version.rb | 53 + stdlib/lib/facter/puppet_vardir.rb | 26 + stdlib/lib/facter/root_home.rb | 19 + stdlib/lib/facter/util/puppet_settings.rb | 21 + stdlib/lib/puppet/parser/functions/abs.rb | 36 + .../lib/puppet/parser/functions/any2array.rb | 33 + stdlib/lib/puppet/parser/functions/base64.rb | 37 + .../lib/puppet/parser/functions/bool2num.rb | 49 + .../lib/puppet/parser/functions/capitalize.rb | 34 + stdlib/lib/puppet/parser/functions/chomp.rb | 35 + stdlib/lib/puppet/parser/functions/chop.rb | 37 + stdlib/lib/puppet/parser/functions/concat.rb | 37 + stdlib/lib/puppet/parser/functions/count.rb | 22 + .../parser/functions/defined_with_params.rb | 35 + stdlib/lib/puppet/parser/functions/delete.rb | 46 + .../lib/puppet/parser/functions/delete_at.rb | 49 + .../lib/puppet/parser/functions/difference.rb | 36 + stdlib/lib/puppet/parser/functions/dirname.rb | 15 + .../lib/puppet/parser/functions/downcase.rb | 33 + stdlib/lib/puppet/parser/functions/empty.rb | 28 + .../parser/functions/ensure_packages.rb | 24 + .../parser/functions/ensure_resource.rb | 45 + stdlib/lib/puppet/parser/functions/flatten.rb | 33 + stdlib/lib/puppet/parser/functions/floor.rb | 20 + .../puppet/parser/functions/fqdn_rotate.rb | 46 + .../parser/functions/get_module_path.rb | 17 + .../lib/puppet/parser/functions/getparam.rb | 35 + stdlib/lib/puppet/parser/functions/getvar.rb | 26 + stdlib/lib/puppet/parser/functions/grep.rb | 33 + .../parser/functions/has_interface_with.rb | 52 + .../puppet/parser/functions/has_ip_address.rb | 25 + .../puppet/parser/functions/has_ip_network.rb | 25 + stdlib/lib/puppet/parser/functions/has_key.rb | 28 + stdlib/lib/puppet/parser/functions/hash.rb | 41 + .../puppet/parser/functions/intersection.rb | 34 + .../lib/puppet/parser/functions/is_array.rb | 22 + .../puppet/parser/functions/is_domain_name.rb | 47 + .../lib/puppet/parser/functions/is_float.rb | 27 + .../parser/functions/is_function_available.rb | 23 + stdlib/lib/puppet/parser/functions/is_hash.rb | 22 + .../lib/puppet/parser/functions/is_integer.rb | 27 + .../puppet/parser/functions/is_ip_address.rb | 32 + .../puppet/parser/functions/is_mac_address.rb | 27 + .../lib/puppet/parser/functions/is_numeric.rb | 27 + .../lib/puppet/parser/functions/is_string.rb | 26 + stdlib/lib/puppet/parser/functions/join.rb | 41 + .../parser/functions/join_keys_to_values.rb | 47 + stdlib/lib/puppet/parser/functions/keys.rb | 26 + .../lib/puppet/parser/functions/loadyaml.rb | 20 + stdlib/lib/puppet/parser/functions/lstrip.rb | 33 + stdlib/lib/puppet/parser/functions/max.rb | 21 + stdlib/lib/puppet/parser/functions/member.rb | 44 + stdlib/lib/puppet/parser/functions/merge.rb | 34 + stdlib/lib/puppet/parser/functions/min.rb | 21 + .../lib/puppet/parser/functions/num2bool.rb | 43 + .../lib/puppet/parser/functions/parsejson.rb | 24 + .../lib/puppet/parser/functions/parseyaml.rb | 24 + stdlib/lib/puppet/parser/functions/pick.rb | 29 + stdlib/lib/puppet/parser/functions/prefix.rb | 45 + stdlib/lib/puppet/parser/functions/range.rb | 88 + stdlib/lib/puppet/parser/functions/reject.rb | 31 + stdlib/lib/puppet/parser/functions/reverse.rb | 28 + stdlib/lib/puppet/parser/functions/rstrip.rb | 32 + stdlib/lib/puppet/parser/functions/shuffle.rb | 46 + stdlib/lib/puppet/parser/functions/size.rb | 48 + stdlib/lib/puppet/parser/functions/sort.rb | 27 + stdlib/lib/puppet/parser/functions/squeeze.rb | 36 + .../lib/puppet/parser/functions/str2bool.rb | 46 + .../parser/functions/str2saltedsha512.rb | 32 + .../lib/puppet/parser/functions/strftime.rb | 107 + stdlib/lib/puppet/parser/functions/strip.rb | 39 + stdlib/lib/puppet/parser/functions/suffix.rb | 45 + .../lib/puppet/parser/functions/swapcase.rb | 39 + stdlib/lib/puppet/parser/functions/time.rb | 49 + .../lib/puppet/parser/functions/to_bytes.rb | 28 + stdlib/lib/puppet/parser/functions/type.rb | 50 + stdlib/lib/puppet/parser/functions/union.rb | 34 + stdlib/lib/puppet/parser/functions/unique.rb | 51 + stdlib/lib/puppet/parser/functions/upcase.rb | 41 + .../lib/puppet/parser/functions/uriescape.rb | 36 + .../functions/validate_absolute_path.rb | 56 + .../puppet/parser/functions/validate_array.rb | 33 + .../parser/functions/validate_augeas.rb | 81 + .../puppet/parser/functions/validate_bool.rb | 34 + .../puppet/parser/functions/validate_cmd.rb | 47 + .../puppet/parser/functions/validate_hash.rb | 33 + .../parser/functions/validate_ipv4_address.rb | 48 + .../parser/functions/validate_ipv6_address.rb | 49 + .../puppet/parser/functions/validate_re.rb | 40 + .../parser/functions/validate_slength.rb | 52 + .../parser/functions/validate_string.rb | 33 + stdlib/lib/puppet/parser/functions/values.rb | 39 + .../lib/puppet/parser/functions/values_at.rb | 98 + stdlib/lib/puppet/parser/functions/zip.rb | 65 + stdlib/lib/puppet/provider/file_line/ruby.rb | 59 + stdlib/lib/puppet/type/anchor.rb | 46 + stdlib/lib/puppet/type/file_line.rb | 70 + stdlib/manifests/init.pp | 20 + stdlib/manifests/stages.pp | 43 + stdlib/spec/classes/anchor_spec.rb | 32 + .../functions/defined_with_params_spec.rb | 37 + stdlib/spec/functions/ensure_packages_spec.rb | 42 + stdlib/spec/functions/ensure_resource_spec.rb | 64 + stdlib/spec/functions/getparam_spec.rb | 34 + .../monkey_patches/alias_should_to_must.rb | 8 + .../spec/monkey_patches/publicize_methods.rb | 10 + stdlib/spec/spec.opts | 6 + stdlib/spec/spec_helper.rb | 28 + .../unit/facter/pe_required_facts_spec.rb | 69 + stdlib/spec/unit/facter/pe_version_spec.rb | 76 + stdlib/spec/unit/facter/root_home_spec.rb | 40 + .../unit/facter/util/puppet_settings_spec.rb | 35 + .../unit/puppet/parser/functions/abs_spec.rb | 25 + .../puppet/parser/functions/any2array_spec.rb | 55 + .../puppet/parser/functions/base64_spec.rb | 34 + .../puppet/parser/functions/bool2num_spec.rb | 24 + .../parser/functions/capitalize_spec.rb | 19 + .../puppet/parser/functions/chomp_spec.rb | 19 + .../unit/puppet/parser/functions/chop_spec.rb | 19 + .../puppet/parser/functions/concat_spec.rb | 15 + .../puppet/parser/functions/count_spec.rb | 31 + .../puppet/parser/functions/delete_at_spec.rb | 19 + .../puppet/parser/functions/delete_spec.rb | 38 + .../parser/functions/difference_spec.rb | 19 + .../puppet/parser/functions/dirname_spec.rb | 24 + .../puppet/parser/functions/downcase_spec.rb | 24 + .../puppet/parser/functions/empty_spec.rb | 23 + .../puppet/parser/functions/flatten_spec.rb | 27 + .../puppet/parser/functions/floor_spec.rb | 39 + .../parser/functions/fqdn_rotate_spec.rb | 33 + .../parser/functions/get_module_path_spec.rb | 46 + .../puppet/parser/functions/getvar_spec.rb | 37 + .../unit/puppet/parser/functions/grep_spec.rb | 19 + .../functions/has_interface_with_spec.rb | 64 + .../parser/functions/has_ip_address_spec.rb | 39 + .../parser/functions/has_ip_network_spec.rb | 36 + .../puppet/parser/functions/has_key_spec.rb | 42 + .../unit/puppet/parser/functions/hash_spec.rb | 19 + .../parser/functions/intersection_spec.rb | 19 + .../puppet/parser/functions/is_array_spec.rb | 29 + .../parser/functions/is_domain_name_spec.rb | 64 + .../puppet/parser/functions/is_float_spec.rb | 33 + .../parser/functions/is_function_available.rb | 31 + .../puppet/parser/functions/is_hash_spec.rb | 29 + .../parser/functions/is_integer_spec.rb | 34 + .../parser/functions/is_ip_address_spec.rb | 39 + .../parser/functions/is_mac_address_spec.rb | 29 + .../parser/functions/is_numeric_spec.rb | 39 + .../puppet/parser/functions/is_string_spec.rb | 34 + .../functions/join_keys_to_values_spec.rb | 40 + .../unit/puppet/parser/functions/join_spec.rb | 19 + .../unit/puppet/parser/functions/keys_spec.rb | 21 + .../puppet/parser/functions/lstrip_spec.rb | 19 + .../unit/puppet/parser/functions/max_spec.rb | 27 + .../puppet/parser/functions/member_spec.rb | 24 + .../puppet/parser/functions/merge_spec.rb | 52 + .../unit/puppet/parser/functions/min_spec.rb | 27 + .../puppet/parser/functions/num2bool_spec.rb | 67 + .../puppet/parser/functions/parsejson_spec.rb | 22 + .../puppet/parser/functions/parseyaml_spec.rb | 24 + .../unit/puppet/parser/functions/pick_spec.rb | 34 + .../puppet/parser/functions/prefix_spec.rb | 19 + .../puppet/parser/functions/range_spec.rb | 70 + .../puppet/parser/functions/reject_spec.rb | 20 + .../puppet/parser/functions/reverse_spec.rb | 19 + .../puppet/parser/functions/rstrip_spec.rb | 24 + .../puppet/parser/functions/shuffle_spec.rb | 24 + .../unit/puppet/parser/functions/size_spec.rb | 24 + .../unit/puppet/parser/functions/sort_spec.rb | 24 + .../puppet/parser/functions/squeeze_spec.rb | 24 + .../puppet/parser/functions/str2bool_spec.rb | 31 + .../parser/functions/str2saltedsha512_spec.rb | 45 + .../puppet/parser/functions/strftime_spec.rb | 29 + .../puppet/parser/functions/strip_spec.rb | 18 + .../puppet/parser/functions/suffix_spec.rb | 19 + .../puppet/parser/functions/swapcase_spec.rb | 19 + .../unit/puppet/parser/functions/time_spec.rb | 29 + .../puppet/parser/functions/to_bytes_spec.rb | 58 + .../unit/puppet/parser/functions/type_spec.rb | 43 + .../puppet/parser/functions/union_spec.rb | 19 + .../puppet/parser/functions/unique_spec.rb | 24 + .../puppet/parser/functions/upcase_spec.rb | 24 + .../puppet/parser/functions/uriescape_spec.rb | 24 + .../functions/validate_absolute_path_spec.rb | 83 + .../parser/functions/validate_array_spec.rb | 38 + .../parser/functions/validate_augeas_spec.rb | 102 + .../parser/functions/validate_bool_spec.rb | 51 + .../parser/functions/validate_cmd_spec.rb | 81 + .../parser/functions/validate_hash_spec.rb | 43 + .../functions/validate_ipv4_address_spec.rb | 64 + .../functions/validate_ipv6_address_spec.rb | 67 + .../parser/functions/validate_re_spec.rb | 76 + .../parser/functions/validate_slength_spec.rb | 48 + .../parser/functions/validate_string_spec.rb | 60 + .../puppet/parser/functions/values_at_spec.rb | 38 + .../puppet/parser/functions/values_spec.rb | 31 + .../unit/puppet/parser/functions/zip_spec.rb | 15 + .../puppet/provider/file_line/ruby_spec.rb | 127 + stdlib/spec/unit/puppet/type/anchor_spec.rb | 11 + .../spec/unit/puppet/type/file_line_spec.rb | 69 + stdlib/spec/watchr.rb | 86 + stdlib/tests/file_line.pp | 9 + stdlib/tests/has_interface_with.pp | 10 + stdlib/tests/has_ip_address.pp | 3 + stdlib/tests/has_ip_network.pp | 4 + stdlib/tests/init.pp | 1 + swift/.fixtures.yml | 19 + swift/.gemfile | 5 + swift/.gitignore | 9 + swift/.gitreview | 5 + swift/Gemfile | 15 + swift/LICENSE | 17 + swift/Modulefile | 19 + swift/README.md | 279 ++ swift/Rakefile | 5 + swift/ext/swift.rb | 117 + swift/ext/swift_keystone_test.rb | 115 + swift/files/swift-account.conf.upstart | 23 + swift/files/swift-container-sync.conf.upstart | 20 + swift/files/swift-container.conf.upstart | 23 + swift/files/swift-object.conf.upstart | 23 + swift/files/swift_tester.rb | 118 + .../ring_account_device/swift_ring_builder.rb | 23 + .../swift_ring_builder.rb | 23 + .../ring_object_device/swift_ring_builder.rb | 23 + .../swift_account_config/ini_setting.rb | 27 + .../swift_bench_config/ini_setting.rb | 27 + .../provider/swift_config/ini_setting.rb | 27 + .../swift_container_config/ini_setting.rb | 27 + .../swift_dispersion_config/ini_setting.rb | 27 + .../swift_object_config/ini_setting.rb | 27 + .../swift_proxy_config/ini_setting.rb | 27 + .../lib/puppet/provider/swift_ring_builder.rb | 155 + swift/lib/puppet/type/ring_account_device.rb | 34 + .../lib/puppet/type/ring_container_device.rb | 34 + swift/lib/puppet/type/ring_object_device.rb | 34 + swift/lib/puppet/type/swift_account_config.rb | 40 + swift/lib/puppet/type/swift_bench_config.rb | 40 + swift/lib/puppet/type/swift_config.rb | 46 + .../lib/puppet/type/swift_container_config.rb | 40 + .../puppet/type/swift_dispersion_config.rb | 40 + swift/lib/puppet/type/swift_object_config.rb | 40 + swift/lib/puppet/type/swift_proxy_config.rb | 40 + swift/manifests/auth_file.pp | 20 + swift/manifests/bench.pp | 41 + swift/manifests/client.pp | 22 + swift/manifests/dispersion.pp | 109 + swift/manifests/init.pp | 78 + swift/manifests/keystone/auth.pp | 103 + swift/manifests/keystone/dispersion.pp | 44 + swift/manifests/params.pp | 57 + swift/manifests/proxy.pp | 123 + swift/manifests/proxy/account_quotas.pp | 27 + swift/manifests/proxy/authtoken.pp | 83 + swift/manifests/proxy/bulk.pp | 52 + swift/manifests/proxy/cache.pp | 36 + swift/manifests/proxy/catch_errors.pp | 27 + swift/manifests/proxy/ceilometer.pp | 33 + swift/manifests/proxy/container_quotas.pp | 27 + swift/manifests/proxy/formpost.pp | 27 + swift/manifests/proxy/healthcheck.pp | 24 + swift/manifests/proxy/keystone.pp | 31 + swift/manifests/proxy/proxy-logging.pp | 5 + swift/manifests/proxy/proxy_logging.pp | 15 + swift/manifests/proxy/ratelimit.pp | 46 + swift/manifests/proxy/s3token.pp | 37 + swift/manifests/proxy/staticweb.pp | 27 + swift/manifests/proxy/swauth.pp | 20 + swift/manifests/proxy/swift3.pp | 34 + swift/manifests/proxy/tempauth.pp | 9 + swift/manifests/proxy/tempurl.pp | 27 + swift/manifests/ringbuilder.pp | 49 + swift/manifests/ringbuilder/create.pp | 45 + swift/manifests/ringbuilder/rebalance.pp | 18 + swift/manifests/ringserver.pp | 40 + swift/manifests/ringsync.pp | 10 + swift/manifests/storage.pp | 33 + swift/manifests/storage/account.pp | 25 + swift/manifests/storage/all.pp | 65 + swift/manifests/storage/container.pp | 50 + swift/manifests/storage/disk.pp | 63 + swift/manifests/storage/ext4.pp | 36 + swift/manifests/storage/filter/healthcheck.pp | 26 + swift/manifests/storage/filter/recon.pp | 31 + swift/manifests/storage/generic.pp | 63 + swift/manifests/storage/loopback.pp | 49 + swift/manifests/storage/mount.pp | 57 + swift/manifests/storage/node.pp | 55 + swift/manifests/storage/object.pp | 25 + swift/manifests/storage/server.pp | 83 + swift/manifests/storage/xfs.pp | 59 + swift/manifests/test_file.pp | 43 + swift/manifests/xfs.pp | 15 + swift/spec/classes/swift_bench_spec.rb | 96 + swift/spec/classes/swift_client_spec.rb | 20 + swift/spec/classes/swift_dispersion_spec.rb | 103 + .../spec/classes/swift_keystone_auth_spec.rb | 179 ++ .../classes/swift_keystone_dispersion_spec.rb | 43 + .../swift_proxy_account_quotas_spec.rb | 43 + .../classes/swift_proxy_authtoken_spec.rb | 152 + swift/spec/classes/swift_proxy_bulk_spec.rb | 78 + swift/spec/classes/swift_proxy_cache_spec.rb | 53 + .../classes/swift_proxy_catch_errors_spec.rb | 23 + .../classes/swift_proxy_ceilometer_spec.rb | 30 + .../swift_proxy_container_quotas_spec.rb | 43 + .../spec/classes/swift_proxy_formpost_spec.rb | 23 + .../classes/swift_proxy_healthcheck_spec.rb | 23 + .../spec/classes/swift_proxy_keystone_spec.rb | 48 + .../classes/swift_proxy_ratelimit_spec.rb | 61 + .../spec/classes/swift_proxy_s3token_spec.rb | 57 + swift/spec/classes/swift_proxy_spec.rb | 137 + .../classes/swift_proxy_staticweb_spec.rb | 23 + swift/spec/classes/swift_proxy_swauth_spec.rb | 47 + swift/spec/classes/swift_proxy_swift3_spec.rb | 23 + .../spec/classes/swift_proxy_tempurl_spec.rb | 23 + swift/spec/classes/swift_ringbuilder_spec.rb | 100 + swift/spec/classes/swift_spec.rb | 74 + .../classes/swift_storage_account_spec.rb | 52 + swift/spec/classes/swift_storage_all_spec.rb | 162 ++ .../classes/swift_storage_container_spec.rb | 103 + .../spec/classes/swift_storage_object_spec.rb | 52 + swift/spec/classes/swift_storage_spec.rb | 45 + swift/spec/classes/swift_test_file_spec.rb | 49 + swift/spec/classes/swift_xfs_spec.rb | 6 + .../defines/swift_ringbuilder_create_spec.rb | 51 + .../swift_ringbuilder_rebalance_spec.rb | 26 + swift/spec/defines/swift_storage_disk_spec.rb | 26 + .../swift_storage_filter_healthcheck_spec.rb | 32 + .../swift_storage_filter_recon_spec.rb | 52 + .../defines/swift_storage_generic_spec.rb | 74 + .../defines/swift_storage_loopback_spec.rb | 18 + .../spec/defines/swift_storage_mount_spec.rb | 42 + swift/spec/defines/swift_storage_node_spec.rb | 48 + .../spec/defines/swift_storage_server_spec.rb | 175 ++ swift/spec/defines/swift_storage_xfs_spec.rb | 51 + swift/spec/spec.opts | 6 + swift/spec/spec_helper.rb | 1 + .../provider/swift_ring_builder_spec.rb | 97 + .../puppet/type/ring_account_device_spec.rb | 15 + .../puppet/type/ring_container_device_spec.rb | 16 + .../puppet/type/ring_object_device_spec.rb | 15 + swift/templates/account-server.conf.erb | 28 + swift/templates/container-server.conf.erb | 31 + swift/templates/object-server.conf.erb | 27 + swift/templates/proxy-server.conf.erb | 34 + swift/templates/proxy/account_quotas.conf.erb | 4 + swift/templates/proxy/authtoken.conf.erb | 22 + swift/templates/proxy/bulk.conf.erb | 8 + swift/templates/proxy/cache.conf.erb | 3 + swift/templates/proxy/catch_errors.conf.erb | 3 + swift/templates/proxy/ceilometer.conf.erb | 3 + .../templates/proxy/container_quotas.conf.erb | 4 + swift/templates/proxy/formpost.conf.erb | 4 + swift/templates/proxy/healthcheck.conf.erb | 4 + swift/templates/proxy/keystone.conf.erb | 5 + swift/templates/proxy/proxy-logging.conf.erb | 3 + swift/templates/proxy/ratelimit.conf.erb | 8 + swift/templates/proxy/s3token.conf.erb | 5 + swift/templates/proxy/staticweb.conf.erb | 4 + swift/templates/proxy/swauth.conf.erb | 7 + swift/templates/proxy/swift3.conf.erb | 3 + swift/templates/proxy/tempauth.conf.erb | 8 + swift/templates/proxy/tempurl.conf.erb | 4 + swift/templates/recon.conf.erb | 3 + swift/templates/rsyncd.conf.erb | 25 + swift/templates/swift_keystone_test.erb | 115 + swift/tests/all.pp | 62 + swift/tests/site.pp | 273 ++ sysctl/README | 18 + sysctl/lib/puppet/provider/sysctl/parsed.rb | 17 + sysctl/lib/puppet/type/sysctl.rb | 42 + sysctl/manifests/value.pp | 14 + tempest/.gitignore | 5 + tempest/.gitreview | 5 + tempest/Gemfile | 14 + tempest/Modulefile | 11 + tempest/README.markdown | 7 + tempest/Rakefile | 6 + .../provider/tempest_config/ini_setting.rb | 18 + .../provider/tempest_glance_id_setter/ruby.rb | 55 + .../tempest_neutron_net_id_setter/ruby.rb | 56 + tempest/lib/puppet/type/tempest_config.rb | 28 + .../puppet/type/tempest_glance_id_setter.rb | 23 + .../type/tempest_neutron_net_id_setter.rb | 17 + tempest/manifests/init.pp | 205 ++ tempest/manifests/params.pp | 42 + vcsrepo/.gitignore | 4 + vcsrepo/.travis.yml | 34 + vcsrepo/Gemfile | 5 + vcsrepo/LICENSE | 17 + vcsrepo/Modulefile | 4 + vcsrepo/README.BZR.markdown | 47 + vcsrepo/README.CVS.markdown | 56 + vcsrepo/README.GIT.markdown | 95 + vcsrepo/README.HG.markdown | 73 + vcsrepo/README.SVN.markdown | 62 + vcsrepo/README.markdown | 32 + vcsrepo/Rakefile | 13 + vcsrepo/examples/bzr/branch.pp | 6 + vcsrepo/examples/bzr/init_repo.pp | 4 + vcsrepo/examples/cvs/local.pp | 11 + vcsrepo/examples/cvs/remote.pp | 5 + vcsrepo/examples/git/bare_init.pp | 4 + vcsrepo/examples/git/clone.pp | 5 + vcsrepo/examples/git/working_copy_init.pp | 4 + vcsrepo/examples/hg/clone.pp | 6 + vcsrepo/examples/hg/init_repo.pp | 4 + vcsrepo/examples/svn/checkout.pp | 5 + vcsrepo/examples/svn/server.pp | 4 + vcsrepo/lib/puppet/provider/vcsrepo.rb | 34 + vcsrepo/lib/puppet/provider/vcsrepo/bzr.rb | 85 + vcsrepo/lib/puppet/provider/vcsrepo/cvs.rb | 137 + vcsrepo/lib/puppet/provider/vcsrepo/dummy.rb | 12 + vcsrepo/lib/puppet/provider/vcsrepo/git.rb | 323 +++ vcsrepo/lib/puppet/provider/vcsrepo/hg.rb | 115 + vcsrepo/lib/puppet/provider/vcsrepo/svn.rb | 124 + vcsrepo/lib/puppet/type/vcsrepo.rb | 194 ++ vcsrepo/spec/fixtures/bzr_version_info.txt | 5 + vcsrepo/spec/fixtures/git_branch_a.txt | 14 + vcsrepo/spec/fixtures/hg_parents.txt | 6 + vcsrepo/spec/fixtures/hg_tags.txt | 18 + vcsrepo/spec/fixtures/svn_info.txt | 10 + vcsrepo/spec/spec.opts | 6 + vcsrepo/spec/spec_helper.rb | 25 + vcsrepo/spec/support/filesystem_helpers.rb | 18 + vcsrepo/spec/support/fixture_helpers.rb | 7 + .../spec/support/provider_example_group.rb | 80 + .../unit/puppet/provider/vcsrepo/bzr_spec.rb | 90 + .../unit/puppet/provider/vcsrepo/cvs_spec.rb | 100 + .../puppet/provider/vcsrepo/dummy_spec.rb | 17 + .../unit/puppet/provider/vcsrepo/git_spec.rb | 269 ++ .../unit/puppet/provider/vcsrepo/hg_spec.rb | 104 + .../unit/puppet/provider/vcsrepo/svn_spec.rb | 94 + vcsrepo/spec/unit/puppet/type/README.markdown | 4 + vlan/LICENSE | 13 + vlan/README | 3 + vlan/manifests/init.pp | 19 + vlan/templates/ifcfg.erb | 5 + vswitch/.fixtures.yml | 3 + vswitch/.gitignore | 4 + vswitch/.gitreview | 4 + vswitch/Gemfile | 14 + vswitch/Gemfile.lock | 41 + vswitch/LICENSE | 176 ++ vswitch/Modulefile | 8 + vswitch/README.md | 34 + vswitch/Rakefile | 7 + vswitch/lib/puppet/provider/vs_bridge/ovs.rb | 42 + .../puppet/provider/vs_bridge/ovs_redhat.rb | 51 + vswitch/lib/puppet/provider/vs_port/ovs.rb | 17 + .../lib/puppet/provider/vs_port/ovs_redhat.rb | 105 + vswitch/lib/puppet/type/vs_bridge.rb | 19 + vswitch/lib/puppet/type/vs_port.rb | 45 + vswitch/manifests/init.pp | 32 + vswitch/manifests/ovs.pp | 51 + vswitch/manifests/params.pp | 19 + vswitch/spec/classes/vswitch_ovs_spec.rb | 27 + vswitch/spec/spec_helper.rb | 1 + xinetd/.fixtures.yml | 3 + xinetd/.gemfile | 5 + xinetd/.gitignore | 4 + xinetd/CHANGELOG | 9 + xinetd/LICENSE | 17 + xinetd/Modulefile | 8 + xinetd/README | 43 + xinetd/Rakefile | 2 + xinetd/files/xinetd.conf | 52 + xinetd/manifests/init.pp | 27 + xinetd/manifests/params.pp | 8 + xinetd/manifests/service.pp | 86 + xinetd/spec/classes/xinetd_init_spec.rb | 9 + xinetd/spec/defines/xinetd_service_spec.rb | 41 + xinetd/spec/fixtures/manifests/site.pp | 0 xinetd/spec/spec.opts | 6 + xinetd/spec/spec_helper.rb | 1 + xinetd/templates/service.erb | 21 + xinetd/tests/init.pp | 1 + 1796 files changed, 116322 insertions(+) create mode 100644 apache/.fixtures.yml create mode 100644 apache/.gitignore create mode 100644 apache/.nodeset.yml create mode 100644 apache/.travis.yml create mode 100644 apache/CHANGELOG.md create mode 100644 apache/CONTRIBUTING.md create mode 100644 apache/Gemfile create mode 100644 apache/LICENSE create mode 100644 apache/Modulefile create mode 100644 apache/README.md create mode 100644 apache/README.passenger.md create mode 100644 apache/Rakefile create mode 100644 apache/files/httpd create mode 100644 apache/lib/puppet/provider/a2mod.rb create mode 100644 apache/lib/puppet/provider/a2mod/a2mod.rb create mode 100644 apache/lib/puppet/provider/a2mod/gentoo.rb create mode 100644 apache/lib/puppet/provider/a2mod/modfix.rb create mode 100644 apache/lib/puppet/provider/a2mod/redhat.rb create mode 100644 apache/lib/puppet/type/a2mod.rb create mode 100644 apache/manifests/balancer.pp create mode 100644 apache/manifests/balancermember.pp create mode 100644 apache/manifests/confd/no_accf.pp create mode 100644 apache/manifests/default_confd_files.pp create mode 100644 apache/manifests/default_mods.pp create mode 100644 apache/manifests/default_mods/load.pp create mode 100644 apache/manifests/dev.pp create mode 100644 apache/manifests/init.pp create mode 100644 apache/manifests/listen.pp create mode 100644 apache/manifests/mod.pp create mode 100644 apache/manifests/mod/alias.pp create mode 100644 apache/manifests/mod/auth_basic.pp create mode 100644 apache/manifests/mod/auth_kerb.pp create mode 100644 apache/manifests/mod/authnz_ldap.pp create mode 100644 apache/manifests/mod/autoindex.pp create mode 100644 apache/manifests/mod/cache.pp create mode 100644 apache/manifests/mod/cgi.pp create mode 100644 apache/manifests/mod/cgid.pp create mode 100644 apache/manifests/mod/dav.pp create mode 100644 apache/manifests/mod/dav_fs.pp create mode 100644 apache/manifests/mod/dav_svn.pp create mode 100644 apache/manifests/mod/deflate.pp create mode 100644 apache/manifests/mod/dev.pp create mode 100644 apache/manifests/mod/dir.pp create mode 100644 apache/manifests/mod/disk_cache.pp create mode 100644 apache/manifests/mod/event.pp create mode 100644 apache/manifests/mod/expires.pp create mode 100644 apache/manifests/mod/fastcgi.pp create mode 100644 apache/manifests/mod/fcgid.pp create mode 100644 apache/manifests/mod/headers.pp create mode 100644 apache/manifests/mod/include.pp create mode 100644 apache/manifests/mod/info.pp create mode 100644 apache/manifests/mod/itk.pp create mode 100644 apache/manifests/mod/ldap.pp create mode 100644 apache/manifests/mod/mime.pp create mode 100644 apache/manifests/mod/mime_magic.pp create mode 100644 apache/manifests/mod/negotiation.pp create mode 100644 apache/manifests/mod/nss.pp create mode 100644 apache/manifests/mod/passenger.pp create mode 100644 apache/manifests/mod/perl.pp create mode 100644 apache/manifests/mod/peruser.pp create mode 100644 apache/manifests/mod/php.pp create mode 100644 apache/manifests/mod/prefork.pp create mode 100644 apache/manifests/mod/proxy.pp create mode 100644 apache/manifests/mod/proxy_ajp.pp create mode 100644 apache/manifests/mod/proxy_balancer.pp create mode 100644 apache/manifests/mod/proxy_html.pp create mode 100644 apache/manifests/mod/proxy_http.pp create mode 100644 apache/manifests/mod/python.pp create mode 100644 apache/manifests/mod/reqtimeout.pp create mode 100644 apache/manifests/mod/rewrite.pp create mode 100644 apache/manifests/mod/rpaf.pp create mode 100644 apache/manifests/mod/setenvif.pp create mode 100644 apache/manifests/mod/ssl.pp create mode 100644 apache/manifests/mod/status.pp create mode 100644 apache/manifests/mod/suphp.pp create mode 100644 apache/manifests/mod/userdir.pp create mode 100644 apache/manifests/mod/vhost_alias.pp create mode 100644 apache/manifests/mod/worker.pp create mode 100644 apache/manifests/mod/wsgi.pp create mode 100644 apache/manifests/mod/xsendfile.pp create mode 100644 apache/manifests/namevirtualhost.pp create mode 100644 apache/manifests/package.pp create mode 100644 apache/manifests/params.pp create mode 100644 apache/manifests/peruser/multiplexer.pp create mode 100644 apache/manifests/peruser/processor.pp create mode 100644 apache/manifests/php.pp create mode 100644 apache/manifests/proxy.pp create mode 100644 apache/manifests/python.pp create mode 100644 apache/manifests/service.pp create mode 100644 apache/manifests/ssl.pp create mode 100644 apache/manifests/vhost.pp create mode 100644 apache/spec/acceptance/apache_parameters_spec.rb create mode 100644 apache/spec/acceptance/apache_ssl_spec.rb create mode 100644 apache/spec/acceptance/basic_spec.rb create mode 100644 apache/spec/acceptance/class_spec.rb create mode 100644 apache/spec/acceptance/default_mods_spec.rb create mode 100644 apache/spec/acceptance/itk_spec.rb create mode 100644 apache/spec/acceptance/mod_php_spec.rb create mode 100644 apache/spec/acceptance/mod_suphp_spec.rb create mode 100644 apache/spec/acceptance/nodesets/centos-59-x64.yml create mode 100644 apache/spec/acceptance/nodesets/centos-64-x64-pe.yml create mode 100644 apache/spec/acceptance/nodesets/centos-64-x64.yml create mode 100644 apache/spec/acceptance/nodesets/debian-607-x64.yml create mode 100644 apache/spec/acceptance/nodesets/debian-70rc1-x64.yml create mode 120000 apache/spec/acceptance/nodesets/default.yml create mode 100644 apache/spec/acceptance/nodesets/fedora-18-x64.yml create mode 100644 apache/spec/acceptance/nodesets/sles-11sp1-x64.yml create mode 100644 apache/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml create mode 100644 apache/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml create mode 100644 apache/spec/acceptance/prefork_worker_spec.rb create mode 100644 apache/spec/acceptance/service_spec.rb create mode 100644 apache/spec/acceptance/vhost_spec.rb create mode 100644 apache/spec/classes/apache_spec.rb create mode 100644 apache/spec/classes/dev_spec.rb create mode 100644 apache/spec/classes/mod/auth_kerb_spec.rb create mode 100644 apache/spec/classes/mod/authnz_ldap_spec.rb create mode 100644 apache/spec/classes/mod/dav_svn_spec.rb create mode 100644 apache/spec/classes/mod/dev_spec.rb create mode 100644 apache/spec/classes/mod/dir_spec.rb create mode 100644 apache/spec/classes/mod/event_spec.rb create mode 100644 apache/spec/classes/mod/fastcgi_spec.rb create mode 100644 apache/spec/classes/mod/fcgid_spec.rb create mode 100644 apache/spec/classes/mod/info_spec.rb create mode 100644 apache/spec/classes/mod/itk_spec.rb create mode 100644 apache/spec/classes/mod/mime_magic_spec.rb create mode 100644 apache/spec/classes/mod/passenger_spec.rb create mode 100644 apache/spec/classes/mod/perl_spec.rb create mode 100644 apache/spec/classes/mod/peruser_spec.rb create mode 100644 apache/spec/classes/mod/php_spec.rb create mode 100644 apache/spec/classes/mod/prefork_spec.rb create mode 100644 apache/spec/classes/mod/proxy_html_spec.rb create mode 100644 apache/spec/classes/mod/python_spec.rb create mode 100644 apache/spec/classes/mod/rpaf_spec.rb create mode 100644 apache/spec/classes/mod/ssl_spec.rb create mode 100644 apache/spec/classes/mod/status_spec.rb create mode 100644 apache/spec/classes/mod/suphp_spec.rb create mode 100644 apache/spec/classes/mod/worker_spec.rb create mode 100644 apache/spec/classes/mod/wsgi_spec.rb create mode 100644 apache/spec/classes/params_spec.rb create mode 100644 apache/spec/classes/service_spec.rb create mode 100644 apache/spec/defines/mod_spec.rb create mode 100644 apache/spec/defines/vhost_spec.rb create mode 100644 apache/spec/fixtures/modules/site_apache/templates/fake.conf.erb create mode 100644 apache/spec/spec.opts create mode 100644 apache/spec/spec_helper.rb create mode 100644 apache/spec/spec_helper_acceptance.rb create mode 100644 apache/spec/unit/provider/a2mod/gentoo_spec.rb create mode 100644 apache/templates/confd/no-accf.conf.erb create mode 100644 apache/templates/httpd.conf.erb create mode 100644 apache/templates/listen.erb create mode 100644 apache/templates/mod/alias.conf.erb create mode 100644 apache/templates/mod/authnz_ldap.conf.erb create mode 100644 apache/templates/mod/autoindex.conf.erb create mode 100644 apache/templates/mod/cgid.conf.erb create mode 100644 apache/templates/mod/dav_fs.conf.erb create mode 100644 apache/templates/mod/deflate.conf.erb create mode 100644 apache/templates/mod/dir.conf.erb create mode 100644 apache/templates/mod/disk_cache.conf.erb create mode 100644 apache/templates/mod/event.conf.erb create mode 100644 apache/templates/mod/fastcgi.conf.erb create mode 100644 apache/templates/mod/info.conf.erb create mode 100644 apache/templates/mod/itk.conf.erb create mode 100644 apache/templates/mod/ldap.conf.erb create mode 100644 apache/templates/mod/mime.conf.erb create mode 100644 apache/templates/mod/mime_magic.conf.erb create mode 100644 apache/templates/mod/mpm_event.conf.erb create mode 100644 apache/templates/mod/negotiation.conf.erb create mode 100644 apache/templates/mod/nss.conf.erb create mode 100644 apache/templates/mod/passenger.conf.erb create mode 100644 apache/templates/mod/peruser.conf.erb create mode 100644 apache/templates/mod/php5.conf.erb create mode 100644 apache/templates/mod/prefork.conf.erb create mode 100644 apache/templates/mod/proxy.conf.erb create mode 100644 apache/templates/mod/proxy_html.conf.erb create mode 100644 apache/templates/mod/reqtimeout.conf.erb create mode 100644 apache/templates/mod/rpaf.conf.erb create mode 100644 apache/templates/mod/setenvif.conf.erb create mode 100644 apache/templates/mod/ssl.conf.erb create mode 100644 apache/templates/mod/status.conf.erb create mode 100644 apache/templates/mod/suphp.conf.erb create mode 100644 apache/templates/mod/userdir.conf.erb create mode 100644 apache/templates/mod/worker.conf.erb create mode 100644 apache/templates/mod/wsgi.conf.erb create mode 100644 apache/templates/namevirtualhost.erb create mode 100644 apache/templates/ports_header.erb create mode 100644 apache/templates/vhost.conf.erb create mode 100644 apache/templates/vhost/_aliases.erb create mode 100644 apache/templates/vhost/_block.erb create mode 100644 apache/templates/vhost/_custom_fragment.erb create mode 100644 apache/templates/vhost/_directories.erb create mode 100644 apache/templates/vhost/_error_document.erb create mode 100644 apache/templates/vhost/_fastcgi.erb create mode 100644 apache/templates/vhost/_header.erb create mode 100644 apache/templates/vhost/_itk.erb create mode 100644 apache/templates/vhost/_php_admin.erb create mode 100644 apache/templates/vhost/_proxy.erb create mode 100644 apache/templates/vhost/_rack.erb create mode 100644 apache/templates/vhost/_redirect.erb create mode 100644 apache/templates/vhost/_requestheader.erb create mode 100644 apache/templates/vhost/_rewrite.erb create mode 100644 apache/templates/vhost/_scriptalias.erb create mode 100644 apache/templates/vhost/_serveralias.erb create mode 100644 apache/templates/vhost/_setenv.erb create mode 100644 apache/templates/vhost/_ssl.erb create mode 100644 apache/templates/vhost/_suphp.erb create mode 100644 apache/templates/vhost/_wsgi.erb create mode 100644 apache/tests/apache.pp create mode 100644 apache/tests/dev.pp create mode 100644 apache/tests/init.pp create mode 100644 apache/tests/mod_load_params.pp create mode 100644 apache/tests/mods.pp create mode 100644 apache/tests/mods_custom.pp create mode 100644 apache/tests/php.pp create mode 100644 apache/tests/vhost.pp create mode 100644 apache/tests/vhost_directories.pp create mode 100644 apache/tests/vhost_ip_based.pp create mode 100644 apache/tests/vhost_ssl.pp create mode 100644 apache/tests/vhosts_without_listen.pp create mode 100644 ceilometer/.fixtures.yml create mode 100644 ceilometer/.gitreview create mode 100644 ceilometer/.travis.yml create mode 100644 ceilometer/Gemfile create mode 100644 ceilometer/LICENSE create mode 100644 ceilometer/Modulefile create mode 100644 ceilometer/README.md create mode 100644 ceilometer/Rakefile create mode 100644 ceilometer/examples/site.pp create mode 100644 ceilometer/lib/puppet/provider/ceilometer_config/ini_setting.rb create mode 100644 ceilometer/lib/puppet/provider/file_line_after/ruby.rb create mode 100644 ceilometer/lib/puppet/type/ceilometer_config.rb create mode 100644 ceilometer/lib/puppet/type/file_line_after.rb create mode 100644 ceilometer/manifests/agent/auth.pp create mode 100644 ceilometer/manifests/agent/central.pp create mode 100644 ceilometer/manifests/agent/compute.pp create mode 100644 ceilometer/manifests/alarm/evaluator.pp create mode 100644 ceilometer/manifests/alarm/notifier.pp create mode 100644 ceilometer/manifests/api.pp create mode 100644 ceilometer/manifests/client.pp create mode 100644 ceilometer/manifests/collector.pp create mode 100644 ceilometer/manifests/db.pp create mode 100644 ceilometer/manifests/db/mysql.pp create mode 100644 ceilometer/manifests/db/mysql/host_access.pp create mode 100644 ceilometer/manifests/expirer.pp create mode 100644 ceilometer/manifests/init.pp create mode 100644 ceilometer/manifests/keystone/auth.pp create mode 100644 ceilometer/manifests/params.pp create mode 100644 ceilometer/spec/classes/ceilometer_agent_auth_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_agent_central_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_agent_compute_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_alarm_evaluator_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_alarm_notifier_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_api_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_client_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_collector_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_db_mysql_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_db_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_expirer_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_init_spec.rb create mode 100644 ceilometer/spec/classes/ceilometer_keystone_auth_spec.rb create mode 100644 ceilometer/spec/spec_helper.rb create mode 100644 certmonger/LICENSE create mode 100644 certmonger/Modulefile create mode 100644 certmonger/README.md create mode 100644 certmonger/lib/facter/ipa_client_configured.rb create mode 100644 certmonger/manifests/request_ipa_cert.pp create mode 100644 certmonger/manifests/server.pp create mode 100644 certmonger/tests/nss_ipa.pp create mode 100644 certmonger/tests/openssl_ipa.pp create mode 100644 cinder/.fixtures.yml create mode 100644 cinder/.gitignore create mode 100644 cinder/.gitreview create mode 100644 cinder/Gemfile create mode 100644 cinder/LICENSE create mode 100644 cinder/Modulefile create mode 100644 cinder/README.md create mode 100644 cinder/Rakefile create mode 100644 cinder/lib/puppet/provider/cinder_api_paste_ini/ini_setting.rb create mode 100644 cinder/lib/puppet/provider/cinder_config/ini_setting.rb create mode 100644 cinder/lib/puppet/type/cinder_api_paste_ini.rb create mode 100644 cinder/lib/puppet/type/cinder_config.rb create mode 100644 cinder/manifests/api.pp create mode 100644 cinder/manifests/backend/iscsi.pp create mode 100644 cinder/manifests/backend/netapp.pp create mode 100644 cinder/manifests/backend/nfs.pp create mode 100644 cinder/manifests/backend/rbd.pp create mode 100644 cinder/manifests/backend/vmdk.pp create mode 100644 cinder/manifests/backends.pp create mode 100644 cinder/manifests/backup.pp create mode 100644 cinder/manifests/backup/ceph.pp create mode 100644 cinder/manifests/backup/swift.pp create mode 100644 cinder/manifests/base.pp create mode 100644 cinder/manifests/ceilometer.pp create mode 100644 cinder/manifests/client.pp create mode 100644 cinder/manifests/config.pp create mode 100644 cinder/manifests/db/mysql.pp create mode 100644 cinder/manifests/db/mysql/host_access.pp create mode 100644 cinder/manifests/db/postgresql.pp create mode 100644 cinder/manifests/db/sync.pp create mode 100644 cinder/manifests/glance.pp create mode 100644 cinder/manifests/init.pp create mode 100644 cinder/manifests/keystone/auth.pp create mode 100644 cinder/manifests/params.pp create mode 100644 cinder/manifests/qpid.pp create mode 100644 cinder/manifests/quota.pp create mode 100644 cinder/manifests/rabbitmq.pp create mode 100644 cinder/manifests/scheduler.pp create mode 100644 cinder/manifests/setup_test_volume.pp create mode 100644 cinder/manifests/type.pp create mode 100644 cinder/manifests/type_set.pp create mode 100644 cinder/manifests/vmware.pp create mode 100644 cinder/manifests/volume.pp create mode 100644 cinder/manifests/volume/glusterfs.pp create mode 100644 cinder/manifests/volume/iscsi.pp create mode 100644 cinder/manifests/volume/netapp.pp create mode 100644 cinder/manifests/volume/nexenta.pp create mode 100644 cinder/manifests/volume/nfs.pp create mode 100644 cinder/manifests/volume/rbd.pp create mode 100644 cinder/manifests/volume/san.pp create mode 100644 cinder/manifests/volume/solidfire.pp create mode 100644 cinder/manifests/volume/vmdk.pp create mode 100644 cinder/spec/classes/cinder_api_spec.rb create mode 100644 cinder/spec/classes/cinder_backup_ceph_spec.rb create mode 100644 cinder/spec/classes/cinder_backup_spec.rb create mode 100644 cinder/spec/classes/cinder_backup_swift_spec.rb create mode 100644 cinder/spec/classes/cinder_ceilometer_spec.rb create mode 100644 cinder/spec/classes/cinder_client_spec.rb create mode 100644 cinder/spec/classes/cinder_db_mysql_spec.rb create mode 100644 cinder/spec/classes/cinder_db_postgresql_spec.rb create mode 100644 cinder/spec/classes/cinder_db_sync_spec.rb create mode 100644 cinder/spec/classes/cinder_glance_spec.rb create mode 100644 cinder/spec/classes/cinder_keystone_auth_spec.rb create mode 100644 cinder/spec/classes/cinder_params_spec.rb create mode 100644 cinder/spec/classes/cinder_qpid_spec.rb create mode 100644 cinder/spec/classes/cinder_quota_spec.rb create mode 100644 cinder/spec/classes/cinder_rabbitmq_spec.rb create mode 100644 cinder/spec/classes/cinder_scheduler_spec.rb create mode 100644 cinder/spec/classes/cinder_setup_test_volume_spec.rb create mode 100644 cinder/spec/classes/cinder_spec.rb create mode 100644 cinder/spec/classes/cinder_vmware_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_glusterfs_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_iscsi_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_netapp_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_nexenta_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_nfs_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_rbd_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_san_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_solidfire_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_spec.rb create mode 100644 cinder/spec/classes/cinder_volume_vmdk_spec.rb create mode 100644 cinder/spec/defines/cinder_backend_iscsi_spec.rb create mode 100644 cinder/spec/defines/cinder_backend_netapp_spec.rb create mode 100644 cinder/spec/defines/cinder_backend_nfs_spec.rb create mode 100644 cinder/spec/defines/cinder_backend_rbd_spec.rb create mode 100644 cinder/spec/defines/cinder_backend_vmdk_spec.rb create mode 100644 cinder/spec/defines/cinder_type_set_spec.rb create mode 100644 cinder/spec/defines/cinder_type_spec.rb create mode 100644 cinder/spec/spec_helper.rb create mode 100644 common/.gitignore create mode 100644 common/COPYING create mode 100644 common/COPYRIGHT create mode 100644 common/INSTALL create mode 100644 common/Makefile create mode 100644 common/README create mode 100644 common/VERSION create mode 100644 common/examples/again-delta-example.pp create mode 100644 common/examples/again-example.pp create mode 100644 common/manifests/again.pp create mode 100644 common/manifests/again/delta.pp create mode 100644 common/manifests/frag/frag.pp create mode 100644 common/manifests/init.pp create mode 100644 common/manifests/vardir.pp create mode 100644 common/puppet-common.spec.in create mode 100755 common/templates/again/again.py.erb create mode 100644 concat/CHANGELOG create mode 100644 concat/README.markdown create mode 100644 concat/files/concatfragments.sh create mode 100644 concat/files/null/.gitignore create mode 100644 concat/manifests/fragment.pp create mode 100644 concat/manifests/init.pp create mode 100644 concat/manifests/setup.pp create mode 100644 firewall/.fixtures.yml create mode 100644 firewall/.gitignore create mode 100644 firewall/.nodeset.yml create mode 100644 firewall/.travis.yml create mode 100644 firewall/CHANGELOG.md create mode 100644 firewall/CONTRIBUTING.md create mode 100644 firewall/Gemfile create mode 100644 firewall/LICENSE create mode 100644 firewall/Modulefile create mode 100644 firewall/README.markdown create mode 100644 firewall/Rakefile create mode 100644 firewall/lib/facter/ip6tables_version.rb create mode 100644 firewall/lib/facter/iptables_persistent_version.rb create mode 100644 firewall/lib/facter/iptables_version.rb create mode 100644 firewall/lib/puppet/provider/firewall.rb create mode 100644 firewall/lib/puppet/provider/firewall/ip6tables.rb create mode 100644 firewall/lib/puppet/provider/firewall/iptables.rb create mode 100644 firewall/lib/puppet/provider/firewallchain/iptables_chain.rb create mode 100644 firewall/lib/puppet/type/firewall.rb create mode 100644 firewall/lib/puppet/type/firewallchain.rb create mode 100644 firewall/lib/puppet/util/firewall.rb create mode 100644 firewall/lib/puppet/util/ipcidr.rb create mode 100644 firewall/manifests/init.pp create mode 100644 firewall/manifests/linux.pp create mode 100644 firewall/manifests/linux/archlinux.pp create mode 100644 firewall/manifests/linux/debian.pp create mode 100644 firewall/manifests/linux/redhat.pp create mode 100644 firewall/metadata.json create mode 100644 firewall/spec/acceptance/change_source_spec.rb create mode 100644 firewall/spec/acceptance/class_spec.rb create mode 100644 firewall/spec/acceptance/firewall_spec.rb create mode 100644 firewall/spec/acceptance/firewallchain_spec.rb create mode 100644 firewall/spec/acceptance/ip6_fragment_spec.rb create mode 100644 firewall/spec/acceptance/isfragment_spec.rb create mode 100644 firewall/spec/acceptance/nodesets/centos-59-x64-pe.yml create mode 100644 firewall/spec/acceptance/nodesets/centos-59-x64.yml create mode 100644 firewall/spec/acceptance/nodesets/centos-64-x64-fusion.yml create mode 100644 firewall/spec/acceptance/nodesets/centos-64-x64-pe.yml create mode 100644 firewall/spec/acceptance/nodesets/centos-64-x64.yml create mode 100644 firewall/spec/acceptance/nodesets/debian-607-x64.yml create mode 100644 firewall/spec/acceptance/nodesets/debian-70rc1-x64.yml create mode 100644 firewall/spec/acceptance/nodesets/default.yml create mode 100644 firewall/spec/acceptance/nodesets/fedora-18-x64.yml create mode 100644 firewall/spec/acceptance/nodesets/sles-11sp1-x64.yml create mode 100644 firewall/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml create mode 100644 firewall/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml create mode 100644 firewall/spec/acceptance/params_spec.rb create mode 100644 firewall/spec/acceptance/purge_spec.rb create mode 100644 firewall/spec/acceptance/resource_cmd_spec.rb create mode 100644 firewall/spec/acceptance/rules_spec.rb create mode 100644 firewall/spec/acceptance/socket_spec.rb create mode 100644 firewall/spec/acceptance/standard_usage_spec.rb create mode 100644 firewall/spec/fixtures/ip6tables/conversion_hash.rb create mode 100644 firewall/spec/fixtures/iptables/conversion_hash.rb create mode 100644 firewall/spec/spec_helper.rb create mode 100644 firewall/spec/spec_helper_acceptance.rb create mode 100644 firewall/spec/unit/classes/firewall_linux_archlinux_spec.rb create mode 100644 firewall/spec/unit/classes/firewall_linux_debian_spec.rb create mode 100644 firewall/spec/unit/classes/firewall_linux_redhat_spec.rb create mode 100644 firewall/spec/unit/classes/firewall_linux_spec.rb create mode 100644 firewall/spec/unit/classes/firewall_spec.rb create mode 100644 firewall/spec/unit/facter/iptables_persistent_version_spec.rb create mode 100644 firewall/spec/unit/facter/iptables_spec.rb create mode 100755 firewall/spec/unit/puppet/provider/iptables_chain_spec.rb create mode 100644 firewall/spec/unit/puppet/provider/iptables_spec.rb create mode 100755 firewall/spec/unit/puppet/type/firewall_spec.rb create mode 100755 firewall/spec/unit/puppet/type/firewallchain_spec.rb create mode 100644 firewall/spec/unit/puppet/util/firewall_spec.rb create mode 100644 firewall/spec/unit/puppet/util/ipcidr_spec.rb create mode 100644 glance/.fixtures.yml create mode 100644 glance/.gitignore create mode 100644 glance/.gitreview create mode 100644 glance/Gemfile create mode 100644 glance/LICENSE create mode 100644 glance/Modulefile create mode 100644 glance/README.md create mode 100644 glance/Rakefile create mode 100644 glance/ext/glance.rb create mode 100755 glance/ext/glance.sh create mode 100644 glance/lib/puppet/provider/glance.rb create mode 100644 glance/lib/puppet/provider/glance_api_config/ini_setting.rb create mode 100644 glance/lib/puppet/provider/glance_api_paste_ini/ini_setting.rb create mode 100644 glance/lib/puppet/provider/glance_cache_config/ini_setting.rb create mode 100644 glance/lib/puppet/provider/glance_image/glance.rb create mode 100644 glance/lib/puppet/provider/glance_registry_config/ini_setting.rb create mode 100644 glance/lib/puppet/provider/glance_registry_paste_ini/ini_setting.rb create mode 100644 glance/lib/puppet/type/glance_api_config.rb create mode 100644 glance/lib/puppet/type/glance_api_paste_ini.rb create mode 100644 glance/lib/puppet/type/glance_cache_config.rb create mode 100644 glance/lib/puppet/type/glance_image.rb create mode 100644 glance/lib/puppet/type/glance_registry_config.rb create mode 100644 glance/lib/puppet/type/glance_registry_paste_ini.rb create mode 100644 glance/manifests/api.pp create mode 100644 glance/manifests/backend/cinder.pp create mode 100644 glance/manifests/backend/file.pp create mode 100644 glance/manifests/backend/rbd.pp create mode 100644 glance/manifests/backend/swift.pp create mode 100644 glance/manifests/cache/cleaner.pp create mode 100644 glance/manifests/cache/pruner.pp create mode 100644 glance/manifests/client.pp create mode 100644 glance/manifests/config.pp create mode 100644 glance/manifests/db/mysql.pp create mode 100644 glance/manifests/db/mysql/host_access.pp create mode 100644 glance/manifests/db/postgresql.pp create mode 100644 glance/manifests/init.pp create mode 100644 glance/manifests/keystone/auth.pp create mode 100644 glance/manifests/notify/qpid.pp create mode 100644 glance/manifests/notify/rabbitmq.pp create mode 100644 glance/manifests/params.pp create mode 100644 glance/manifests/registry.pp create mode 100644 glance/spec/classes/glance_api_spec.rb create mode 100644 glance/spec/classes/glance_backend_cinder_spec.rb create mode 100644 glance/spec/classes/glance_backend_file_spec.rb create mode 100644 glance/spec/classes/glance_backend_rbd_spec.rb create mode 100644 glance/spec/classes/glance_backend_swift_spec.rb create mode 100644 glance/spec/classes/glance_cache_cleaner_spec.rb create mode 100644 glance/spec/classes/glance_cache_pruner_spec.rb create mode 100644 glance/spec/classes/glance_client_spec.rb create mode 100644 glance/spec/classes/glance_db_mysql_spec.rb create mode 100644 glance/spec/classes/glance_db_postgresql_spec.rb create mode 100644 glance/spec/classes/glance_keystone_auth_spec.rb create mode 100644 glance/spec/classes/glance_notify_qpid_spec.rb create mode 100644 glance/spec/classes/glance_notify_rabbitmq_spec.rb create mode 100644 glance/spec/classes/glance_registry_spec.rb create mode 100644 glance/spec/classes/glance_spec.rb create mode 100644 glance/spec/shared_examples.rb create mode 100644 glance/spec/spec.opts create mode 100644 glance/spec/spec_helper.rb create mode 100644 glance/spec/unit/provider/glance_spec.rb create mode 100644 glance/tests/api.pp create mode 100644 glance/tests/init.pp create mode 100644 glance/tests/registry.pp create mode 100644 glance/tests/site.pp create mode 100644 gluster/.gitignore create mode 100644 gluster/.gitmodules create mode 100644 gluster/COPYING create mode 100644 gluster/COPYRIGHT create mode 100644 gluster/DOCUMENTATION.md create mode 100644 gluster/INSTALL create mode 100644 gluster/Makefile create mode 100644 gluster/Modulefile create mode 100644 gluster/README create mode 120000 gluster/README.md create mode 100644 gluster/VERSION create mode 100644 gluster/builder/Makefile create mode 100644 gluster/builder/README create mode 100644 gluster/builder/Vagrantfile create mode 100644 gluster/builder/files/cleanup.sh create mode 100644 gluster/builder/files/epel-release-6-8.noarch.rpm create mode 100644 gluster/builder/files/network.sh create mode 100644 gluster/builder/files/password create mode 100644 gluster/builder/files/puppetlabs-release-el-6.noarch.rpm create mode 100644 gluster/builder/files/selinux create mode 100644 gluster/builder/files/ssh.sh create mode 100644 gluster/builder/files/user.sh create mode 100755 gluster/builder/versions/template.sh create mode 100644 gluster/examples/distributed-replicate-example.pp create mode 100644 gluster/examples/filesystem-backed-bricks-example.pp create mode 100644 gluster/examples/gluster-nfs-ipa-example.pp create mode 100644 gluster/examples/gluster-simple-example.pp create mode 100644 gluster/examples/gluster-simple-physical-example-best.pp create mode 100644 gluster/examples/gluster-simple-physical-example.pp create mode 100644 gluster/examples/mount-example.pp create mode 100644 gluster/examples/wrapper-example.pp create mode 100644 gluster/files/groups/small-file-perf create mode 100644 gluster/files/groups/virt create mode 100755 gluster/files/sponge.py create mode 100644 gluster/files/xml.py create mode 100644 gluster/lib/facter/gluster_bricks.rb create mode 100644 gluster/lib/facter/gluster_fsm.rb create mode 100644 gluster/lib/facter/gluster_fsuuid.rb create mode 100644 gluster/lib/facter/gluster_host.rb create mode 100644 gluster/lib/facter/gluster_ports.rb create mode 100644 gluster/lib/facter/gluster_property.rb create mode 100644 gluster/lib/facter/gluster_uuid.rb create mode 100644 gluster/lib/facter/gluster_version.rb create mode 100644 gluster/lib/facter/gluster_vrrp.rb create mode 100644 gluster/lib/puppet/parser/functions/brick_layout_chained.rb create mode 100644 gluster/lib/puppet/parser/functions/brick_layout_simple.rb create mode 100644 gluster/manifests/again.pp create mode 100644 gluster/manifests/api.pp create mode 100644 gluster/manifests/brick.pp create mode 100644 gluster/manifests/brick/base.pp create mode 100644 gluster/manifests/brick/ext4.pp create mode 100644 gluster/manifests/brick/xfs.pp create mode 100644 gluster/manifests/host.pp create mode 100644 gluster/manifests/host/data.pp create mode 100644 gluster/manifests/init.pp create mode 100644 gluster/manifests/mount.pp create mode 100644 gluster/manifests/mount/base.pp create mode 100644 gluster/manifests/repo.pp create mode 100644 gluster/manifests/rulewrapper.pp create mode 100644 gluster/manifests/server.pp create mode 100644 gluster/manifests/simple.pp create mode 100644 gluster/manifests/vardir.pp create mode 100644 gluster/manifests/volume.pp create mode 100644 gluster/manifests/volume/base.pp create mode 100644 gluster/manifests/volume/fsm.pp create mode 100644 gluster/manifests/volume/ping.pp create mode 100644 gluster/manifests/volume/property.pp create mode 100644 gluster/manifests/volume/property/data.pp create mode 100644 gluster/manifests/volume/property/group.pp create mode 100644 gluster/manifests/volume/property/group/data.pp create mode 100644 gluster/manifests/wrapper.pp create mode 100644 gluster/manifests/xml.pp create mode 100644 gluster/puppet-gluster-documentation.pdf create mode 100644 gluster/puppet-gluster.spec.in create mode 100644 gluster/templates/glusterd.info.erb create mode 100644 gluster/templates/glusterd.vol.erb create mode 100644 gluster/vagrant/README create mode 100644 gluster/vagrant/gluster/.gitignore create mode 100644 gluster/vagrant/gluster/Vagrantfile create mode 100644 gluster/vagrant/gluster/puppet/files/README create mode 100644 gluster/vagrant/gluster/puppet/hiera.yaml create mode 100644 gluster/vagrant/gluster/puppet/hieradata/common.yaml create mode 100644 gluster/vagrant/gluster/puppet/manifests/site.pp create mode 100644 gluster/vagrant/gluster/puppet/modules/.gitignore create mode 100644 gluster/vagrant/gluster/puppet/modules/Makefile create mode 100644 gluster/vagrant/gluster/puppet/modules/README create mode 160000 gluster/vagrant/gluster/puppet/modules/apt create mode 160000 gluster/vagrant/gluster/puppet/modules/common create mode 160000 gluster/vagrant/gluster/puppet/modules/keepalived create mode 160000 gluster/vagrant/gluster/puppet/modules/puppet create mode 160000 gluster/vagrant/gluster/puppet/modules/shorewall create mode 160000 gluster/vagrant/gluster/puppet/modules/stdlib create mode 160000 gluster/vagrant/gluster/puppet/modules/yum create mode 100644 haproxy/.fixtures.yml create mode 100644 haproxy/.gemfile create mode 100644 haproxy/.travis.yml create mode 100644 haproxy/CHANGELOG create mode 100644 haproxy/Modulefile create mode 100644 haproxy/README.md create mode 100644 haproxy/Rakefile create mode 100644 haproxy/manifests/backend.pp create mode 100644 haproxy/manifests/balancermember.pp create mode 100644 haproxy/manifests/frontend.pp create mode 100644 haproxy/manifests/init.pp create mode 100644 haproxy/manifests/listen.pp create mode 100644 haproxy/manifests/params.pp create mode 100644 haproxy/spec/classes/haproxy_spec.rb create mode 100644 haproxy/spec/defines/backend_spec.rb create mode 100644 haproxy/spec/defines/balancermember_spec.rb create mode 100644 haproxy/spec/defines/frontend_spec.rb create mode 100644 haproxy/spec/defines/listen_spec.rb create mode 100644 haproxy/spec/spec.opts create mode 100644 haproxy/spec/spec_helper.rb create mode 100644 haproxy/templates/haproxy-base.cfg.erb create mode 100644 haproxy/templates/haproxy_backend_block.erb create mode 100644 haproxy/templates/haproxy_balancermember.erb create mode 100644 haproxy/templates/haproxy_frontend_block.erb create mode 100644 haproxy/templates/haproxy_listen_block.erb create mode 100644 haproxy/tests/init.pp create mode 100644 heat/.fixtures.yml create mode 100644 heat/.gitreview create mode 100644 heat/Gemfile create mode 100644 heat/LICENSE create mode 100644 heat/Modulefile create mode 100644 heat/README.md create mode 100644 heat/Rakefile create mode 100644 heat/examples/site.pp create mode 100644 heat/lib/puppet/provider/heat_config/ini_setting.rb create mode 100644 heat/lib/puppet/type/heat_config.rb create mode 100644 heat/manifests/api-cfn.pp create mode 100644 heat/manifests/api-cloudwatch.pp create mode 100644 heat/manifests/api.pp create mode 100644 heat/manifests/api_cfn.pp create mode 100644 heat/manifests/api_cloudwatch.pp create mode 100644 heat/manifests/client.pp create mode 100644 heat/manifests/db/mysql.pp create mode 100644 heat/manifests/db/mysql/host_access.pp create mode 100644 heat/manifests/engine.pp create mode 100644 heat/manifests/init.pp create mode 100644 heat/manifests/keystone/auth.pp create mode 100644 heat/manifests/keystone/auth_cfn.pp create mode 100644 heat/manifests/params.pp create mode 100644 heat/spec/classes/heat_api_cfn_spec.rb create mode 100644 heat/spec/classes/heat_api_cloudwatch_spec.rb create mode 100644 heat/spec/classes/heat_api_spec.rb create mode 100644 heat/spec/classes/heat_db_mysql_spec.rb create mode 100644 heat/spec/classes/heat_engine_spec.rb create mode 100644 heat/spec/classes/heat_init_spec.rb create mode 100644 heat/spec/classes/heat_keystone_auth_cfn_spec.rb create mode 100644 heat/spec/classes/heat_keystone_auth_spec.rb create mode 100644 heat/spec/shared_examples.rb create mode 100644 heat/spec/spec_helper.rb create mode 100644 horizon/.fixtures.yml create mode 100644 horizon/.gitignore create mode 100644 horizon/.gitreview create mode 100644 horizon/Gemfile create mode 100644 horizon/LICENSE create mode 100644 horizon/Modulefile create mode 100644 horizon/README.md create mode 100644 horizon/Rakefile create mode 100644 horizon/manifests/init.pp create mode 100644 horizon/manifests/params.pp create mode 100644 horizon/spec/classes/horizon_init_spec.rb create mode 100644 horizon/spec/fixtures/override_local_settings.py.erb create mode 100644 horizon/spec/spec_helper.rb create mode 100644 horizon/templates/local_settings.py.erb create mode 100644 inifile/.fixtures.yml create mode 100644 inifile/.gitignore create mode 100644 inifile/.travis.yml create mode 100644 inifile/CHANGELOG create mode 100644 inifile/Gemfile create mode 100644 inifile/LICENSE create mode 100644 inifile/Modulefile create mode 100644 inifile/README.markdown create mode 100644 inifile/Rakefile create mode 100644 inifile/lib/puppet/provider/ini_setting/ruby.rb create mode 100644 inifile/lib/puppet/provider/ini_subsetting/ruby.rb create mode 100644 inifile/lib/puppet/type/ini_setting.rb create mode 100644 inifile/lib/puppet/type/ini_subsetting.rb create mode 100644 inifile/lib/puppet/util/external_iterator.rb create mode 100644 inifile/lib/puppet/util/ini_file.rb create mode 100644 inifile/lib/puppet/util/ini_file/section.rb create mode 100644 inifile/lib/puppet/util/setting_value.rb create mode 100644 inifile/spec/classes/inherit_test1_spec.rb create mode 100644 inifile/spec/fixtures/modules/inherit_ini_setting/lib/puppet/provider/inherit_ini_setting/ini_setting.rb create mode 100644 inifile/spec/fixtures/modules/inherit_ini_setting/lib/puppet/type/inherit_ini_setting.rb create mode 100644 inifile/spec/fixtures/modules/inherit_test1/manifests/init.pp create mode 100644 inifile/spec/fixtures/tmp/.empty create mode 100644 inifile/spec/spec_helper.rb create mode 100644 inifile/spec/unit/puppet/provider/ini_setting/inheritance_spec.rb create mode 100644 inifile/spec/unit/puppet/provider/ini_setting/ruby_spec.rb create mode 100644 inifile/spec/unit/puppet/provider/ini_subsetting/ruby_spec.rb create mode 100644 inifile/spec/unit/puppet/util/external_iterator_spec.rb create mode 100644 inifile/spec/unit/puppet/util/ini_file_spec.rb create mode 100644 inifile/spec/unit/puppet/util/setting_value_spec.rb create mode 100644 inifile/tests/ini_setting.pp create mode 100644 inifile/tests/ini_subsetting.pp create mode 100644 keystone/.fixtures.yml create mode 100644 keystone/.gitignore create mode 100644 keystone/.gitreview create mode 100644 keystone/Gemfile create mode 100644 keystone/LICENSE create mode 100644 keystone/Modulefile create mode 100644 keystone/README.md create mode 100644 keystone/Rakefile create mode 100644 keystone/examples/apache_dropin.pp create mode 100644 keystone/examples/apache_with_paths.pp create mode 100644 keystone/ext/keystone_test.rb create mode 100644 keystone/files/httpd/keystone.py create mode 100644 keystone/lib/puppet/provider/keystone.rb create mode 100644 keystone/lib/puppet/provider/keystone_config/ini_setting.rb create mode 100644 keystone/lib/puppet/provider/keystone_endpoint/keystone.rb create mode 100644 keystone/lib/puppet/provider/keystone_role/keystone.rb create mode 100644 keystone/lib/puppet/provider/keystone_service/keystone.rb create mode 100644 keystone/lib/puppet/provider/keystone_tenant/keystone.rb create mode 100644 keystone/lib/puppet/provider/keystone_user/keystone.rb create mode 100644 keystone/lib/puppet/provider/keystone_user_role/keystone.rb create mode 100644 keystone/lib/puppet/type/keystone_config.rb create mode 100644 keystone/lib/puppet/type/keystone_endpoint.rb create mode 100644 keystone/lib/puppet/type/keystone_role.rb create mode 100644 keystone/lib/puppet/type/keystone_service.rb create mode 100644 keystone/lib/puppet/type/keystone_tenant.rb create mode 100644 keystone/lib/puppet/type/keystone_user.rb create mode 100644 keystone/lib/puppet/type/keystone_user_role.rb create mode 100644 keystone/manifests/client.pp create mode 100644 keystone/manifests/config.pp create mode 100644 keystone/manifests/cron/token_flush.pp create mode 100644 keystone/manifests/db/mysql.pp create mode 100644 keystone/manifests/db/mysql/host_access.pp create mode 100644 keystone/manifests/db/postgresql.pp create mode 100644 keystone/manifests/db/sync.pp create mode 100644 keystone/manifests/dev/install.pp create mode 100644 keystone/manifests/endpoint.pp create mode 100644 keystone/manifests/init.pp create mode 100644 keystone/manifests/ldap.pp create mode 100644 keystone/manifests/params.pp create mode 100644 keystone/manifests/python.pp create mode 100644 keystone/manifests/roles/admin.pp create mode 100644 keystone/manifests/wsgi/apache.pp create mode 100644 keystone/spec/classes/keystone_client_spec.rb create mode 100644 keystone/spec/classes/keystone_cron_token_flush.rb create mode 100644 keystone/spec/classes/keystone_db_mysql_spec.rb create mode 100644 keystone/spec/classes/keystone_db_postgresql_spec.rb create mode 100644 keystone/spec/classes/keystone_endpoint_spec.rb create mode 100644 keystone/spec/classes/keystone_ldap_spec.rb create mode 100644 keystone/spec/classes/keystone_python_spec.rb create mode 100644 keystone/spec/classes/keystone_roles_admin_spec.rb create mode 100644 keystone/spec/classes/keystone_spec.rb create mode 100644 keystone/spec/classes/keystone_wsgi_apache_spec.rb create mode 100644 keystone/spec/shared_examples.rb create mode 100644 keystone/spec/spec.opts create mode 100644 keystone/spec/spec_helper.rb create mode 100644 keystone/spec/unit/provider/keystone_endpoint/keystone_spec.rb create mode 100644 keystone/spec/unit/provider/keystone_spec.rb create mode 100644 keystone/spec/unit/provider/keystone_tenant/keystone_spec.rb create mode 100644 keystone/spec/unit/provider/keystone_user/keystone_spec.rb create mode 100644 keystone/spec/unit/type/keystone_endpoint_spec.rb create mode 100644 keystone/tests/site.pp create mode 100644 memcached/.fixtures.yml create mode 100644 memcached/.gemfile create mode 100644 memcached/.gitignore create mode 100644 memcached/.travis.yml create mode 100644 memcached/LICENSE create mode 100644 memcached/Modulefile create mode 100644 memcached/README-DEVELOPER create mode 100644 memcached/README.md create mode 100644 memcached/Rakefile create mode 100644 memcached/lib/puppet/parser/functions/memcached_max_memory.rb create mode 100644 memcached/manifests/init.pp create mode 100644 memcached/manifests/params.pp create mode 100644 memcached/spec/classes/memcached_spec.rb create mode 100644 memcached/spec/spec.opts create mode 100644 memcached/spec/spec_helper.rb create mode 100644 memcached/templates/memcached.conf.erb create mode 100644 memcached/templates/memcached_sysconfig.erb create mode 100644 memcached/tests/init.pp create mode 100644 mongodb/.fixtures.yml create mode 100644 mongodb/.gitignore create mode 100644 mongodb/.nodeset.yml create mode 100644 mongodb/.travis.yml create mode 100644 mongodb/CHANGELOG create mode 100644 mongodb/Gemfile create mode 100644 mongodb/LICENSE create mode 100644 mongodb/Modulefile create mode 100644 mongodb/README.md create mode 100644 mongodb/Rakefile create mode 100644 mongodb/lib/puppet/parser/functions/mongodb_password.rb create mode 100644 mongodb/lib/puppet/provider/mongodb_database/mongodb.rb create mode 100644 mongodb/lib/puppet/provider/mongodb_user/mongodb.rb create mode 100644 mongodb/lib/puppet/type/mongodb_database.rb create mode 100644 mongodb/lib/puppet/type/mongodb_user.rb create mode 100644 mongodb/manifests/db.pp create mode 100644 mongodb/manifests/globals.pp create mode 100644 mongodb/manifests/init.pp create mode 100644 mongodb/manifests/params.pp create mode 100644 mongodb/manifests/repo.pp create mode 100644 mongodb/manifests/repo/apt.pp create mode 100644 mongodb/manifests/repo/yum.pp create mode 100644 mongodb/manifests/server.pp create mode 100644 mongodb/manifests/server/config.pp create mode 100644 mongodb/manifests/server/install.pp create mode 100644 mongodb/manifests/server/service.pp create mode 100644 mongodb/spec/classes/repo_spec.rb create mode 100644 mongodb/spec/classes/server_config_spec.rb create mode 100644 mongodb/spec/classes/server_install_spec.rb create mode 100644 mongodb/spec/classes/server_spec.rb create mode 100644 mongodb/spec/defines/db_spec.rb create mode 100644 mongodb/spec/spec_helper.rb create mode 100644 mongodb/spec/spec_helper_system.rb create mode 100644 mongodb/spec/system/basic_spec.rb create mode 100644 mongodb/spec/system/server_10gen_spec.rb create mode 100644 mongodb/spec/system/server_distro_spec.rb create mode 100644 mongodb/spec/unit/mongodb_password_spec.rb create mode 100644 mongodb/spec/unit/puppet/provider/mongodb_database/mongodb_spec.rb create mode 100644 mongodb/spec/unit/puppet/provider/mongodb_user/mongodb_spec.rb create mode 100644 mongodb/spec/unit/puppet/type/mongodb_database_spec.rb create mode 100644 mongodb/spec/unit/puppet/type/mongodb_user_spec.rb create mode 100644 mongodb/templates/mongodb.conf.erb create mode 100644 mongodb/tests/globals.pp create mode 100644 mongodb/tests/init.pp create mode 100644 mongodb/tests/server.pp create mode 100644 mysql/.fixtures.yml create mode 100644 mysql/.gemfile create mode 100644 mysql/.gitignore create mode 100644 mysql/.travis.yml create mode 100644 mysql/CHANGELOG create mode 100644 mysql/LICENSE create mode 100644 mysql/Modulefile create mode 100644 mysql/README.md create mode 100644 mysql/Rakefile create mode 100644 mysql/TODO create mode 100644 mysql/files/mysqltuner.pl create mode 100644 mysql/lib/puppet/parser/functions/mysql_password.rb create mode 100644 mysql/lib/puppet/provider/database/mysql.rb create mode 100644 mysql/lib/puppet/provider/database_grant/mysql.rb create mode 100644 mysql/lib/puppet/provider/database_user/mysql.rb create mode 100644 mysql/lib/puppet/type/database.rb create mode 100644 mysql/lib/puppet/type/database_grant.rb create mode 100644 mysql/lib/puppet/type/database_user.rb create mode 100644 mysql/manifests/backup.pp create mode 100644 mysql/manifests/config.pp create mode 100644 mysql/manifests/db.pp create mode 100644 mysql/manifests/init.pp create mode 100644 mysql/manifests/java.pp create mode 100644 mysql/manifests/params.pp create mode 100644 mysql/manifests/python.pp create mode 100644 mysql/manifests/ruby.pp create mode 100644 mysql/manifests/server.pp create mode 100644 mysql/manifests/server/account_security.pp create mode 100644 mysql/manifests/server/config.pp create mode 100644 mysql/manifests/server/monitor.pp create mode 100644 mysql/manifests/server/mysqltuner.pp create mode 100644 mysql/spec/classes/mysql_backup_spec.rb create mode 100644 mysql/spec/classes/mysql_config_spec.rb create mode 100644 mysql/spec/classes/mysql_init_spec.rb create mode 100644 mysql/spec/classes/mysql_java_spec.rb create mode 100644 mysql/spec/classes/mysql_python_spec.rb create mode 100644 mysql/spec/classes/mysql_ruby_spec.rb create mode 100644 mysql/spec/classes/mysql_server_account_security_spec.rb create mode 100644 mysql/spec/classes/mysql_server_monitor_spec.rb create mode 100644 mysql/spec/classes/mysql_server_spec.rb create mode 100644 mysql/spec/defines/mysql_db_spec.rb create mode 100644 mysql/spec/defines/mysql_server_config_spec.rb create mode 100644 mysql/spec/spec.opts create mode 100644 mysql/spec/spec_helper.rb create mode 100644 mysql/spec/unit/mysql_password_spec.rb create mode 100644 mysql/spec/unit/puppet/provider/database_grant/mysql_spec.rb create mode 100644 mysql/templates/my.cnf.erb create mode 100644 mysql/templates/my.cnf.pass.erb create mode 100644 mysql/templates/my.conf.cnf.erb create mode 100644 mysql/templates/mysqlbackup.sh.erb create mode 100644 mysql/tests/backup.pp create mode 100644 mysql/tests/init.pp create mode 100644 mysql/tests/java.pp create mode 100644 mysql/tests/mysql_database.pp create mode 100644 mysql/tests/mysql_grant.pp create mode 100644 mysql/tests/mysql_user.pp create mode 100644 mysql/tests/python.pp create mode 100644 mysql/tests/ruby.pp create mode 100644 mysql/tests/server.pp create mode 100644 mysql/tests/server/account_security.pp create mode 100644 mysql/tests/server/config.pp create mode 100644 neutron/.fixtures.yml create mode 100644 neutron/.gitignore create mode 100644 neutron/.gitreview create mode 100644 neutron/Gemfile create mode 100644 neutron/LICENSE create mode 100644 neutron/Modulefile create mode 100644 neutron/README.md create mode 100644 neutron/Rakefile create mode 100644 neutron/examples/base_provision.pp create mode 100644 neutron/examples/create_network.sh create mode 100644 neutron/examples/neutron.pp create mode 100644 neutron/lib/puppet/parser/functions/validate_network_vlan_ranges.rb create mode 100644 neutron/lib/puppet/parser/functions/validate_tunnel_id_ranges.rb create mode 100644 neutron/lib/puppet/parser/functions/validate_vni_ranges.rb create mode 100644 neutron/lib/puppet/parser/functions/validate_vxlan_udp_port.rb create mode 100644 neutron/lib/puppet/provider/neutron.rb create mode 100644 neutron/lib/puppet/provider/neutron_api_config/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_config/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_dhcp_agent_config/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_fwaas_service_config/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_l3_agent_config/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_l3_ovs_bridge/neutron.rb create mode 100644 neutron/lib/puppet/provider/neutron_lbaas_agent_config/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_metadata_agent_config/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_metering_agent_config/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_network/neutron.rb create mode 100644 neutron/lib/puppet/provider/neutron_plugin_cisco/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_plugin_cisco_credentials/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_plugin_cisco_db_conn/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_plugin_cisco_l2network/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_plugin_linuxbridge/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_plugin_ml2/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_plugin_nvp/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_plugin_ovs/ini_setting.rb create mode 100644 neutron/lib/puppet/provider/neutron_port/neutron.rb create mode 100644 neutron/lib/puppet/provider/neutron_router/neutron.rb create mode 100644 neutron/lib/puppet/provider/neutron_router_interface/neutron.rb create mode 100644 neutron/lib/puppet/provider/neutron_subnet/neutron.rb create mode 100644 neutron/lib/puppet/provider/neutron_vpnaas_agent_config/ini_setting.rb create mode 100644 neutron/lib/puppet/type/neutron_api_config.rb create mode 100644 neutron/lib/puppet/type/neutron_config.rb create mode 100644 neutron/lib/puppet/type/neutron_dhcp_agent_config.rb create mode 100644 neutron/lib/puppet/type/neutron_fwaas_service_config.rb create mode 100644 neutron/lib/puppet/type/neutron_l3_agent_config.rb create mode 100644 neutron/lib/puppet/type/neutron_l3_ovs_bridge.rb create mode 100644 neutron/lib/puppet/type/neutron_lbaas_agent_config.rb create mode 100644 neutron/lib/puppet/type/neutron_metadata_agent_config.rb create mode 100644 neutron/lib/puppet/type/neutron_metering_agent_config.rb create mode 100644 neutron/lib/puppet/type/neutron_network.rb create mode 100644 neutron/lib/puppet/type/neutron_plugin_cisco.rb create mode 100644 neutron/lib/puppet/type/neutron_plugin_cisco_credentials.rb create mode 100644 neutron/lib/puppet/type/neutron_plugin_cisco_db_conn.rb create mode 100644 neutron/lib/puppet/type/neutron_plugin_cisco_l2network.rb create mode 100644 neutron/lib/puppet/type/neutron_plugin_linuxbridge.rb create mode 100644 neutron/lib/puppet/type/neutron_plugin_ml2.rb create mode 100644 neutron/lib/puppet/type/neutron_plugin_nvp.rb create mode 100644 neutron/lib/puppet/type/neutron_plugin_ovs.rb create mode 100644 neutron/lib/puppet/type/neutron_port.rb create mode 100644 neutron/lib/puppet/type/neutron_router.rb create mode 100644 neutron/lib/puppet/type/neutron_router_interface.rb create mode 100644 neutron/lib/puppet/type/neutron_subnet.rb create mode 100644 neutron/lib/puppet/type/neutron_vpnaas_agent_config.rb create mode 100644 neutron/manifests/agents/dhcp.pp create mode 100644 neutron/manifests/agents/l3.pp create mode 100644 neutron/manifests/agents/lbaas.pp create mode 100644 neutron/manifests/agents/linuxbridge.pp create mode 100644 neutron/manifests/agents/metadata.pp create mode 100644 neutron/manifests/agents/metering.pp create mode 100644 neutron/manifests/agents/ovs.pp create mode 100644 neutron/manifests/agents/vpnaas.pp create mode 100644 neutron/manifests/client.pp create mode 100644 neutron/manifests/config.pp create mode 100644 neutron/manifests/db/mysql.pp create mode 100644 neutron/manifests/db/mysql/host_access.pp create mode 100644 neutron/manifests/init.pp create mode 100644 neutron/manifests/keystone/auth.pp create mode 100644 neutron/manifests/params.pp create mode 100644 neutron/manifests/plugins/cisco.pp create mode 100644 neutron/manifests/plugins/linuxbridge.pp create mode 100644 neutron/manifests/plugins/ml2.pp create mode 100644 neutron/manifests/plugins/ml2/driver.pp create mode 100644 neutron/manifests/plugins/nvp.pp create mode 100644 neutron/manifests/plugins/ovs.pp create mode 100644 neutron/manifests/plugins/ovs/bridge.pp create mode 100644 neutron/manifests/plugins/ovs/port.pp create mode 100644 neutron/manifests/quota.pp create mode 100644 neutron/manifests/server.pp create mode 100644 neutron/manifests/services/fwaas.pp create mode 100644 neutron/spec/classes/neutron_agents_dhcp_spec.rb create mode 100644 neutron/spec/classes/neutron_agents_l3_spec.rb create mode 100644 neutron/spec/classes/neutron_agents_lbaas_spec.rb create mode 100644 neutron/spec/classes/neutron_agents_linuxbridge_spec.rb create mode 100644 neutron/spec/classes/neutron_agents_metadata_spec.rb create mode 100644 neutron/spec/classes/neutron_agents_metering_spec.rb create mode 100644 neutron/spec/classes/neutron_agents_ovs_spec.rb create mode 100644 neutron/spec/classes/neutron_agents_vpnaas_spec.rb create mode 100644 neutron/spec/classes/neutron_client_spec.rb create mode 100644 neutron/spec/classes/neutron_db_mysql_spec.rb create mode 100644 neutron/spec/classes/neutron_init_spec.rb create mode 100644 neutron/spec/classes/neutron_keystone_auth_spec.rb create mode 100644 neutron/spec/classes/neutron_plugins_cisco_spec.rb create mode 100644 neutron/spec/classes/neutron_plugins_linuxbridge_spec.rb create mode 100644 neutron/spec/classes/neutron_plugins_ml2_spec.rb create mode 100644 neutron/spec/classes/neutron_plugins_nvp_spec.rb create mode 100644 neutron/spec/classes/neutron_plugins_ovs_spec.rb create mode 100644 neutron/spec/classes/neutron_quota_spec.rb create mode 100644 neutron/spec/classes/neutron_server_spec.rb create mode 100644 neutron/spec/classes/neutron_services_fwaas_spec.rb create mode 100644 neutron/spec/defines/neutron_db_mysql_host_access_spec.rb create mode 100644 neutron/spec/shared_examples.rb create mode 100644 neutron/spec/spec_helper.rb create mode 100644 neutron/spec/unit/provider/neutron_l3_ovs_bridge/neutron_spec.rb create mode 100644 neutron/spec/unit/provider/neutron_network/neutron_spec.rb create mode 100644 neutron/spec/unit/provider/neutron_router/neutron_spec.rb create mode 100644 neutron/spec/unit/provider/neutron_router_interface/neutron_spec.rb create mode 100644 neutron/spec/unit/provider/neutron_spec.rb create mode 100644 neutron/spec/unit/provider/neutron_subnet/neutron_spec.rb create mode 100644 nova/.fixtures.yml create mode 100644 nova/.gitignore create mode 100644 nova/.gitreview create mode 100644 nova/Gemfile create mode 100644 nova/LICENSE create mode 100644 nova/Modulefile create mode 100644 nova/README.md create mode 100644 nova/Rakefile create mode 100755 nova/files/compute.py create mode 100644 nova/files/connection.py create mode 100644 nova/files/nova-novncproxy.init create mode 100644 nova/lib/puppet/provider/nova_admin/nova_manage.rb create mode 100644 nova/lib/puppet/provider/nova_cells/nova_manage.rb create mode 100644 nova/lib/puppet/provider/nova_config/ini_setting.rb create mode 100644 nova/lib/puppet/provider/nova_floating/nova_manage.rb create mode 100644 nova/lib/puppet/provider/nova_network/nova_manage.rb create mode 100644 nova/lib/puppet/provider/nova_paste_api_ini/ini_setting.rb create mode 100644 nova/lib/puppet/provider/nova_project/nova_manage.rb create mode 100644 nova/lib/puppet/type/nova_admin.rb create mode 100644 nova/lib/puppet/type/nova_cells.rb create mode 100644 nova/lib/puppet/type/nova_config.rb create mode 100644 nova/lib/puppet/type/nova_floating.rb create mode 100644 nova/lib/puppet/type/nova_network.rb create mode 100644 nova/lib/puppet/type/nova_paste_api_ini.rb create mode 100644 nova/lib/puppet/type/nova_project.rb create mode 100644 nova/manifests/api.pp create mode 100644 nova/manifests/cells.pp create mode 100644 nova/manifests/cert.pp create mode 100644 nova/manifests/client.pp create mode 100644 nova/manifests/compute.pp create mode 100644 nova/manifests/compute/file_hack.pp create mode 100644 nova/manifests/compute/libvirt.pp create mode 100644 nova/manifests/compute/neutron.pp create mode 100644 nova/manifests/compute/rbd.pp create mode 100644 nova/manifests/compute/spice.pp create mode 100644 nova/manifests/compute/vmware.pp create mode 100644 nova/manifests/compute/xenserver.pp create mode 100644 nova/manifests/conductor.pp create mode 100644 nova/manifests/config.pp create mode 100644 nova/manifests/consoleauth.pp create mode 100644 nova/manifests/db/mysql.pp create mode 100644 nova/manifests/db/mysql/host_access.pp create mode 100644 nova/manifests/db/postgresql.pp create mode 100644 nova/manifests/generic_service.pp create mode 100644 nova/manifests/init.pp create mode 100644 nova/manifests/keystone/auth.pp create mode 100644 nova/manifests/manage/cells.pp create mode 100644 nova/manifests/manage/floating.pp create mode 100644 nova/manifests/manage/network.pp create mode 100644 nova/manifests/migration/libvirt.pp create mode 100644 nova/manifests/network.pp create mode 100644 nova/manifests/network/bridge.pp create mode 100644 nova/manifests/network/flat.pp create mode 100644 nova/manifests/network/flatdhcp.pp create mode 100644 nova/manifests/network/neutron.pp create mode 100644 nova/manifests/network/vlan.pp create mode 100644 nova/manifests/objectstore.pp create mode 100644 nova/manifests/params.pp create mode 100644 nova/manifests/qpid.pp create mode 100644 nova/manifests/quota.pp create mode 100644 nova/manifests/rabbitmq.pp create mode 100644 nova/manifests/scheduler.pp create mode 100644 nova/manifests/scheduler/filter.pp create mode 100644 nova/manifests/spicehtml5proxy.pp create mode 100644 nova/manifests/utilities.pp create mode 100644 nova/manifests/vncproxy.pp create mode 100644 nova/spec/classes/nova_api_spec.rb create mode 100644 nova/spec/classes/nova_cells_spec.rb create mode 100644 nova/spec/classes/nova_cert_spec.rb create mode 100644 nova/spec/classes/nova_client_spec.rb create mode 100644 nova/spec/classes/nova_compute_libvirt_spec.rb create mode 100644 nova/spec/classes/nova_compute_neutron_spec.rb create mode 100644 nova/spec/classes/nova_compute_rbd_spec.rb create mode 100644 nova/spec/classes/nova_compute_spec.rb create mode 100644 nova/spec/classes/nova_compute_spice_spec.rb create mode 100644 nova/spec/classes/nova_compute_vmware_spec.rb create mode 100644 nova/spec/classes/nova_compute_xenserver_spec.rb create mode 100644 nova/spec/classes/nova_conductor_spec.rb create mode 100644 nova/spec/classes/nova_config_spec.rb create mode 100644 nova/spec/classes/nova_consoleauth_spec.rb create mode 100644 nova/spec/classes/nova_db_mysql_spec.rb create mode 100644 nova/spec/classes/nova_db_postgresql_spec.rb create mode 100644 nova/spec/classes/nova_init_spec.rb create mode 100644 nova/spec/classes/nova_keystone_endpoint_spec.rb create mode 100644 nova/spec/classes/nova_migration_libvirt_spec.rb create mode 100644 nova/spec/classes/nova_network_flat_spec.rb create mode 100644 nova/spec/classes/nova_network_flatdhcp_spec.rb create mode 100644 nova/spec/classes/nova_network_neutron_spec.rb create mode 100644 nova/spec/classes/nova_network_spec.rb create mode 100644 nova/spec/classes/nova_network_vlan_spec.rb create mode 100644 nova/spec/classes/nova_objectstore_spec.rb create mode 100644 nova/spec/classes/nova_qpid_spec.rb create mode 100644 nova/spec/classes/nova_quota_spec.rb create mode 100644 nova/spec/classes/nova_rabbitmq_spec.rb create mode 100644 nova/spec/classes/nova_scheduler_filter_spec.rb create mode 100644 nova/spec/classes/nova_scheduler_spec.rb create mode 100644 nova/spec/classes/nova_spicehtml5_proxy_spec.rb create mode 100644 nova/spec/classes/nova_utilities_spec.rb create mode 100644 nova/spec/classes/nova_vnc_proxy_spec.rb create mode 100644 nova/spec/defines/nova_generic_service_spec.rb create mode 100644 nova/spec/defines/nova_manage_networks_spec.rb create mode 100644 nova/spec/spec_helper.rb create mode 100644 nova/spec/unit/provider/nova_config/ini_setting_spec.rb create mode 100644 nova/spec/unit/type/nova_config_spec.rb create mode 100644 nova/spec/unit/type/nova_network_spec.rb create mode 100644 nova/templates/secret.xml-compute.erb create mode 100644 nova/tests/all.pp create mode 100644 nova/tests/multi.pp create mode 100644 nova/tests/site.pp create mode 100644 nssdb/CHANGELOG create mode 100644 nssdb/LICENSE create mode 100644 nssdb/Modulefile create mode 100644 nssdb/README.md create mode 100644 nssdb/manifests/add_cert_and_key.pp create mode 100644 nssdb/manifests/create.pp create mode 100644 nssdb/tests/create.pp create mode 100644 openstack/.fixtures.yml create mode 100644 openstack/.gitignore create mode 100644 openstack/.gitreview create mode 100644 openstack/.mailmap create mode 100644 openstack/.travis.yml create mode 100644 openstack/Gemfile create mode 100644 openstack/LICENSE create mode 100644 openstack/Modulefile create mode 100644 openstack/Puppetfile create mode 100644 openstack/README.md create mode 100644 openstack/Rakefile create mode 100644 openstack/files/RPM-GPG-KEY-EPEL-6 create mode 100644 openstack/files/RPM-GPG-KEY-RDO-Grizzly create mode 100644 openstack/files/RPM-GPG-KEY-RDO-Havana create mode 100644 openstack/manifests/all.pp create mode 100644 openstack/manifests/auth_file.pp create mode 100644 openstack/manifests/cinder/all.pp create mode 100644 openstack/manifests/cinder/controller.pp create mode 100644 openstack/manifests/cinder/storage.pp create mode 100644 openstack/manifests/client.pp create mode 100644 openstack/manifests/compute.pp create mode 100644 openstack/manifests/controller.pp create mode 100644 openstack/manifests/db/mysql.pp create mode 100644 openstack/manifests/glance.pp create mode 100644 openstack/manifests/horizon.pp create mode 100644 openstack/manifests/keystone.pp create mode 100644 openstack/manifests/neutron.pp create mode 100644 openstack/manifests/nova/controller.pp create mode 100644 openstack/manifests/provision.pp create mode 100644 openstack/manifests/repo.pp create mode 100644 openstack/manifests/repo/epel.pp create mode 100644 openstack/manifests/repo/rdo.pp create mode 100644 openstack/manifests/repo/uca.pp create mode 100644 openstack/manifests/repo/yum_refresh.pp create mode 100644 openstack/manifests/swift/device_endpoint.pp create mode 100644 openstack/manifests/swift/proxy.pp create mode 100644 openstack/manifests/swift/storage-node.pp create mode 100644 openstack/manifests/test_file.pp create mode 100644 openstack/spec/classes/openstack_all_spec.rb create mode 100644 openstack/spec/classes/openstack_auth_file_spec.rb create mode 100644 openstack/spec/classes/openstack_cinder_all_spec.rb create mode 100644 openstack/spec/classes/openstack_cinder_controller_spec.rb create mode 100644 openstack/spec/classes/openstack_cinder_storage_spec.rb create mode 100644 openstack/spec/classes/openstack_client_spec.rb create mode 100644 openstack/spec/classes/openstack_compute_spec.rb create mode 100644 openstack/spec/classes/openstack_controller_spec.rb create mode 100644 openstack/spec/classes/openstack_glance_spec.rb create mode 100644 openstack/spec/classes/openstack_horizon_spec.rb create mode 100644 openstack/spec/classes/openstack_keystone_spec.rb create mode 100644 openstack/spec/classes/openstack_neutron_spec.rb create mode 100644 openstack/spec/classes/openstack_nova_controller_spec.rb create mode 100644 openstack/spec/classes/openstack_provision_spec.rb create mode 100644 openstack/spec/classes/openstack_repo_spec.rb create mode 100644 openstack/spec/classes/openstack_repo_uca_spec.rb create mode 100644 openstack/spec/classes/openstack_swift_storage-node.rb create mode 100644 openstack/spec/classes/openstack_test_file_spec.rb create mode 100644 openstack/spec/spec_helper.rb create mode 100644 openstack/templates/openrc.erb create mode 100644 openstack/templates/test_nova.sh.erb create mode 100644 openstack/tests/all.pp create mode 100644 openstack/tests/auth_file.pp create mode 100644 openstack/tests/cloudcontroller.pp create mode 100644 openstack/tests/compute.pp create mode 100644 openstack/tests/site.pp create mode 100644 openstack/tests/testfile.pp create mode 100644 pacemaker/LICENSE create mode 100644 pacemaker/Modulefile create mode 100644 pacemaker/README.md create mode 100644 pacemaker/lib/puppet/provider/pcmk_constraint/default.rb create mode 100644 pacemaker/lib/puppet/provider/pcmk_resource/default.rb create mode 100644 pacemaker/lib/puppet/type/pcmk_constraint.rb create mode 100644 pacemaker/lib/puppet/type/pcmk_resource.rb create mode 100644 pacemaker/manifests/constraint/base.pp create mode 100644 pacemaker/manifests/constraint/colocation.pp create mode 100644 pacemaker/manifests/constraint/location.pp create mode 100644 pacemaker/manifests/corosync.pp create mode 100644 pacemaker/manifests/init.pp create mode 100644 pacemaker/manifests/install.pp create mode 100644 pacemaker/manifests/params.pp create mode 100644 pacemaker/manifests/resource/filesystem.pp create mode 100644 pacemaker/manifests/resource/ip.pp create mode 100644 pacemaker/manifests/resource/lsb.pp create mode 100644 pacemaker/manifests/resource/mysql.pp create mode 100644 pacemaker/manifests/resource/qpid_clustered.pp create mode 100644 pacemaker/manifests/service.pp create mode 100644 pacemaker/manifests/stonith.pp create mode 100644 pacemaker/manifests/stonith/fence_xvm.pp create mode 100644 pacemaker/manifests/stonith/ipmilan.pp create mode 100644 pacemaker/spec/spec_helper.rb create mode 100644 pacemaker/tests/init.pp create mode 100644 puppet/.gitignore create mode 100644 puppet/COPYING create mode 100644 puppet/COPYRIGHT create mode 100644 puppet/INSTALL create mode 100644 puppet/Makefile create mode 100644 puppet/README create mode 100644 puppet/VERSION create mode 100644 puppet/lib/facter/puppet_bug.rb create mode 100644 puppet/lib/facter/puppet_vardirtmp.rb create mode 100644 puppet/manifests/client.pp create mode 100644 puppet/manifests/common.pp create mode 100644 puppet/manifests/deploy.pp create mode 100644 puppet/manifests/init.pp create mode 100644 puppet/manifests/params.pp create mode 100644 puppet/manifests/params/package.pp create mode 100644 puppet/manifests/params/path.pp create mode 100644 puppet/manifests/params/repo.pp create mode 100644 puppet/manifests/params/repo/apt.pp create mode 100644 puppet/manifests/params/repo/yum.pp create mode 100644 puppet/manifests/puppetdb.pp create mode 100644 puppet/manifests/server.pp create mode 100644 puppet/manifests/vardir.pp create mode 100644 puppet/puppet-puppet.spec.in create mode 100644 puppet/templates/auth.conf.erb create mode 100644 puppet/templates/autosign.conf.erb create mode 100644 puppet/templates/fileserver.conf.erb create mode 100644 puppet/templates/puppet.conf.erb create mode 100644 puppet/templates/puppetdb.conf.erb create mode 100644 puppet/templates/routes.yaml.erb create mode 100644 qpid/CHANGELOG create mode 100644 qpid/LICENSE create mode 100644 qpid/Modulefile create mode 100644 qpid/README create mode 100644 qpid/lib/puppet/provider/qpid_user/default.rb create mode 100644 qpid/lib/puppet/provider/qpid_user/saslpasswd2.rb create mode 100644 qpid/lib/puppet/type/qpid_user.rb create mode 100644 qpid/manifests/server.pp create mode 100644 qpid/templates/qpidd.conf.erb create mode 100644 rsync/.fixtures.yml create mode 100644 rsync/.gemfile create mode 100644 rsync/.gitignore create mode 100644 rsync/CHANGELOG create mode 100644 rsync/LICENSE create mode 100644 rsync/Modulefile create mode 100644 rsync/README.markdown create mode 100644 rsync/Rakefile create mode 100644 rsync/files/motd create mode 100644 rsync/manifests/get.pp create mode 100644 rsync/manifests/init.pp create mode 100644 rsync/manifests/repo.pp create mode 100644 rsync/manifests/server.pp create mode 100644 rsync/manifests/server/module.pp create mode 100644 rsync/spec/classes/server_spec.rb create mode 100644 rsync/spec/defines/get_spec.rb create mode 100644 rsync/spec/defines/server_module_spec.rb create mode 100644 rsync/spec/fixtures/manifests/site.pp create mode 100644 rsync/spec/spec.opts create mode 100644 rsync/spec/spec_helper.rb create mode 100644 rsync/templates/header.erb create mode 100644 rsync/templates/module.erb create mode 100644 rsync/tests/init.pp create mode 100644 rsync/tests/repo.pp create mode 100644 rsync/tests/server_with_motd.pp create mode 100644 ssh/.gitignore create mode 100644 ssh/LICENSE create mode 100644 ssh/Modulefile create mode 100644 ssh/README.markdown create mode 100644 ssh/files/ssh_config create mode 100644 ssh/files/sshd_config create mode 100644 ssh/lib/puppet/parser/functions/ipaddresses.rb create mode 100644 ssh/manifests/client.pp create mode 100644 ssh/manifests/client/config.pp create mode 100644 ssh/manifests/client/install.pp create mode 100644 ssh/manifests/hostkeys.pp create mode 100644 ssh/manifests/init.pp create mode 100644 ssh/manifests/knownhosts.pp create mode 100644 ssh/manifests/params.pp create mode 100644 ssh/manifests/server.pp create mode 100644 ssh/manifests/server/config.pp create mode 100644 ssh/manifests/server/configline.pp create mode 100644 ssh/manifests/server/install.pp create mode 100644 ssh/manifests/server/service.pp create mode 100644 ssh/spec/spec.opts create mode 100644 ssh/spec/spec_helper.rb create mode 100644 ssh/tests/init.pp create mode 100644 stdlib/.gemspec create mode 100644 stdlib/.gitignore create mode 100644 stdlib/.project create mode 100644 stdlib/.rspec create mode 100644 stdlib/.travis.yml create mode 100644 stdlib/CHANGELOG create mode 100644 stdlib/CONTRIBUTING.md create mode 100644 stdlib/Gemfile create mode 100644 stdlib/LICENSE create mode 100644 stdlib/Modulefile create mode 100644 stdlib/README.markdown create mode 100644 stdlib/README_DEVELOPER.markdown create mode 100644 stdlib/README_SPECS.markdown create mode 100644 stdlib/RELEASE_PROCESS.markdown create mode 100644 stdlib/Rakefile create mode 100644 stdlib/lib/facter/facter_dot_d.rb create mode 100644 stdlib/lib/facter/pe_version.rb create mode 100644 stdlib/lib/facter/puppet_vardir.rb create mode 100644 stdlib/lib/facter/root_home.rb create mode 100644 stdlib/lib/facter/util/puppet_settings.rb create mode 100644 stdlib/lib/puppet/parser/functions/abs.rb create mode 100644 stdlib/lib/puppet/parser/functions/any2array.rb create mode 100644 stdlib/lib/puppet/parser/functions/base64.rb create mode 100644 stdlib/lib/puppet/parser/functions/bool2num.rb create mode 100644 stdlib/lib/puppet/parser/functions/capitalize.rb create mode 100644 stdlib/lib/puppet/parser/functions/chomp.rb create mode 100644 stdlib/lib/puppet/parser/functions/chop.rb create mode 100644 stdlib/lib/puppet/parser/functions/concat.rb create mode 100644 stdlib/lib/puppet/parser/functions/count.rb create mode 100644 stdlib/lib/puppet/parser/functions/defined_with_params.rb create mode 100644 stdlib/lib/puppet/parser/functions/delete.rb create mode 100644 stdlib/lib/puppet/parser/functions/delete_at.rb create mode 100644 stdlib/lib/puppet/parser/functions/difference.rb create mode 100644 stdlib/lib/puppet/parser/functions/dirname.rb create mode 100644 stdlib/lib/puppet/parser/functions/downcase.rb create mode 100644 stdlib/lib/puppet/parser/functions/empty.rb create mode 100644 stdlib/lib/puppet/parser/functions/ensure_packages.rb create mode 100644 stdlib/lib/puppet/parser/functions/ensure_resource.rb create mode 100644 stdlib/lib/puppet/parser/functions/flatten.rb create mode 100644 stdlib/lib/puppet/parser/functions/floor.rb create mode 100644 stdlib/lib/puppet/parser/functions/fqdn_rotate.rb create mode 100644 stdlib/lib/puppet/parser/functions/get_module_path.rb create mode 100644 stdlib/lib/puppet/parser/functions/getparam.rb create mode 100644 stdlib/lib/puppet/parser/functions/getvar.rb create mode 100644 stdlib/lib/puppet/parser/functions/grep.rb create mode 100644 stdlib/lib/puppet/parser/functions/has_interface_with.rb create mode 100644 stdlib/lib/puppet/parser/functions/has_ip_address.rb create mode 100644 stdlib/lib/puppet/parser/functions/has_ip_network.rb create mode 100644 stdlib/lib/puppet/parser/functions/has_key.rb create mode 100644 stdlib/lib/puppet/parser/functions/hash.rb create mode 100644 stdlib/lib/puppet/parser/functions/intersection.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_array.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_domain_name.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_float.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_function_available.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_hash.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_integer.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_ip_address.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_mac_address.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_numeric.rb create mode 100644 stdlib/lib/puppet/parser/functions/is_string.rb create mode 100644 stdlib/lib/puppet/parser/functions/join.rb create mode 100644 stdlib/lib/puppet/parser/functions/join_keys_to_values.rb create mode 100644 stdlib/lib/puppet/parser/functions/keys.rb create mode 100644 stdlib/lib/puppet/parser/functions/loadyaml.rb create mode 100644 stdlib/lib/puppet/parser/functions/lstrip.rb create mode 100644 stdlib/lib/puppet/parser/functions/max.rb create mode 100644 stdlib/lib/puppet/parser/functions/member.rb create mode 100644 stdlib/lib/puppet/parser/functions/merge.rb create mode 100644 stdlib/lib/puppet/parser/functions/min.rb create mode 100644 stdlib/lib/puppet/parser/functions/num2bool.rb create mode 100644 stdlib/lib/puppet/parser/functions/parsejson.rb create mode 100644 stdlib/lib/puppet/parser/functions/parseyaml.rb create mode 100644 stdlib/lib/puppet/parser/functions/pick.rb create mode 100644 stdlib/lib/puppet/parser/functions/prefix.rb create mode 100644 stdlib/lib/puppet/parser/functions/range.rb create mode 100644 stdlib/lib/puppet/parser/functions/reject.rb create mode 100644 stdlib/lib/puppet/parser/functions/reverse.rb create mode 100644 stdlib/lib/puppet/parser/functions/rstrip.rb create mode 100644 stdlib/lib/puppet/parser/functions/shuffle.rb create mode 100644 stdlib/lib/puppet/parser/functions/size.rb create mode 100644 stdlib/lib/puppet/parser/functions/sort.rb create mode 100644 stdlib/lib/puppet/parser/functions/squeeze.rb create mode 100644 stdlib/lib/puppet/parser/functions/str2bool.rb create mode 100644 stdlib/lib/puppet/parser/functions/str2saltedsha512.rb create mode 100644 stdlib/lib/puppet/parser/functions/strftime.rb create mode 100644 stdlib/lib/puppet/parser/functions/strip.rb create mode 100644 stdlib/lib/puppet/parser/functions/suffix.rb create mode 100644 stdlib/lib/puppet/parser/functions/swapcase.rb create mode 100644 stdlib/lib/puppet/parser/functions/time.rb create mode 100644 stdlib/lib/puppet/parser/functions/to_bytes.rb create mode 100644 stdlib/lib/puppet/parser/functions/type.rb create mode 100644 stdlib/lib/puppet/parser/functions/union.rb create mode 100644 stdlib/lib/puppet/parser/functions/unique.rb create mode 100644 stdlib/lib/puppet/parser/functions/upcase.rb create mode 100644 stdlib/lib/puppet/parser/functions/uriescape.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_absolute_path.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_array.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_augeas.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_bool.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_cmd.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_hash.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_ipv4_address.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_ipv6_address.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_re.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_slength.rb create mode 100644 stdlib/lib/puppet/parser/functions/validate_string.rb create mode 100644 stdlib/lib/puppet/parser/functions/values.rb create mode 100644 stdlib/lib/puppet/parser/functions/values_at.rb create mode 100644 stdlib/lib/puppet/parser/functions/zip.rb create mode 100644 stdlib/lib/puppet/provider/file_line/ruby.rb create mode 100644 stdlib/lib/puppet/type/anchor.rb create mode 100644 stdlib/lib/puppet/type/file_line.rb create mode 100644 stdlib/manifests/init.pp create mode 100644 stdlib/manifests/stages.pp create mode 100644 stdlib/spec/classes/anchor_spec.rb create mode 100644 stdlib/spec/functions/defined_with_params_spec.rb create mode 100644 stdlib/spec/functions/ensure_packages_spec.rb create mode 100644 stdlib/spec/functions/ensure_resource_spec.rb create mode 100644 stdlib/spec/functions/getparam_spec.rb create mode 100755 stdlib/spec/monkey_patches/alias_should_to_must.rb create mode 100755 stdlib/spec/monkey_patches/publicize_methods.rb create mode 100644 stdlib/spec/spec.opts create mode 100644 stdlib/spec/spec_helper.rb create mode 100644 stdlib/spec/unit/facter/pe_required_facts_spec.rb create mode 100644 stdlib/spec/unit/facter/pe_version_spec.rb create mode 100644 stdlib/spec/unit/facter/root_home_spec.rb create mode 100644 stdlib/spec/unit/facter/util/puppet_settings_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/abs_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/any2array_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/base64_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/bool2num_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/capitalize_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/chomp_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/chop_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/concat_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/count_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/delete_at_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/delete_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/difference_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/dirname_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/downcase_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/empty_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/flatten_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/floor_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/fqdn_rotate_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/get_module_path_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/getvar_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/grep_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/has_interface_with_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/has_ip_address_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/has_ip_network_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/has_key_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/hash_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/intersection_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_array_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_domain_name_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_float_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_function_available.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_hash_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_integer_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_ip_address_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_mac_address_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_numeric_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/is_string_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/join_keys_to_values_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/join_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/keys_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/lstrip_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/max_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/member_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/merge_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/min_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/num2bool_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/parsejson_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/parseyaml_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/pick_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/prefix_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/range_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/reject_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/reverse_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/rstrip_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/shuffle_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/size_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/sort_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/squeeze_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/str2bool_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/str2saltedsha512_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/strftime_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/strip_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/suffix_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/swapcase_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/time_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/to_bytes_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/type_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/union_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/unique_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/upcase_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/uriescape_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_absolute_path_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_array_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_augeas_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_bool_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_cmd_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_hash_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_ipv4_address_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_ipv6_address_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_re_spec.rb create mode 100755 stdlib/spec/unit/puppet/parser/functions/validate_slength_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/validate_string_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/values_at_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/values_spec.rb create mode 100644 stdlib/spec/unit/puppet/parser/functions/zip_spec.rb create mode 100644 stdlib/spec/unit/puppet/provider/file_line/ruby_spec.rb create mode 100644 stdlib/spec/unit/puppet/type/anchor_spec.rb create mode 100644 stdlib/spec/unit/puppet/type/file_line_spec.rb create mode 100644 stdlib/spec/watchr.rb create mode 100644 stdlib/tests/file_line.pp create mode 100644 stdlib/tests/has_interface_with.pp create mode 100644 stdlib/tests/has_ip_address.pp create mode 100644 stdlib/tests/has_ip_network.pp create mode 100644 stdlib/tests/init.pp create mode 100644 swift/.fixtures.yml create mode 100644 swift/.gemfile create mode 100644 swift/.gitignore create mode 100644 swift/.gitreview create mode 100644 swift/Gemfile create mode 100644 swift/LICENSE create mode 100644 swift/Modulefile create mode 100644 swift/README.md create mode 100644 swift/Rakefile create mode 100644 swift/ext/swift.rb create mode 100644 swift/ext/swift_keystone_test.rb create mode 100644 swift/files/swift-account.conf.upstart create mode 100644 swift/files/swift-container-sync.conf.upstart create mode 100644 swift/files/swift-container.conf.upstart create mode 100644 swift/files/swift-object.conf.upstart create mode 100644 swift/files/swift_tester.rb create mode 100644 swift/lib/puppet/provider/ring_account_device/swift_ring_builder.rb create mode 100644 swift/lib/puppet/provider/ring_container_device/swift_ring_builder.rb create mode 100644 swift/lib/puppet/provider/ring_object_device/swift_ring_builder.rb create mode 100644 swift/lib/puppet/provider/swift_account_config/ini_setting.rb create mode 100644 swift/lib/puppet/provider/swift_bench_config/ini_setting.rb create mode 100644 swift/lib/puppet/provider/swift_config/ini_setting.rb create mode 100644 swift/lib/puppet/provider/swift_container_config/ini_setting.rb create mode 100644 swift/lib/puppet/provider/swift_dispersion_config/ini_setting.rb create mode 100644 swift/lib/puppet/provider/swift_object_config/ini_setting.rb create mode 100644 swift/lib/puppet/provider/swift_proxy_config/ini_setting.rb create mode 100644 swift/lib/puppet/provider/swift_ring_builder.rb create mode 100644 swift/lib/puppet/type/ring_account_device.rb create mode 100644 swift/lib/puppet/type/ring_container_device.rb create mode 100644 swift/lib/puppet/type/ring_object_device.rb create mode 100644 swift/lib/puppet/type/swift_account_config.rb create mode 100644 swift/lib/puppet/type/swift_bench_config.rb create mode 100644 swift/lib/puppet/type/swift_config.rb create mode 100644 swift/lib/puppet/type/swift_container_config.rb create mode 100644 swift/lib/puppet/type/swift_dispersion_config.rb create mode 100644 swift/lib/puppet/type/swift_object_config.rb create mode 100644 swift/lib/puppet/type/swift_proxy_config.rb create mode 100644 swift/manifests/auth_file.pp create mode 100644 swift/manifests/bench.pp create mode 100644 swift/manifests/client.pp create mode 100644 swift/manifests/dispersion.pp create mode 100644 swift/manifests/init.pp create mode 100644 swift/manifests/keystone/auth.pp create mode 100644 swift/manifests/keystone/dispersion.pp create mode 100644 swift/manifests/params.pp create mode 100644 swift/manifests/proxy.pp create mode 100644 swift/manifests/proxy/account_quotas.pp create mode 100644 swift/manifests/proxy/authtoken.pp create mode 100644 swift/manifests/proxy/bulk.pp create mode 100644 swift/manifests/proxy/cache.pp create mode 100644 swift/manifests/proxy/catch_errors.pp create mode 100644 swift/manifests/proxy/ceilometer.pp create mode 100644 swift/manifests/proxy/container_quotas.pp create mode 100644 swift/manifests/proxy/formpost.pp create mode 100644 swift/manifests/proxy/healthcheck.pp create mode 100644 swift/manifests/proxy/keystone.pp create mode 100644 swift/manifests/proxy/proxy-logging.pp create mode 100644 swift/manifests/proxy/proxy_logging.pp create mode 100644 swift/manifests/proxy/ratelimit.pp create mode 100644 swift/manifests/proxy/s3token.pp create mode 100644 swift/manifests/proxy/staticweb.pp create mode 100644 swift/manifests/proxy/swauth.pp create mode 100644 swift/manifests/proxy/swift3.pp create mode 100644 swift/manifests/proxy/tempauth.pp create mode 100644 swift/manifests/proxy/tempurl.pp create mode 100644 swift/manifests/ringbuilder.pp create mode 100644 swift/manifests/ringbuilder/create.pp create mode 100644 swift/manifests/ringbuilder/rebalance.pp create mode 100644 swift/manifests/ringserver.pp create mode 100644 swift/manifests/ringsync.pp create mode 100644 swift/manifests/storage.pp create mode 100644 swift/manifests/storage/account.pp create mode 100644 swift/manifests/storage/all.pp create mode 100644 swift/manifests/storage/container.pp create mode 100644 swift/manifests/storage/disk.pp create mode 100644 swift/manifests/storage/ext4.pp create mode 100644 swift/manifests/storage/filter/healthcheck.pp create mode 100644 swift/manifests/storage/filter/recon.pp create mode 100644 swift/manifests/storage/generic.pp create mode 100644 swift/manifests/storage/loopback.pp create mode 100644 swift/manifests/storage/mount.pp create mode 100644 swift/manifests/storage/node.pp create mode 100644 swift/manifests/storage/object.pp create mode 100644 swift/manifests/storage/server.pp create mode 100644 swift/manifests/storage/xfs.pp create mode 100644 swift/manifests/test_file.pp create mode 100644 swift/manifests/xfs.pp create mode 100644 swift/spec/classes/swift_bench_spec.rb create mode 100644 swift/spec/classes/swift_client_spec.rb create mode 100644 swift/spec/classes/swift_dispersion_spec.rb create mode 100644 swift/spec/classes/swift_keystone_auth_spec.rb create mode 100644 swift/spec/classes/swift_keystone_dispersion_spec.rb create mode 100644 swift/spec/classes/swift_proxy_account_quotas_spec.rb create mode 100644 swift/spec/classes/swift_proxy_authtoken_spec.rb create mode 100644 swift/spec/classes/swift_proxy_bulk_spec.rb create mode 100644 swift/spec/classes/swift_proxy_cache_spec.rb create mode 100644 swift/spec/classes/swift_proxy_catch_errors_spec.rb create mode 100644 swift/spec/classes/swift_proxy_ceilometer_spec.rb create mode 100644 swift/spec/classes/swift_proxy_container_quotas_spec.rb create mode 100644 swift/spec/classes/swift_proxy_formpost_spec.rb create mode 100644 swift/spec/classes/swift_proxy_healthcheck_spec.rb create mode 100644 swift/spec/classes/swift_proxy_keystone_spec.rb create mode 100644 swift/spec/classes/swift_proxy_ratelimit_spec.rb create mode 100644 swift/spec/classes/swift_proxy_s3token_spec.rb create mode 100644 swift/spec/classes/swift_proxy_spec.rb create mode 100644 swift/spec/classes/swift_proxy_staticweb_spec.rb create mode 100644 swift/spec/classes/swift_proxy_swauth_spec.rb create mode 100644 swift/spec/classes/swift_proxy_swift3_spec.rb create mode 100644 swift/spec/classes/swift_proxy_tempurl_spec.rb create mode 100644 swift/spec/classes/swift_ringbuilder_spec.rb create mode 100644 swift/spec/classes/swift_spec.rb create mode 100644 swift/spec/classes/swift_storage_account_spec.rb create mode 100644 swift/spec/classes/swift_storage_all_spec.rb create mode 100644 swift/spec/classes/swift_storage_container_spec.rb create mode 100644 swift/spec/classes/swift_storage_object_spec.rb create mode 100644 swift/spec/classes/swift_storage_spec.rb create mode 100644 swift/spec/classes/swift_test_file_spec.rb create mode 100644 swift/spec/classes/swift_xfs_spec.rb create mode 100644 swift/spec/defines/swift_ringbuilder_create_spec.rb create mode 100644 swift/spec/defines/swift_ringbuilder_rebalance_spec.rb create mode 100644 swift/spec/defines/swift_storage_disk_spec.rb create mode 100644 swift/spec/defines/swift_storage_filter_healthcheck_spec.rb create mode 100644 swift/spec/defines/swift_storage_filter_recon_spec.rb create mode 100644 swift/spec/defines/swift_storage_generic_spec.rb create mode 100644 swift/spec/defines/swift_storage_loopback_spec.rb create mode 100644 swift/spec/defines/swift_storage_mount_spec.rb create mode 100644 swift/spec/defines/swift_storage_node_spec.rb create mode 100644 swift/spec/defines/swift_storage_server_spec.rb create mode 100644 swift/spec/defines/swift_storage_xfs_spec.rb create mode 100644 swift/spec/spec.opts create mode 100644 swift/spec/spec_helper.rb create mode 100644 swift/spec/unit/puppet/provider/swift_ring_builder_spec.rb create mode 100644 swift/spec/unit/puppet/type/ring_account_device_spec.rb create mode 100644 swift/spec/unit/puppet/type/ring_container_device_spec.rb create mode 100644 swift/spec/unit/puppet/type/ring_object_device_spec.rb create mode 100644 swift/templates/account-server.conf.erb create mode 100644 swift/templates/container-server.conf.erb create mode 100644 swift/templates/object-server.conf.erb create mode 100644 swift/templates/proxy-server.conf.erb create mode 100644 swift/templates/proxy/account_quotas.conf.erb create mode 100644 swift/templates/proxy/authtoken.conf.erb create mode 100644 swift/templates/proxy/bulk.conf.erb create mode 100644 swift/templates/proxy/cache.conf.erb create mode 100644 swift/templates/proxy/catch_errors.conf.erb create mode 100644 swift/templates/proxy/ceilometer.conf.erb create mode 100644 swift/templates/proxy/container_quotas.conf.erb create mode 100644 swift/templates/proxy/formpost.conf.erb create mode 100644 swift/templates/proxy/healthcheck.conf.erb create mode 100644 swift/templates/proxy/keystone.conf.erb create mode 100644 swift/templates/proxy/proxy-logging.conf.erb create mode 100644 swift/templates/proxy/ratelimit.conf.erb create mode 100644 swift/templates/proxy/s3token.conf.erb create mode 100644 swift/templates/proxy/staticweb.conf.erb create mode 100644 swift/templates/proxy/swauth.conf.erb create mode 100644 swift/templates/proxy/swift3.conf.erb create mode 100644 swift/templates/proxy/tempauth.conf.erb create mode 100644 swift/templates/proxy/tempurl.conf.erb create mode 100644 swift/templates/recon.conf.erb create mode 100644 swift/templates/rsyncd.conf.erb create mode 100644 swift/templates/swift_keystone_test.erb create mode 100644 swift/tests/all.pp create mode 100644 swift/tests/site.pp create mode 100644 sysctl/README create mode 100644 sysctl/lib/puppet/provider/sysctl/parsed.rb create mode 100644 sysctl/lib/puppet/type/sysctl.rb create mode 100644 sysctl/manifests/value.pp create mode 100644 tempest/.gitignore create mode 100644 tempest/.gitreview create mode 100644 tempest/Gemfile create mode 100644 tempest/Modulefile create mode 100644 tempest/README.markdown create mode 100644 tempest/Rakefile create mode 100644 tempest/lib/puppet/provider/tempest_config/ini_setting.rb create mode 100644 tempest/lib/puppet/provider/tempest_glance_id_setter/ruby.rb create mode 100644 tempest/lib/puppet/provider/tempest_neutron_net_id_setter/ruby.rb create mode 100644 tempest/lib/puppet/type/tempest_config.rb create mode 100644 tempest/lib/puppet/type/tempest_glance_id_setter.rb create mode 100644 tempest/lib/puppet/type/tempest_neutron_net_id_setter.rb create mode 100644 tempest/manifests/init.pp create mode 100644 tempest/manifests/params.pp create mode 100644 vcsrepo/.gitignore create mode 100644 vcsrepo/.travis.yml create mode 100644 vcsrepo/Gemfile create mode 100644 vcsrepo/LICENSE create mode 100644 vcsrepo/Modulefile create mode 100644 vcsrepo/README.BZR.markdown create mode 100644 vcsrepo/README.CVS.markdown create mode 100644 vcsrepo/README.GIT.markdown create mode 100644 vcsrepo/README.HG.markdown create mode 100644 vcsrepo/README.SVN.markdown create mode 100644 vcsrepo/README.markdown create mode 100644 vcsrepo/Rakefile create mode 100644 vcsrepo/examples/bzr/branch.pp create mode 100644 vcsrepo/examples/bzr/init_repo.pp create mode 100644 vcsrepo/examples/cvs/local.pp create mode 100644 vcsrepo/examples/cvs/remote.pp create mode 100644 vcsrepo/examples/git/bare_init.pp create mode 100644 vcsrepo/examples/git/clone.pp create mode 100644 vcsrepo/examples/git/working_copy_init.pp create mode 100644 vcsrepo/examples/hg/clone.pp create mode 100644 vcsrepo/examples/hg/init_repo.pp create mode 100644 vcsrepo/examples/svn/checkout.pp create mode 100644 vcsrepo/examples/svn/server.pp create mode 100644 vcsrepo/lib/puppet/provider/vcsrepo.rb create mode 100644 vcsrepo/lib/puppet/provider/vcsrepo/bzr.rb create mode 100644 vcsrepo/lib/puppet/provider/vcsrepo/cvs.rb create mode 100644 vcsrepo/lib/puppet/provider/vcsrepo/dummy.rb create mode 100644 vcsrepo/lib/puppet/provider/vcsrepo/git.rb create mode 100644 vcsrepo/lib/puppet/provider/vcsrepo/hg.rb create mode 100644 vcsrepo/lib/puppet/provider/vcsrepo/svn.rb create mode 100644 vcsrepo/lib/puppet/type/vcsrepo.rb create mode 100644 vcsrepo/spec/fixtures/bzr_version_info.txt create mode 100644 vcsrepo/spec/fixtures/git_branch_a.txt create mode 100644 vcsrepo/spec/fixtures/hg_parents.txt create mode 100644 vcsrepo/spec/fixtures/hg_tags.txt create mode 100644 vcsrepo/spec/fixtures/svn_info.txt create mode 100644 vcsrepo/spec/spec.opts create mode 100644 vcsrepo/spec/spec_helper.rb create mode 100644 vcsrepo/spec/support/filesystem_helpers.rb create mode 100644 vcsrepo/spec/support/fixture_helpers.rb create mode 100644 vcsrepo/spec/support/provider_example_group.rb create mode 100644 vcsrepo/spec/unit/puppet/provider/vcsrepo/bzr_spec.rb create mode 100644 vcsrepo/spec/unit/puppet/provider/vcsrepo/cvs_spec.rb create mode 100644 vcsrepo/spec/unit/puppet/provider/vcsrepo/dummy_spec.rb create mode 100644 vcsrepo/spec/unit/puppet/provider/vcsrepo/git_spec.rb create mode 100644 vcsrepo/spec/unit/puppet/provider/vcsrepo/hg_spec.rb create mode 100644 vcsrepo/spec/unit/puppet/provider/vcsrepo/svn_spec.rb create mode 100644 vcsrepo/spec/unit/puppet/type/README.markdown create mode 100644 vlan/LICENSE create mode 100644 vlan/README create mode 100644 vlan/manifests/init.pp create mode 100644 vlan/templates/ifcfg.erb create mode 100644 vswitch/.fixtures.yml create mode 100644 vswitch/.gitignore create mode 100644 vswitch/.gitreview create mode 100644 vswitch/Gemfile create mode 100644 vswitch/Gemfile.lock create mode 100644 vswitch/LICENSE create mode 100644 vswitch/Modulefile create mode 100644 vswitch/README.md create mode 100644 vswitch/Rakefile create mode 100644 vswitch/lib/puppet/provider/vs_bridge/ovs.rb create mode 100644 vswitch/lib/puppet/provider/vs_bridge/ovs_redhat.rb create mode 100644 vswitch/lib/puppet/provider/vs_port/ovs.rb create mode 100644 vswitch/lib/puppet/provider/vs_port/ovs_redhat.rb create mode 100644 vswitch/lib/puppet/type/vs_bridge.rb create mode 100644 vswitch/lib/puppet/type/vs_port.rb create mode 100644 vswitch/manifests/init.pp create mode 100644 vswitch/manifests/ovs.pp create mode 100644 vswitch/manifests/params.pp create mode 100644 vswitch/spec/classes/vswitch_ovs_spec.rb create mode 100644 vswitch/spec/spec_helper.rb create mode 100644 xinetd/.fixtures.yml create mode 100644 xinetd/.gemfile create mode 100644 xinetd/.gitignore create mode 100644 xinetd/CHANGELOG create mode 100644 xinetd/LICENSE create mode 100644 xinetd/Modulefile create mode 100644 xinetd/README create mode 100644 xinetd/Rakefile create mode 100644 xinetd/files/xinetd.conf create mode 100644 xinetd/manifests/init.pp create mode 100644 xinetd/manifests/params.pp create mode 100644 xinetd/manifests/service.pp create mode 100644 xinetd/spec/classes/xinetd_init_spec.rb create mode 100644 xinetd/spec/defines/xinetd_service_spec.rb create mode 100644 xinetd/spec/fixtures/manifests/site.pp create mode 100644 xinetd/spec/spec.opts create mode 100644 xinetd/spec/spec_helper.rb create mode 100644 xinetd/templates/service.erb create mode 100644 xinetd/tests/init.pp diff --git a/apache/.fixtures.yml b/apache/.fixtures.yml new file mode 100644 index 000000000..b5f76c03a --- /dev/null +++ b/apache/.fixtures.yml @@ -0,0 +1,6 @@ +fixtures: + repositories: + stdlib: "git://github.com/puppetlabs/puppetlabs-stdlib.git" + concat: "git://github.com/puppetlabs/puppetlabs-concat.git" + symlinks: + apache: "#{source_dir}" diff --git a/apache/.gitignore b/apache/.gitignore new file mode 100644 index 000000000..e8f52b445 --- /dev/null +++ b/apache/.gitignore @@ -0,0 +1,7 @@ +.pkg +Gemfile.lock +vendor +spec/fixtures +.rspec_system +.bundle +.*sw* diff --git a/apache/.nodeset.yml b/apache/.nodeset.yml new file mode 100644 index 000000000..767f9cd2f --- /dev/null +++ b/apache/.nodeset.yml @@ -0,0 +1,31 @@ +--- +default_set: 'centos-64-x64' +sets: + 'centos-59-x64': + nodes: + "main.foo.vm": + prefab: 'centos-59-x64' + 'centos-64-x64': + nodes: + "main.foo.vm": + prefab: 'centos-64-x64' + 'fedora-18-x64': + nodes: + "main.foo.vm": + prefab: 'fedora-18-x64' + 'debian-607-x64': + nodes: + "main.foo.vm": + prefab: 'debian-607-x64' + 'debian-70rc1-x64': + nodes: + "main.foo.vm": + prefab: 'debian-70rc1-x64' + 'ubuntu-server-10044-x64': + nodes: + "main.foo.vm": + prefab: 'ubuntu-server-10044-x64' + 'ubuntu-server-12042-x64': + nodes: + "main.foo.vm": + prefab: 'ubuntu-server-12042-x64' diff --git a/apache/.travis.yml b/apache/.travis.yml new file mode 100644 index 000000000..5efc64fa7 --- /dev/null +++ b/apache/.travis.yml @@ -0,0 +1,40 @@ +--- +branches: + only: + - master +language: ruby +bundler_args: --without development +script: "bundle exec rake spec SPEC_OPTS='--format documentation'" +after_success: + - git clone -q git://github.com/puppetlabs/ghpublisher.git .forge-releng + - .forge-releng/publish +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 +env: + matrix: + - PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" + - PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" + - PUPPET_GEM_VERSION="~> 3.0" + global: + - PUBLISHER_LOGIN=puppetlabs + - secure: |- + MO4pB4bqBQJjm2yFHf3Mgho+y0Qv4GmMxTMhzI02tGy1V0HMtruZbR7EBN0i + n2CiR7V9V0mNR7/ymzDMF9yVBcgqyXMsp/C6u992Dd0U63ZwFpbRWkxuAeEY + ioupWBkiczjVEo+sxn+gVOnx28pcH/X8kDWbr6wFOMIjO03K66Y= +matrix: + fast_finish: true + exclude: + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.6.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" FACTER_GEM_VERSION="~> 1.7.0" + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 3.2.0" +notifications: + email: false diff --git a/apache/CHANGELOG.md b/apache/CHANGELOG.md new file mode 100644 index 000000000..9528972ec --- /dev/null +++ b/apache/CHANGELOG.md @@ -0,0 +1,172 @@ +## 2013-12-05 Release 0.10.0 +### Summary: + +This release adds FreeBSD osfamily support and various other improvements to some mods. + +### Features: + +- Add suPHP_UserGroup directive to directory context +- Add support for ScriptAliasMatch directives +- Set SSLOptions StdEnvVars in server context +- No implicit entry for ScriptAlias path +- Add support for overriding ErrorDocument +- Add support for AliasMatch directives +- Disable default "allow from all" in vhost-directories +- Add WSGIPythonPath as an optional parameter to mod_wsgi. +- Add mod_rpaf support +- Add directives: IndexOptions, IndexOrderDefault +- Add ability to include additional external configurations in vhost +- need to use the provider variable not the provider key value from the directory hash for matches +- Support for FreeBSD and few other features +- Add new params to apache::mod::mime class +- Allow apache::mod to specify module id and path +- added $server_root parameter +- Add Allow and ExtendedStatus support to mod_status +- Expand vhost/_directories.pp directive support +- Add initial support for nss module (no directives in vhost template yet) +- added peruser and event mpms +- added $service_name parameter +- add parameter for TraceEnable +- Make LogLevel configurable for server and vhost +- Add documentation about $ip +- Add ability to pass ip (instead of wildcard) in default vhost files + +### Bugfixes: + +- Don't listen on port or set NameVirtualHost for non-existent vhost +- only apply Directory defaults when provider is a directory +- Working mod_authnz_ldap support on Debian/Ubuntu + +## 2013-09-06 Release 0.9.0 +### Summary: +This release adds more parameters to the base apache class and apache defined +resource to make the module more flexible. It also adds or enhances SuPHP, +WSGI, and Passenger mod support, and support for the ITK mpm module. + +### Backwards-incompatible Changes: +- Remove many default mods that are not normally needed. +- Remove `rewrite_base` `apache::vhost` parameter; did not work anyway. +- Specify dependencies on stdlib >=2.4.0 (this was already the case, but +making explicit) +- Deprecate `a2mod` in favor of the `apache::mod::*` classes and `apache::mod` +defined resource. + +### Features: +- `apache` class + - Add `httpd_dir` parameter to change the location of the configuration + files. + - Add `logroot` parameter to change the logroot + - Add `ports_file` parameter to changes the `ports.conf` file location + - Add `keepalive` parameter to enable persistent connections + - Add `keepalive_timeout` parameter to change the timeout + - Update `default_mods` to be able to take an array of mods to enable. +- `apache::vhost` + - Add `wsgi_daemon_process`, `wsgi_daemon_process_options`, + `wsgi_process_group`, and `wsgi_script_aliases` parameters for per-vhost + WSGI configuration. + - Add `access_log_syslog` parameter to enable syslogging. + - Add `error_log_syslog` parameter to enable syslogging of errors. + - Add `directories` hash parameter. Please see README for documentation. + - Add `sslproxyengine` parameter to enable SSLProxyEngine + - Add `suphp_addhandler`, `suphp_engine`, and `suphp_configpath` for + configuring SuPHP. + - Add `custom_fragment` parameter to allow for arbitrary apache + configuration injection. (Feature pull requests are prefered over using + this, but it is available in a pinch.) +- Add `apache::mod::suphp` class for configuring SuPHP. +- Add `apache::mod::itk` class for configuring ITK mpm module. +- Update `apache::mod::wsgi` class for global WSGI configuration with +`wsgi_socket_prefix` and `wsgi_python_home` parameters. +- Add README.passenger.md to document the `apache::mod::passenger` usage. +Added `passenger_high_performance`, `passenger_pool_idle_time`, +`passenger_max_requests`, `passenger_stat_throttle_rate`, `rack_autodetect`, +and `rails_autodetect` parameters. +- Separate the httpd service resource into a new `apache::service` class for +dependency chaining of `Class['apache'] -> ~> +Class['apache::service']` +- Added `apache::mod::proxy_balancer` class for `apache::balancer` + +### Bugfixes: +- Change dependency to puppetlabs-concat +- Fix ruby 1.9 bug for `a2mod` +- Change servername to be `$::hostname` if there is no `$::fqdn` +- Make `/etc/ssl/certs` the default ssl certs directory for RedHat non-5. +- Make `php` the default php package for RedHat non-5. +- Made `aliases` able to take a single alias hash instead of requiring an +array. + +## 2013-07-26 Release 0.8.1 +### Bugfixes: +- Update `apache::mpm_module` detection for worker/prefork +- Update `apache::mod::cgi` and `apache::mod::cgid` detection for +worker/prefork + +## 2013-07-16 Release 0.8.0 +### Features: +- Add `servername` parameter to `apache` class +- Add `proxy_set` parameter to `apache::balancer` define + +### Bugfixes: +- Fix ordering for multiple `apache::balancer` clusters +- Fix symlinking for sites-available on Debian-based OSs +- Fix dependency ordering for recursive confdir management +- Fix `apache::mod::*` to notify the service on config change +- Documentation updates + +## 2013-07-09 Release 0.7.0 +### Changes: +- Essentially rewrite the module -- too many to list +- `apache::vhost` has many abilities -- see README.md for details +- `apache::mod::*` classes provide httpd mod-loading capabilities +- `apache` base class is much more configurable + +### Bugfixes: +- Many. And many more to come + +## 2013-03-2 Release 0.6.0 +- update travis tests (add more supported versions) +- add access log_parameter +- make purging of vhost dir configurable + +## 2012-08-24 Release 0.4.0 +### Changes: +- `include apache` is now required when using `apache::mod::*` + +### Bugfixes: +- Fix syntax for validate_re +- Fix formatting in vhost template +- Fix spec tests such that they pass + + 2012-05-08 Puppet Labs - 0.0.4 + e62e362 Fix broken tests for ssl, vhost, vhost::* + 42c6363 Changes to match style guide and pass puppet-lint without error + 42bc8ba changed name => path for file resources in order to name namevar by it's name + 72e13de One end too much + 0739641 style guide fixes: 'true' <> true, $operatingsystem needs to be $::operatingsystem, etc. + 273f94d fix tests + a35ede5 (#13860) Make a2enmod/a2dismo commands optional + 98d774e (#13860) Autorequire Package['httpd'] + 05fcec5 (#13073) Add missing puppet spec tests + 541afda (#6899) Remove virtual a2mod definition + 976cb69 (#13072) Move mod python and wsgi package names to params + 323915a (#13060) Add .gitignore to repo + fdf40af (#13060) Remove pkg directory from source tree + fd90015 Add LICENSE file and update the ModuleFile + d3d0d23 Re-enable local php class + d7516c7 Make management of firewalls configurable for vhosts + 60f83ba Explicitly lookup scope of apache_name in templates. + f4d287f (#12581) Add explicit ordering for vdir directory + 88a2ac6 (#11706) puppetlabs-apache depends on puppetlabs-firewall + a776a8b (#11071) Fix to work with latest firewall module + 2b79e8b (#11070) Add support for Scientific Linux + 405b3e9 Fix for a2mod + 57b9048 Commit apache::vhost::redirect Manifest + 8862d01 Commit apache::vhost::proxy Manifest + d5c1fd0 Commit apache::mod::wsgi Manifest + a825ac7 Commit apache::mod::python Manifest + b77062f Commit Templates + 9a51b4a Vhost File Declarations + 6cf7312 Defaults for Parameters + 6a5b11a Ensure installed + f672e46 a2mod fix + 8a56ee9 add pthon support to apache diff --git a/apache/CONTRIBUTING.md b/apache/CONTRIBUTING.md new file mode 100644 index 000000000..e1288478a --- /dev/null +++ b/apache/CONTRIBUTING.md @@ -0,0 +1,234 @@ +Checklist (and a short version for the impatient) +================================================= + + * Commits: + + - Make commits of logical units. + + - Check for unnecessary whitespace with "git diff --check" before + committing. + + - Commit using Unix line endings (check the settings around "crlf" in + git-config(1)). + + - Do not check in commented out code or unneeded files. + + - The first line of the commit message should be a short + description (50 characters is the soft limit, excluding ticket + number(s)), and should skip the full stop. + + - Associate the issue in the message. The first line should include + the issue number in the form "(#XXXX) Rest of message". + + - The body should provide a meaningful commit message, which: + + - uses the imperative, present tense: "change", not "changed" or + "changes". + + - includes motivation for the change, and contrasts its + implementation with the previous behavior. + + - Make sure that you have tests for the bug you are fixing, or + feature you are adding. + + - Make sure the test suites passes after your commit: + `bundle exec rspec spec/acceptance` More information on [testing](#Testing) below + + - When introducing a new feature, make sure it is properly + documented in the README.md + + * Submission: + + * Pre-requisites: + + - Sign the [Contributor License Agreement](https://cla.puppetlabs.com/) + + - Make sure you have a [GitHub account](https://github.com/join) + + - [Create a ticket](http://projects.puppetlabs.com/projects/modules/issues/new), or [watch the ticket](http://projects.puppetlabs.com/projects/modules/issues) you are patching for. + + * Preferred method: + + - Fork the repository on GitHub. + + - Push your changes to a topic branch in your fork of the + repository. (the format ticket/1234-short_description_of_change is + usually preferred for this project). + + - Submit a pull request to the repository in the puppetlabs + organization. + +The long version +================ + + 1. Make separate commits for logically separate changes. + + Please break your commits down into logically consistent units + which include new or changed tests relevant to the rest of the + change. The goal of doing this is to make the diff easier to + read for whoever is reviewing your code. In general, the easier + your diff is to read, the more likely someone will be happy to + review it and get it into the code base. + + If you are going to refactor a piece of code, please do so as a + separate commit from your feature or bug fix changes. + + We also really appreciate changes that include tests to make + sure the bug is not re-introduced, and that the feature is not + accidentally broken. + + Describe the technical detail of the change(s). If your + description starts to get too long, that is a good sign that you + probably need to split up your commit into more finely grained + pieces. + + Commits which plainly describe the things which help + reviewers check the patch and future developers understand the + code are much more likely to be merged in with a minimum of + bike-shedding or requested changes. Ideally, the commit message + would include information, and be in a form suitable for + inclusion in the release notes for the version of Puppet that + includes them. + + Please also check that you are not introducing any trailing + whitespace or other "whitespace errors". You can do this by + running "git diff --check" on your changes before you commit. + + 2. Sign the Contributor License Agreement + + Before we can accept your changes, we do need a signed Puppet + Labs Contributor License Agreement (CLA). + + You can access the CLA via the [Contributor License Agreement link](https://cla.puppetlabs.com/) + + If you have any questions about the CLA, please feel free to + contact Puppet Labs via email at cla-submissions@puppetlabs.com. + + 3. Sending your patches + + To submit your changes via a GitHub pull request, we _highly_ + recommend that you have them on a topic branch, instead of + directly on "master". + It makes things much easier to keep track of, especially if + you decide to work on another thing before your first change + is merged in. + + GitHub has some pretty good + [general documentation](http://help.github.com/) on using + their site. They also have documentation on + [creating pull requests](http://help.github.com/send-pull-requests/). + + In general, after pushing your topic branch up to your + repository on GitHub, you can switch to the branch in the + GitHub UI and click "Pull Request" towards the top of the page + in order to open a pull request. + + + 4. Update the related GitHub issue. + + If there is a GitHub issue associated with the change you + submitted, then you should update the ticket to include the + location of your branch, along with any other commentary you + may wish to make. + +Testing +======= + +Getting Started +--------------- + +Our puppet modules provide [`Gemfile`](./Gemfile)s which can tell a ruby +package manager such as [bundler](http://bundler.io/) what Ruby packages, +or Gems, are required to build, develop, and test this software. + +Please make sure you have [bundler installed](http://bundler.io/#getting-started) +on your system, then use it to install all dependencies needed for this project, +by running + +```shell +% bundle install +Fetching gem metadata from https://rubygems.org/........ +Fetching gem metadata from https://rubygems.org/.. +Using rake (10.1.0) +Using builder (3.2.2) +-- 8><-- many more --><8 -- +Using rspec-system-puppet (2.2.0) +Using serverspec (0.6.3) +Using rspec-system-serverspec (1.0.0) +Using bundler (1.3.5) +Your bundle is complete! +Use `bundle show [gemname]` to see where a bundled gem is installed. +``` + +NOTE some systems may require you to run this command with sudo. + +If you already have those gems installed, make sure they are up-to-date: + +```shell +% bundle update +``` + +With all dependencies in place and up-to-date we can now run the tests: + +```shell +% rake spec +``` + +This will execute all the [rspec tests](http://rspec-puppet.com/) tests +under [spec/defines](./spec/defines), [spec/classes](./spec/classes), +and so on. rspec tests may have the same kind of dependencies as the +module they are testing. While the module defines in its [Modulefile](./Modulefile), +rspec tests define them in [.fixtures.yml](./fixtures.yml). + +Some puppet modules also come with [beaker](https://github.com/puppetlabs/beaker) +tests. These tests spin up a virtual machine under +[VirtualBox](https://www.virtualbox.org/)) with, controlling it with +[Vagrant](http://www.vagrantup.com/) to actually simulate scripted test +scenarios. In order to run these, you will need both of those tools +installed on your system. + +You can run them by issuing the following command + +```shell +% rake spec_clean +% rspec spec/acceptance +``` + +This will now download a pre-fabricated image configured in the [default node-set](./spec/acceptance/nodesets/default.yml), +install puppet, copy this module and install its dependencies per [spec/spec_helper_acceptance.rb](./spec/spec_helper_acceptance.rb) +and then run all the tests under [spec/acceptance](./spec/acceptance). + +Writing Tests +------------- + +XXX getting started writing tests. + +If you have commit access to the repository +=========================================== + +Even if you have commit access to the repository, you will still need to +go through the process above, and have someone else review and merge +in your changes. The rule is that all changes must be reviewed by a +developer on the project (that did not write the code) to ensure that +all changes go through a code review process. + +Having someone other than the author of the topic branch recorded as +performing the merge is the record that they performed the code +review. + + +Additional Resources +==================== + +* [Getting additional help](http://projects.puppetlabs.com/projects/puppet/wiki/Getting_Help) + +* [Writing tests](http://projects.puppetlabs.com/projects/puppet/wiki/Development_Writing_Tests) + +* [Patchwork](https://patchwork.puppetlabs.com) + +* [Contributor License Agreement](https://projects.puppetlabs.com/contributor_licenses/sign) + +* [General GitHub documentation](http://help.github.com/) + +* [GitHub pull request documentation](http://help.github.com/send-pull-requests/) + diff --git a/apache/Gemfile b/apache/Gemfile new file mode 100644 index 000000000..9dfc87712 --- /dev/null +++ b/apache/Gemfile @@ -0,0 +1,25 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'rake', :require => false + gem 'rspec-puppet', :require => false + gem 'puppetlabs_spec_helper', :require => false + gem 'serverspec', :require => false + gem 'puppet-lint', :require => false + gem 'beaker', :require => false + gem 'beaker-rspec', :require => false +end + +if facterversion = ENV['FACTER_GEM_VERSION'] + gem 'facter', facterversion, :require => false +else + gem 'facter', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/apache/LICENSE b/apache/LICENSE new file mode 100644 index 000000000..8961ce8a6 --- /dev/null +++ b/apache/LICENSE @@ -0,0 +1,15 @@ +Copyright (C) 2012 Puppet Labs Inc + +Puppet Labs can be contacted at: info@puppetlabs.com + +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. diff --git a/apache/Modulefile b/apache/Modulefile new file mode 100644 index 000000000..71bb19263 --- /dev/null +++ b/apache/Modulefile @@ -0,0 +1,12 @@ +name 'puppetlabs-apache' +version '0.10.0' +source 'git://github.com/puppetlabs/puppetlabs-apache.git' +author 'puppetlabs' +license 'Apache 2.0' +summary 'Puppet module for Apache' +description 'Module for Apache configuration' +project_page 'https://github.com/puppetlabs/puppetlabs-apache' + +## Add dependencies, if any: +dependency 'puppetlabs/stdlib', '>= 2.4.0' +dependency 'puppetlabs/concat', '>= 1.0.0' diff --git a/apache/README.md b/apache/README.md new file mode 100644 index 000000000..97fb7b963 --- /dev/null +++ b/apache/README.md @@ -0,0 +1,1536 @@ +#apache + +[![Build Status](https://travis-ci.org/puppetlabs/puppetlabs-apache.png?branch=master)](https://travis-ci.org/puppetlabs/puppetlabs-apache) + +####Table of Contents + +1. [Overview - What is the Apache module?](#overview) +2. [Module Description - What does the module do?](#module-description) +3. [Setup - The basics of getting started with Apache](#setup) + * [Beginning with Apache - Installation](#beginning-with-apache) + * [Configure a Virtual Host - Basic options for getting started](#configure-a-virtual-host) +4. [Usage - The classes, defined types, and their parameters available for configuration](#usage) + * [Classes and Defined Types](#classes-and-defined-types) + * [Class: apache](#class-apache) + * [Classes: apache::mod::*](#classes-apachemodname) + * [Defined Type: apache::vhost](#defined-type-apachevhost) + * [Virtual Host Examples - Demonstrations of some configuration options](#virtual-host-examples) +5. [Implementation - An under-the-hood peek at what the module is doing](#implementation) + * [Classes and Defined Types](#classes-and-defined-types) + * [Templates](#templates) +6. [Limitations - OS compatibility, etc.](#limitations) +7. [Development - Guide for contributing to the module](#development) +8. [Release Notes - Notes on the most recent updates to the module](#release-notes) + +##Overview + +The Apache module allows you to set up virtual hosts and manage web services with minimal effort. + +##Module Description + +Apache is a widely-used web server, and this module provides a simplified way of creating configurations to manage your infrastructure. This includes the ability to configure and manage a range of different virtual host setups, as well as a streamlined way to install and configure Apache modules. + +##Setup + +**What Apache affects:** + +* configuration files and directories (created and written to) + * **NOTE**: Configurations that are *not* managed by Puppet will be purged. +* package/service/configuration files for Apache +* Apache modules +* virtual hosts +* listened-to ports +* `/etc/make.conf` on FreeBSD + +###Beginning with Apache + +To install Apache with the default parameters + +```puppet + class { 'apache': } +``` + +The defaults are determined by your operating system (e.g. Debian systems have one set of defaults, RedHat systems have another). These defaults will work well in a testing environment, but are not suggested for production. To establish customized parameters + +```puppet + class { 'apache': + default_mods => false, + default_confd_files => false, + } +``` + +###Configure a virtual host + +Declaring the `apache` class will create a default virtual host by setting up a vhost on port 80, listening on all interfaces and serving `$apache::docroot`. + +```puppet + class { 'apache': } +``` + +To configure a very basic, name-based virtual host + +```puppet + apache::vhost { 'first.example.com': + port => '80', + docroot => '/var/www/first', + } +``` + +*Note:* The default priority is 15. If nothing matches this priority, the alphabetically first name-based vhost will be used. This is also true if you pass a higher priority and no names match anything else. + +A slightly more complicated example, which moves the docroot owner/group + +```puppet + apache::vhost { 'second.example.com': + port => '80', + docroot => '/var/www/second', + docroot_owner => 'third', + docroot_group => 'third', + } +``` + +To set up a virtual host with SSL and default SSL certificates + +```puppet + apache::vhost { 'ssl.example.com': + port => '443', + docroot => '/var/www/ssl', + ssl => true, + } +``` + +To set up a virtual host with SSL and specific SSL certificates + +```puppet + apache::vhost { 'fourth.example.com': + port => '443', + docroot => '/var/www/fourth', + ssl => true, + ssl_cert => '/etc/ssl/fourth.example.com.cert', + ssl_key => '/etc/ssl/fourth.example.com.key', + } +``` + +To set up a virtual host with IP address different than '*' + +```puppet + apache::vhost { 'subdomain.example.com': + ip => '127.0.0.1', + port => '80', + docrout => '/var/www/subdomain', + } +``` + +To set up a virtual host with wildcard alias for subdomain mapped to same named directory +`http://examle.com.loc => /var/www/example.com` + +```puppet + apache::vhost { 'subdomain.loc': + vhost_name => '*', + port => '80', + virtual_docroot' => '/var/www/%-2+', + docroot => '/var/www', + serveraliases => ['*.loc',], + } +``` + +To set up a virtual host with suPHP + +```puppet + apache::vhost { 'suphp.example.com': + port => '80', + docroot => '/home/appuser/myphpapp', + suphp_addhandler => 'x-httpd-php', + suphp_engine => 'on', + suphp_configpath => '/etc/php5/apache2', + directories => { path => '/home/appuser/myphpapp', + 'suphp' => { user => 'myappuser', group => 'myappgroup' }, + } + } +``` + +To set up a virtual host with WSGI + +```puppet + apache::vhost { 'wsgi.example.com': + port => '80', + docroot => '/var/www/pythonapp', + wsgi_daemon_process => 'wsgi', + wsgi_daemon_process_options => + { processes => '2', threads => '15', display-name => '%{GROUP}' }, + wsgi_process_group => 'wsgi', + wsgi_script_aliases => { '/' => '/var/www/demo.wsgi' }, + } +``` + +Starting 2.2.16, httpd supports [FallbackResource](https://httpd.apache.org/docs/2.2/mod/mod_dir.html#fallbackresource) which is a simple replace for common RewriteRules: + +```puppet + apache::vhost { 'wordpress.example.com': + port => '80', + docroot => '/var/www/wordpress', + fallbackresource => '/index.php', + } +``` + +Please note that the `disabled` argument to FallbackResource is only supported since 2.2.24. + +To see a list of all virtual host parameters, [please go here](#defined-type-apachevhost). To see an extensive list of virtual host examples [please look here](#virtual-host-examples). + +##Usage + +###Classes and Defined Types + +This module modifies Apache configuration files and directories and will purge any configuration not managed by Puppet. Configuration of Apache should be managed by Puppet, as non-puppet configuration files can cause unexpected failures. + +It is possible to temporarily disable full Puppet management by setting the `purge_configs` parameter within the base `apache` class to 'false'. This option should only be used as a temporary means of saving and relocating customized configurations. + +####Class: `apache` + +The Apache module's primary class, `apache`, guides the basic setup of Apache on your system. + +You may establish a default vhost in this class, the `vhost` class, or both. You may add additional vhost configurations for specific virtual hosts using a declaration of the `vhost` type. + +**Parameters within `apache`:** + +#####`default_mods` + +Sets up Apache with default settings based on your OS. Defaults to 'true', set to 'false' for customized configuration. + +#####`default_vhost` + +Sets up a default virtual host. Defaults to 'true', set to 'false' to set up [customized virtual hosts](#configure-a-virtual-host). + +#####`default_confd_files` + +Generates default set of include-able apache configuration files under `${apache::confd_dir}` directory. These configuration files correspond to what is usually installed with apache package on given platform. + +#####`default_ssl_vhost` + +Sets up a default SSL virtual host. Defaults to 'false'. + +```puppet + apache::vhost { 'default-ssl': + port => 443, + ssl => true, + docroot => $docroot, + scriptalias => $scriptalias, + serveradmin => $serveradmin, + access_log_file => "ssl_${access_log_file}", + } +``` + +SSL vhosts only respond to HTTPS queries. + +#####`default_ssl_cert` + +The default SSL certification, which is automatically set based on your operating system (`/etc/pki/tls/certs/localhost.crt` for RedHat, `/etc/ssl/certs/ssl-cert-snakeoil.pem` for Debian, `/usr/local/etc/apache22/server.crt` for FreeBSD). This default will work out of the box but must be updated with your specific certificate information before being used in production. + +#####`default_ssl_key` + +The default SSL key, which is automatically set based on your operating system (`/etc/pki/tls/private/localhost.key` for RedHat, `/etc/ssl/private/ssl-cert-snakeoil.key` for Debian, `/usr/local/etc/apache22/server.key` for FreeBSD). This default will work out of the box but must be updated with your specific certificate information before being used in production. + +#####`default_ssl_chain` + +The default SSL chain, which is automatically set to 'undef'. This default will work out of the box but must be updated with your specific certificate information before being used in production. + +#####`default_ssl_ca` + +The default certificate authority, which is automatically set to 'undef'. This default will work out of the box but must be updated with your specific certificate information before being used in production. + +#####`default_ssl_crl_path` + +The default certificate revocation list path, which is automatically set to 'undef'. This default will work out of the box but must be updated with your specific certificate information before being used in production. + +#####`default_ssl_crl` + +The default certificate revocation list to use, which is automatically set to 'undef'. This default will work out of the box but must be updated with your specific certificate information before being used in production. + +#####`service_name` + +Name of apache service to run. Defaults to: `'httpd'` on RedHat, `'apache2'` on Debian, and `'apache22'` on FreeBSD. + +#####`service_enable` + +Determines whether the 'httpd' service is enabled when the machine is booted. Defaults to 'true'. + +#####`service_ensure` + +Determines whether the service should be running. Can be set to 'undef' which is useful when you want to let the service be managed by some other application like pacemaker. Defaults to 'running'. + +#####`purge_configs` + +Removes all other apache configs and vhosts, which is automatically set to true. Setting this to false is a stopgap measure to allow the apache module to coexist with existing or otherwise managed configuration. It is recommended that you move your configuration entirely to resources within this module. + +#####`serveradmin` + +Sets the server administrator. Defaults to 'root@localhost'. + +#####`servername` + +Sets the servername. Defaults to fqdn provided by facter. + +#####`server_root` + +A value to be set as `ServerRoot` in main configuration file (`httpd.conf`). Defaults to `/etc/httpd` on RedHat, `/etc/apache2` on Debian and `/usr/local` on FreeBSD. + +#####`sendfile` + +Makes Apache use the Linux kernel 'sendfile' to serve static files. Defaults to 'On'. + +#####`server_root` + +A value to be set as `ServerRoot` in main configuration file (`httpd.conf`). Defaults to `/etc/httpd` on RedHat and `/etc/apache2` on Debian. + +#####`error_documents` + +Enables custom error documents. Defaults to 'false'. + +#####`httpd_dir` + +Changes the base location of the configuration directories used for the service. This is useful for specially repackaged HTTPD builds but may have unintended consequences when used in combination with the default distribution packages. Default is based on your OS. + +#####`confd_dir` + +Changes the location of the configuration directory your custom configuration files are placed in. Default is based on your OS. + +#####`vhost_dir` + +Changes the location of the configuration directory your virtual host configuration files are placed in. Default is based on your OS. + +#####`mod_dir` + +Changes the location of the configuration directory your Apache modules configuration files are placed in. Default is based on your OS. + +#####`mpm_module` + +Configures which mpm module is loaded and configured for the httpd process by the `apache::mod::event`, `apache::mod::itk`, `apache::mod::peruser`, `apache::mod::prefork` and `apache::mod::worker` classes. Must be set to `false` to explicitly declare `apache::mod::event`, `apache::mod::itk`, `apache::mod::peruser`, `apache::mod::prefork` or `apache::mod::worker` classes with parameters. All possible values are `event`, `itk`, `peruser`, `prefork`, `worker` (valid values depend on agent's OS), or the boolean `false`. Defaults to `prefork` on RedHat and FreeBSD and `worker` on Debian. Note: on FreeBSD switching between different mpm modules is quite difficult (but possible). Before changing `$mpm_module` one has to deinstall all packages that depend on currently installed `apache`. + +#####`conf_template` + +Setting this allows you to override the template used for the main apache configuration file. This is a potentially risky thing to do as this module has been built around the concept of a minimal configuration file with most of the configuration coming in the form of conf.d/ entries. Defaults to 'apache/httpd.conf.erb'. + +#####`keepalive` + +Setting this allows you to enable persistent connections. + +#####`keepalive_timeout` + +Amount of time the server will wait for subsequent requests on a persistent connection. Defaults to '15'. + +#####`logroot` + +Changes the location of the directory Apache log files are placed in. Defaut is based on your OS. + +#####`log_level` + +Changes the verbosity level of the error log. Defaults to 'warn'. Valid values are `emerg`, `alert`, `crit`, `error`, `warn`, `notice`, `info` or `debug`. + +#####`ports_file` + +Changes the name of the file containing Apache ports configuration. Default is `${conf_dir}/ports.conf`. + +#####`server_tokens` + +Controls how much information Apache sends to the browser about itself and the operating system. See Apache documentation for 'ServerTokens'. Defaults to 'OS'. + +#####`server_signature` + +Allows the configuration of a trailing footer line under server-generated documents. See Apache documentation for 'ServerSignature'. Defaults to 'On'. + +#####`trace_enable` + +Controls, how TRACE requests per RFC 2616 are handled. See Apache documentation for 'TraceEnable'. Defaults to 'On'. + +#####`manage_user` + +Setting this to false will avoid the user resource to be created by this module. This is useful when you already have a user created in another puppet module and that you want to used it to run apache. Without this, it would result in a duplicate resource error. + +#####`manage_group` + +Setting this to false will avoid the group resource to be created by this module. This is useful when you already have a group created in another puppet module and that you want to used it for apache. Without this, it would result in a duplicate resource error. + +#####`package_ensure` + +Allow control over the package ensure statement. This is useful if you want to make sure apache is always at the latest version or whether it is only installed. + +####Class: `apache::default_mods` + +Installs default Apache modules based on what OS you are running + +```puppet + class { 'apache::default_mods': } +``` + +####Defined Type: `apache::mod` + +Used to enable arbitrary Apache httpd modules for which there is no specific `apache::mod::[name]` class. The `apache::mod` defined type will also install the required packages to enable the module, if any. + +```puppet + apache::mod { 'rewrite': } + apache::mod { 'ldap': } +``` + +####Classes: `apache::mod::[name]` + +There are many `apache::mod::[name]` classes within this module that can be declared using `include`: + +* `alias` +* `auth_basic` +* `auth_kerb` +* `authnz_ldap`* +* `autoindex` +* `cache` +* `cgi` +* `cgid` +* `dav` +* `dav_fs` +* `dav_svn` +* `deflate` +* `dev` +* `dir`* +* `disk_cache` +* `event` +* `expires` +* `fastcgi` +* `fcgid` +* `headers` +* `include` +* `info` +* `itk` +* `ldap` +* `mime` +* `mime_magic`* +* `negotiation` +* `nss`* +* `passenger`* +* `perl` +* `peruser` +* `php` (requires [`mpm_module`](#mpm_module) set to `prefork`) +* `prefork`* +* `proxy`* +* `proxy_ajp` +* `proxy_balancer` +* `proxy_html` +* `proxy_http` +* `python` +* `reqtimeout` +* `rewrite` +* `rpaf`* +* `setenvif` +* `ssl`* (see [apache::mod::ssl](#class-apachemodssl) below) +* `status`* +* `suphp` +* `userdir`* +* `vhost_alias` +* `worker`* +* `wsgi` (see [apache::mod::wsgi](#class-apachemodwsgi) below) +* `xsendfile` + +Modules noted with a * indicate that the module has settings and, thus, a template that includes parameters. These parameters control the module's configuration. Most of the time, these parameters will not require any configuration or attention. + +The modules mentioned above, and other Apache modules that have templates, will cause template files to be dropped along with the mod install, and the module will not work without the template. Any mod without a template will install package but drop no files. + +####Class: `apache::mod::ssl` + +Installs Apache SSL capabilities and utilizes `ssl.conf.erb` template. These are the defaults: + +```puppet + class { 'apache::mod::ssl': + ssl_compression => false, + ssl_options => [ 'StdEnvVars' ], + } +``` + +To *use* SSL with a virtual host, you must either set the`default_ssl_vhost` parameter in `apache` to 'true' or set the `ssl` parameter in `apache::vhost` to 'true'. + +####Class: `apache::mod::wsgi` + +```puppet + class { 'apache::mod::wsgi': + wsgi_socket_prefix => "\${APACHE_RUN_DIR}WSGI", + wsgi_python_home => '/path/to/virtenv', + wsgi_python_path => '/path/to/virtenv/site-packages', + } +``` +####Defined Type: `apache::vhost` + +The Apache module allows a lot of flexibility in the set up and configuration of virtual hosts. This flexibility is due, in part, to `vhost`'s setup as a defined resource type, which allows it to be evaluated multiple times with different parameters. + +The `vhost` defined type allows you to have specialized configurations for virtual hosts that have requirements outside of the defaults. You can set up a default vhost within the base `apache` class as well as set a customized vhost setup as default. Your customized vhost (priority 10) will be privileged over the base class vhost (15). + +If you have a series of specific configurations and do not want a base `apache` class default vhost, make sure to set the base class default host to 'false'. + +```puppet + class { 'apache': + default_vhost => false, + } +``` + +**Parameters within `apache::vhost`:** + +The default values for each parameter will vary based on operating system and type of virtual host. + +#####`access_log` + +Specifies whether `*_access.log` directives should be configured. Valid values are 'true' and 'false'. Defaults to 'true'. + +#####`access_log_file` + +Points to the `*_access.log` file. Defaults to 'undef'. + +#####`access_log_pipe` + +Specifies a pipe to send access log messages to. Defaults to 'undef'. + +#####`access_log_syslog` + +Sends all access log messages to syslog. Defaults to 'undef'. + +#####`access_log_format` + +Specifies either a LogFormat nickname or custom format string for access log. Defaults to 'undef'. + +#####`access_log_env_var` + +Adds writing control of access log via environment variable of the access. Defaults to 'undef'. + +#####`add_listen` + +Determines whether the vhost creates a listen statement. The default value is 'true'. + +Setting `add_listen` to 'false' stops the vhost from creating a listen statement, and this is important when you combine vhosts that are not passed an `ip` parameter with vhosts that *are* passed the `ip` parameter. + +#####`aliases` + +Passes a list of hashes to the vhost to create `Alias` or `AliasMatch` statements as per the [`mod_alias` documentation](http://httpd.apache.org/docs/current/mod/mod_alias.html). Each hash is expected to be of the form: + +``` +aliases => [ + { aliasmatch => '^/image/(.*)\.jpg$', path => '/files/jpg.images/$1.jpg' } + { alias => '/image', path => '/ftp/pub/image' }, +], +``` + +For `Alias` and `AliasMatch` to work, each will need a corresponding `` or `` block. The `Alias` and `AliasMatch` directives are created in the order specified in the `aliases` paramter. As described in the [`mod_alias` documentation](http://httpd.apache.org/docs/current/mod/mod_alias.html) more specific `Alias` or `AliasMatch` directives should come before the more general ones to avoid shadowing. + +**Note:** If `apache::mod::passenger` is loaded and `PassengerHighPerformance true` is set, then `Alias` may have issues honouring the `PassengerEnabled off` statement. See [this article](http://www.conandalton.net/2010/06/passengerenabled-off-not-working.html) for details. + +#####`block` + +Specifies the list of things Apache will block access to. The default is an empty set, '[]'. Currently, the only option is 'scm', which blocks web access to .svn, .git and .bzr directories. To add to this, please see the [Development](#development) section. + +#####`custom_fragment` + +Pass a string of custom configuration directives to be placed at the end of the vhost configuration. + +#####`default_vhost` + +Sets a given `apache::vhost` as the default to serve requests that do not match any other `apache::vhost` definitions. The default value is 'false'. + +#####`directories` + +Passes a list of hashes to the vhost to create `...` directive blocks as per the [Apache core documentation](http://httpd.apache.org/docs/2.2/mod/core.html#directory). The `path` key is required in these hashes. An optional `provider` defaults to `directory`. Usage will typically look like: + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ + { path => '/path/to/directory', => }, + { path => '/path/to/another/directory', => }, + ], + } +``` + +*Note:* At least one directory should match `docroot` parameter, once you start declaring directories `apache::vhost` assumes that all required `` blocks will be declared. + +*Note:* If not defined a single default `` block will be created that matches the `docroot` parameter. + +`provider` can be set to any of `directory`, `files`, or `location`. If the [pathspec starts with a `~`](https://httpd.apache.org/docs/2.2/mod/core.html#files), httpd will interpret this as the equivalent of `DirectoryMatch`, `FilesMatch`, or `LocationMatch`, respectively. + +```puppet + apache::vhost { 'files.example.net': + docroot => '/var/www/files', + directories => [ + { path => '~ (\.swp|\.bak|~)$', 'provider' => 'files', 'deny' => 'from all' }, + ], + } +``` + +The directives will be embedded within the `Directory` (`Files`, or `Location`) directive block, missing directives should be undefined and not be added, resulting in their default vaules in Apache. Currently this is the list of supported directives: + +######`addhandlers` + +Sets `AddHandler` directives as per the [Apache Core documentation](http://httpd.apache.org/docs/2.2/mod/mod_mime.html#addhandler). Accepts a list of hashes of the form `{ handler => 'handler-name', extensions => ['extension']}`. Note that `extensions` is a list of extenstions being handled by the handler. +An example: + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ { path => '/path/to/directory', + addhandlers => [ { handler => 'cgi-script', extensions => ['.cgi']} ], + } ], + } +``` + +######`allow` + +Sets an `Allow` directive as per the [Apache Core documentation](http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#allow). An example: + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ { path => '/path/to/directory', allow => 'from example.org' } ], + } +``` + +######`allow_override` + +Sets the usage of `.htaccess` files as per the [Apache core documentation](http://httpd.apache.org/docs/2.2/mod/core.html#allowoverride). Should accept in the form of a list or a string. An example: + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ { path => '/path/to/directory', allow_override => ['AuthConfig', 'Indexes'] } ], + } +``` + +######`deny` + +Sets an `Deny` directive as per the [Apache Core documentation](http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#deny). An example: + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ { path => '/path/to/directory', deny => 'from example.org' } ], + } +``` +######`error_documents` + +A list of hashes which can be used to override the [ErrorDocument](https://httpd.apache.org/docs/2.2/mod/core.html#errordocument) settings for this directory. Example: + +```puppet + apache::vhost { 'sample.example.net': + directories => [ { path => '/srv/www' + error_documents => [ + { 'error_code' => '503', 'document' => '/service-unavail' }, + ], + }] + } +``` + +######`headers` + +Adds lines for `Header` directives as per the [Apache Header documentation](http://httpd.apache.org/docs/2.2/mod/mod_headers.html#header). An example: + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => { + path => '/path/to/directory', + headers => 'Set X-Robots-Tag "noindex, noarchive, nosnippet"', + }, + } +``` + +######`options` + +Lists the options for the given `` block + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ { path => '/path/to/directory', options => ['Indexes','FollowSymLinks','MultiViews'] }], + } +``` + +######`index_options` + +Styles the list + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ { path => '/path/to/directory', options => ['Indexes','FollowSymLinks','MultiViews'], index_options => ['IgnoreCase', 'FancyIndexing', 'FoldersFirst', 'NameWidth=*', 'DescriptionWidth=*', 'SuppressHTMLPreamble'] }], + } +``` + +######`index_order_default` +Sets the order of the list + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ { path => '/path/to/directory', order => 'Allow,Deny', index_order_default => ['Descending', 'Date']}, ], + } +``` + +######`order` +Sets the order of processing `Allow` and `Deny` statements as per [Apache core documentation](http://httpd.apache.org/docs/2.2/mod/mod_authz_host.html#order). An example: + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ { path => '/path/to/directory', order => 'Allow,Deny' } ], + } +``` + +######`auth_type` + +Sets the value for `AuthType` as per the [Apache AuthType +documentation](https://httpd.apache.org/docs/2.2/mod/core.html#authtype). + +######`auth_name` + +Sets the value for `AuthName` as per the [Apache AuthName +documentation](https://httpd.apache.org/docs/2.2/mod/core.html#authname). + +######`auth_digest_algorithm` + +Sets the value for `AuthDigestAlgorithm` as per the [Apache +AuthDigestAlgorithm +documentation](https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#authdigestalgorithm) + +######`auth_digest_domain` + +Sets the value for `AuthDigestDomain` as per the [Apache AuthDigestDomain +documentation](https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#authdigestdomain). + +######`auth_digest_nonce_lifetime` + +Sets the value for `AuthDigestNonceLifetime` as per the [Apache +AuthDigestNonceLifetime +documentation](https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#authdigestnoncelifetime) + +######`auth_digest_provider` + +Sets the value for `AuthDigestProvider` as per the [Apache AuthDigestProvider +documentation](https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#authdigestprovider). + +######`auth_digest_qop` + +Sets the value for `AuthDigestQop` as per the [Apache AuthDigestQop +documentation](https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#authdigestqop). + +######`auth_digest_shmem_size` + +Sets the value for `AuthAuthDigestShmemSize` as per the [Apache AuthDigestShmemSize +documentation](https://httpd.apache.org/docs/2.2/mod/mod_auth_digest.html#authdigestshmemsize). + +######`auth_basic_authoritative` + +Sets the value for `AuthBasicAuthoritative` as per the [Apache +AuthBasicAuthoritative +documentation](https://httpd.apache.org/docs/2.2/mod/mod_auth_basic.html#authbasicauthoritative). + +######`auth_basic_fake` + +Sets the value for `AuthBasicFake` as per the [Apache AuthBasicFake +documentation](https://httpd.apache.org/docs/trunk/mod/mod_auth_basic.html#authbasicfake). + +######`auth_basic_provider` + +Sets the value for `AuthBasicProvider` as per the [Apache AuthBasicProvider +documentation](https://httpd.apache.org/docs/2.2/mod/mod_auth_basic.html#authbasicprovider). + +######`auth_user_file` + +Sets the value for `AuthUserFile` as per the [Apache AuthUserFile +documentation](https://httpd.apache.org/docs/2.2/mod/mod_authn_file.html#authuserfile). + +######`auth_group_file` + +Sets the value for `AuthGroupFile` as per the [Apache AuthGroupFile +documentation](https://httpd.apache.org/docs/2.2/mod/mod_authz_groupfile.html#authgroupfile). + +######`auth_require` + +Sets the value for `AuthName` as per the [Apache Require +documentation](https://httpd.apache.org/docs/2.2/mod/core.html#require) + + +######`passenger_enabled` + +Sets the value for the `PassengerEnabled` directory to `on` or `off` as per the [Passenger documentation](http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerEnabled). + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + directories => [ { path => '/path/to/directory', passenger_enabled => 'off' } ], + } +``` + +**Note:** This directive requires `apache::mod::passenger` to be active, Apache may not start with an unrecognised directive without it. + +**Note:** Be aware that there is an [issue](http://www.conandalton.net/2010/06/passengerenabled-off-not-working.html) using the `PassengerEnabled` directive with the `PassengerHighPerformance` directive. + +######`ssl_options` + +String or list of [`SSLOptions`](https://httpd.apache.org/docs/2.2/mod/mod_ssl.html#ssloptions) for the given `` block. This overrides, or refines the [`SSLOptions`](https://httpd.apache.org/docs/2.2/mod/mod_ssl.html#ssloptions) of the parent block (either vhost, or server). + +```puppet + apache::vhost { 'secure.example.net': + docroot => '/path/to/directory', + directories => [ + { path => '/path/to/directory', ssl_options => '+ExportCertData' } + { path => '/path/to/different/dir', ssl_options => [ '-StdEnvVars', '+ExportCertData'] }, + ], + } +``` + +######`suphp` + +An array containing two values: User and group for the [suPHP_UserGroup](http://www.suphp.org/DocumentationView.html?file=apache/CONFIG) setting. +This directive must be used with `suphp_engine => on` in the vhost declaration. This directive only works in `` or ``. + +```puppet + apache::vhost { 'secure.example.net': + docroot => '/path/to/directory', + directories => [ + { path => '/path/to/directory', suphp => { user => 'myappuser', group => 'myappgroup' } + ], + } +``` + +######`php_admin_value` and `php_admin_flag` + +Allows per-vhost (and per-directory) setting [`php_admin_value`s or `php_admin_flag`s](http://php.net/manual/en/configuration.changes.php). These flags or values cannot be overwritten by a user, or an application. + +######`custom_fragment` + +Pass a string of custom configuration directives to be placed at the end of the +directory configuration. + +#####`directoryindex` + +Set a DirectoryIndex directive, to set the list of resources to look for, when the client requests an index of the directory by specifying a / at the end of the directory name.. + +#####`docroot` + +Provides the DocumentRoot directive, identifying the directory Apache serves files from. + +#####`docroot_group` + +Sets group access to the docroot directory. Defaults to 'root'. + +#####`docroot_owner` + +Sets individual user access to the docroot directory. Defaults to 'root'. + +#####`error_log` + +Specifies whether `*_error.log` directives should be configured. Defaults to 'true'. + +#####`error_log_file` + +Points to the `*_error.log` file. Defaults to 'undef'. + +#####`error_log_pipe` + +Specifies a pipe to send error log messages to. Defaults to 'undef'. + +#####`error_log_syslog` + +Sends all error log messages to syslog. Defaults to 'undef'. + +#####`error_documents` + +A list of hashes which can be used to override the [ErrorDocument](https://httpd.apache.org/docs/2.2/mod/core.html#errordocument) settings for this vhost. Defaults to `[]`. Example: + +```puppet + apache::vhost { 'sample.example.net': + error_documents => [ + { 'error_code' => '503', 'document' => '/service-unavail' }, + { 'error_code' => '407', 'document' => 'https://example.com/proxy/login' }, + ], + } +``` + +#####`ensure` + +Specifies if the vhost file is present or absent. + +#####`fastcgi_server` + +Specifies the filename as an external FastCGI application. Defaults to 'undef'. + +#####`fastcgi_socket` + +Filename used to communicate with the web server. Defaults to 'undef'. + +#####`fastcgi_dir` + +Directory to enable for FastCGI. Defaults to 'undef'. + +#####`additional_includes` + +Specifies paths to additional static vhost-specific Apache configuration files. +This option is useful when you need to implement a unique and/or custom +configuration not supported by this module. + +#####`headers` + +Specifies additional response headers as per [the `mod_headers` documentation](http://httpd.apache.org/docs/2.2/mod/mod_headers.html#header). + +```puppet + apache::vhost { 'site.name.fdqn': + … + headers => [ + 'add Strict-Transport-Security "max-age=15768000"', + 'merge Cache-Control no-cache env=CGI', + ], + } +``` + +#####`ip` + +The IP address the vhost listens on. Defaults to 'undef'. + +#####`ip_based` + +Enables an IP-based vhost. This parameter inhibits the creation of a NameVirtualHost directive, since those are used to funnel requests to name-based vhosts. Defaults to 'false'. + +#####`logroot` + +Specifies the location of the virtual host's logfiles. Defaults to `/var/log//`. + +#####`log_level` + +Specifies the verbosity level of the error log. Defaults to `warn` for the global server configuration and can be overridden on a per-vhost basis using this parameter. Valid value for `log_level` is one of `emerg`, `alert`, `crit`, `error`, `warn`, `notice`, `info` or `debug`. + +#####`no_proxy_uris` + +Specifies URLs you do not want to proxy. This parameter is meant to be used in combination with `proxy_dest`. + +#####`options` + +Lists the options for the given virtual host + +```puppet + apache::vhost { 'site.name.fdqn': + … + options => ['Indexes','FollowSymLinks','MultiViews'], + } +``` + +#####`override` + +Sets the overrides for the given virtual host. Accepts an array of AllowOverride arguments. + +#####`port` + +Sets the port the host is configured on. + +#####`priority` + +Sets the relative load-order for Apache httpd VirtualHost configuration files. Defaults to '25'. + +If nothing matches the priority, the first name-based vhost will be used. Likewise, passing a higher priority will cause the alphabetically first name-based vhost to be used if no other names match. + +*Note*: You should not need to use this parameter. However, if you do use it, be aware that the `default_vhost` parameter for `apache::vhost` passes a priority of '15'. + +#####`proxy_dest` + +Specifies the destination address of a proxypass configuration. Defaults to 'undef'. + +#####`proxy_pass` + +Specifies an array of path => uri for a proxypass configuration. Defaults to 'undef'. + +Example: + +```puppet +$proxy_pass = [ + { 'path' => '/a', 'url' => 'http://backend-a/' }, + { 'path' => '/b', 'url' => 'http://backend-b/' }, + { 'path' => '/c', 'url' => 'http://backend-a/c' } +] + +apache::vhost { 'site.name.fdqn': + … + proxy_pass => $proxy_pass, +} +``` + +#####`rack_base_uris` + +Specifies the resource identifiers for a rack configuration. The file paths specified will be listed as rack application roots for passenger/rack in the `_rack.erb` template. Defaults to 'undef'. + +#####`redirect_dest` + +Specifies the address to redirect to. Defaults to 'undef'. + +#####`redirect_source` + +Specifies the source items? that will redirect to the destination specified in `redirect_dest`. If more than one item for redirect is supplied, the source and destination must be the same length, and the items are order-dependent. + +```puppet + apache::vhost { 'site.name.fdqn': + … + redirect_source => ['/images','/downloads'], + redirect_dest => ['http://img.example.com/','http://downloads.example.com/'], + } +``` + +#####`redirect_status` + +Specifies the status to append to the redirect. Defaults to 'undef'. + +```puppet + apache::vhost { 'site.name.fdqn': + … + redirect_status => ['temp','permanent'], + } +``` + +#####`request_headers` + +Specifies additional request headers. + +```puppet + apache::vhost { 'site.name.fdqn': + … + request_headers => [ + 'append MirrorID "mirror 12"', + 'unset MirrorID', + ], + } +``` + +#####`rewrite_base` + +Limits the `rewrites` to the specified base URL. Defaults to 'undef'. + +```puppet + apache::vhost { 'site.name.fdqn': + … + rewrite_base => '/blog/', + rewrites => [ + { rewrite_rule => ['^index\.html$ welcome.html'] } + ] + } +``` + +The above example would limit the index.html -> welcome.html rewrite to only something inside of http://example.com/blog/. + +#####`rewrites` + +Creates URL rewrite rules. Defaults to 'undef'. This parameter allows you to specify, for example, that anyone trying to access index.html will be served welcome.html. + +```puppet + apache::vhost { 'site.name.fdqn': + … + rewrites => [ { rewrite_rule => ['^index\.html$ welcome.html'] } ] + } +``` + +Allows rewrite conditions, that when true, will execute the associated rule. For example + +```puppet + apache::vhost { 'site.name.fdqn': + … + rewrites => [ + { + comment => 'redirect IE', + rewrite_cond => ['%{HTTP_USER_AGENT} ^MSIE'], + rewrite_rule => ['^index\.html$ welcome.html'], + } + ] + } +``` + +will rewrite URLs only if the visitor is using IE. + +Multiple conditions can be applied, the following will rewrite index.html to welcome.html only when the browser is lynx or mozilla version 1 or 2 + +```puppet + apache::vhost { 'site.name.fdqn': + … + rewrites => [ + { + comment => 'Lynx or Mozilla v1/2', + rewrite_cond => ['%{HTTP_USER_AGENT} ^Lynx/ [OR]', '%{HTTP_USER_AGENT} ^Mozilla/[12]'], + rewrite_rule => ['^index\.html$ welcome.html'], + } + ] + } +``` + +Multiple rewrites and conditions are also possible + +```puppet + apache::vhost { 'site.name.fdqn': + … + rewrites => [ + { + comment => 'Lynx or Mozilla v1/2', + rewrite_cond => ['%{HTTP_USER_AGENT} ^Lynx/ [OR]', '%{HTTP_USER_AGENT} ^Mozilla/[12]'], + rewrite_rule => ['^index\.html$ welcome.html'], + }, + { + comment => 'Internet Explorer', + rewrite_cond => ['%{HTTP_USER_AGENT} ^MSIE'], + rewrite_rule => ['^index\.html$ /index.IE.html [L]'], + }, + } + rewrite_rule => ['^index\.cgi$ index.php', '^index\.html$ index.php', '^index\.asp$ index.html'], + } + ] + } +``` + +refer to the [`mod_rewrite` documentation](http://httpd.apache.org/docs/current/mod/mod_rewrite.html) for more details on what is possible with rewrite rules and conditions + +#####`scriptalias` + +Defines a directory of CGI scripts to be aliased to the path '/cgi-bin' + +#####`scriptaliases` + +Passes a list of hashes to the vhost to create `ScriptAlias` or `ScriptAliasMatch` statements as per the [`mod_alias` documentation](http://httpd.apache.org/docs/current/mod/mod_alias.html). Each hash is expected to be of the form: + +```puppet + scriptaliases => [ + { + alias => '/myscript', + path => '/usr/share/myscript', + }, + { + aliasmatch => '^/foo(.*)', + path => '/usr/share/fooscripts$1', + }, + { + aliasmatch => '^/bar/(.*)', + path => '/usr/share/bar/wrapper.sh/$1', + }, + { + alias => '/neatscript', + path => '/usr/share/neatscript', + }, + ] +``` + +These directives are created in the order specified. As with `Alias` and `AliasMatch` directives the more specific aliases should come before the more general ones to avoid shadowing. + +#####`serveradmin` + +Specifies the email address Apache will display when it renders one of its error pages. + +#####`serveraliases` + +Sets the server aliases of the site. + +#####`servername` + +Sets the primary name of the virtual host. + +#####`setenv` + +Used by HTTPD to set environment variables for vhosts. Defaults to '[]'. + +#####`setenvif` + +Used by HTTPD to conditionally set environment variables for vhosts. Defaults to '[]'. + +#####`ssl` + +Enables SSL for the virtual host. SSL vhosts only respond to HTTPS queries. Valid values are 'true' or 'false'. + +#####`ssl_ca` + +Specifies the certificate authority. + +#####`ssl_cert` + +Specifies the SSL certification. + +#####`ssl_protocol` + +Specifies the SSL Protocol (SSLProtocol). + +#####`ssl_cipher` + +Specifies the SSLCipherSuite. + +#####`ssl_honorcipherorder` + +Sets SSLHonorCipherOrder directive, used to prefer the server's cipher preference order + +#####`ssl_certs_dir` + +Specifies the location of the SSL certification directory. Defaults to `/etc/ssl/certs` on Debian and `/etc/pki/tls/certs` on RedHat. + +#####`ssl_chain` + +Specifies the SSL chain. + +#####`ssl_crl` + +Specifies the certificate revocation list to use. + +#####`ssl_crl_path` + +Specifies the location of the certificate revocation list. + +#####`ssl_key` + +Specifies the SSL key. + +#####`ssl_verify_client` + +Sets `SSLVerifyClient` directives as per the [Apache Core documentation](http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslverifyclient). Defaults to undef. +An example: + +```puppet + apache::vhost { 'sample.example.net': + … + ssl_verify_client => 'optional', + } +``` + +#####`ssl_verify_depth` + +Sets `SSLVerifyDepth` directives as per the [Apache Core documentation](http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#sslverifydepth). Defaults to undef. +An example: + +```puppet + apache::vhost { 'sample.example.net': + … + ssl_verify_depth => 1, + } +``` + +#####`ssl_options` + +Sets `SSLOptions` directives as per the [Apache Core documentation](http://httpd.apache.org/docs/2.2/mod/mod_ssl.html#ssloptions). This is the global setting for the vhost and can be a string or an array. Defaults to undef. A single string example: + +```puppet + apache::vhost { 'sample.example.net': + … + ssl_options => '+ExportCertData', + } +``` + +An array of strings example: + +```puppet + apache::vhost { 'sample.example.net': + … + ssl_options => [ '+StrictRequire', '+ExportCertData' ], + } +``` + +#####`ssl_proxyengine` + +Specifies whether to use `SSLProxyEngine` or not. Defaults to `false`. + +#####`vhost_name` + +This parameter is for use with name-based virtual hosting. Defaults to '*'. + +#####`itk` + +Hash containing infos to configure itk as per the [ITK documentation](http://mpm-itk.sesse.net/). + +Keys could be: +* user + group +* assignuseridexpr +* assigngroupidexpr +* maxclientvhost +* nice +* limituidrange (Linux 3.5.0 or newer) +* limitgidrange (Linux 3.5.0 or newer) + +Usage will typically look like: + +```puppet + apache::vhost { 'sample.example.net': + docroot => '/path/to/directory', + itk => { + user => 'someuser', + group => 'somegroup', + }, + } +``` + +###Virtual Host Examples + +The Apache module allows you to set up pretty much any configuration of virtual host you might desire. This section will address some common configurations. Please see the [Tests section](https://github.com/puppetlabs/puppetlabs-apache/tree/master/tests) for even more examples. + +Configure a vhost with a server administrator + +```puppet + apache::vhost { 'third.example.com': + port => '80', + docroot => '/var/www/third', + serveradmin => 'admin@example.com', + } +``` + +- - - + +Set up a vhost with aliased servers + +```puppet + apache::vhost { 'sixth.example.com': + serveraliases => [ + 'sixth.example.org', + 'sixth.example.net', + ], + port => '80', + docroot => '/var/www/fifth', + } +``` + +- - - + +Configure a vhost with a cgi-bin + +```puppet + apache::vhost { 'eleventh.example.com': + port => '80', + docroot => '/var/www/eleventh', + scriptalias => '/usr/lib/cgi-bin', + } +``` + +- - - + +Set up a vhost with a rack configuration + +```puppet + apache::vhost { 'fifteenth.example.com': + port => '80', + docroot => '/var/www/fifteenth', + rack_base_uris => ['/rackapp1', '/rackapp2'], + } +``` + +- - - + +Set up a mix of SSL and non-SSL vhosts at the same domain + +```puppet + #The non-ssl vhost + apache::vhost { 'first.example.com non-ssl': + servername => 'first.example.com', + port => '80', + docroot => '/var/www/first', + } + + #The SSL vhost at the same domain + apache::vhost { 'first.example.com ssl': + servername => 'first.example.com', + port => '443', + docroot => '/var/www/first', + ssl => true, + } +``` + +- - - + +Configure a vhost to redirect non-SSL connections to SSL + +```puppet + apache::vhost { 'sixteenth.example.com non-ssl': + servername => 'sixteenth.example.com', + port => '80', + docroot => '/var/www/sixteenth', + redirect_status => 'permanent' + redirect_dest => 'https://sixteenth.example.com/' + } + apache::vhost { 'sixteenth.example.com ssl': + servername => 'sixteenth.example.com', + port => '443', + docroot => '/var/www/sixteenth', + ssl => true, + } +``` + +- - - + +Set up IP-based vhosts on any listen port and have them respond to requests on specific IP addresses. In this example, we will set listening on ports 80 and 81. This is required because the example vhosts are not declared with a port parameter. + +```puppet + apache::listen { '80': } + apache::listen { '81': } +``` + +Then we will set up the IP-based vhosts + +```puppet + apache::vhost { 'first.example.com': + ip => '10.0.0.10', + docroot => '/var/www/first', + ip_based => true, + } + apache::vhost { 'second.example.com': + ip => '10.0.0.11', + docroot => '/var/www/second', + ip_based => true, + } +``` + +- - - + +Configure a mix of name-based and IP-based vhosts. First, we will add two IP-based vhosts on 10.0.0.10, one SSL and one non-SSL + +```puppet + apache::vhost { 'The first IP-based vhost, non-ssl': + servername => 'first.example.com', + ip => '10.0.0.10', + port => '80', + ip_based => true, + docroot => '/var/www/first', + } + apache::vhost { 'The first IP-based vhost, ssl': + servername => 'first.example.com', + ip => '10.0.0.10', + port => '443', + ip_based => true, + docroot => '/var/www/first-ssl', + ssl => true, + } +``` + +Then, we will add two name-based vhosts listening on 10.0.0.20 + +```puppet + apache::vhost { 'second.example.com': + ip => '10.0.0.20', + port => '80', + docroot => '/var/www/second', + } + apache::vhost { 'third.example.com': + ip => '10.0.0.20', + port => '80', + docroot => '/var/www/third', + } +``` + +If you want to add two name-based vhosts so that they will answer on either 10.0.0.10 or 10.0.0.20, you **MUST** declare `add_listen => 'false'` to disable the otherwise automatic 'Listen 80', as it will conflict with the preceding IP-based vhosts. + +```puppet + apache::vhost { 'fourth.example.com': + port => '80', + docroot => '/var/www/fourth', + add_listen => false, + } + apache::vhost { 'fifth.example.com': + port => '80', + docroot => '/var/www/fifth', + add_listen => false, + } +``` + +##Implementation + +###Classes and Defined Types + +####Class: `apache::dev` + +Installs Apache development libraries + +```puppet + class { 'apache::dev': } +``` + +On FreeBSD you're required to define `apache::package` or `apache` class before `apache::dev`. + +####Defined Type: `apache::listen` + +Controls which ports Apache binds to for listening based on the title: + +```puppet + apache::listen { '80': } + apache::listen { '443': } +``` + +Declaring this defined type will add all `Listen` directives to the `ports.conf` file in the Apache httpd configuration directory. `apache::listen` titles should always take the form of: ``, `:`, or `[]:` + +Apache httpd requires that `Listen` directives must be added for every port. The `apache::vhost` defined type will automatically add `Listen` directives unless the `apache::vhost` is passed `add_listen => false`. + +####Defined Type: `apache::namevirtualhost` + +Enables named-based hosting of a virtual host + +```puppet + apache::namevirtualhost { '*:80': } +``` + +Declaring this defined type will add all `NameVirtualHost` directives to the `ports.conf` file in the Apache https configuration directory. `apache::namevirtualhost` titles should always take the form of: `*`, `*:`, `_default_:`, ``, or `:`. + +####Defined Type: `apache::balancermember` + +Define members of a proxy_balancer set (mod_proxy_balancer). Very useful when using exported resources. + +On every app server you can export a balancermember like this: + +```puppet + @@apache::balancermember { "${::fqdn}-puppet00": + balancer_cluster => 'puppet00', + url => "ajp://${::fqdn}:8009" + options => ['ping=5', 'disablereuse=on', 'retry=5', 'ttl=120'], + } +``` + +And on the proxy itself you create the balancer cluster using the defined type apache::balancer: + +```puppet + apache::balancer { 'puppet00': } +``` + +If you need to use ProxySet in the balncer config you can do as so: + +```puppet + apache::balancer { 'puppet01': + proxy_set => {'stickysession' => 'JSESSIONID'}, + } +``` + +###Templates + +The Apache module relies heavily on templates to enable the `vhost` and `apache::mod` defined types. These templates are built based on Facter facts around your operating system. Unless explicitly called out, most templates are not meant for configuration. + +##Limitations + +This has been tested on Ubuntu Precise, Debian Wheezy, CentOS 5.8, and FreeBSD 9.1. + +##Development + +### Overview + +Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve. + +We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. + +You can read the complete module contribution guide [on the Puppet Labs wiki.](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing) + +### Running tests + +This project contains tests for both [rspec-puppet](http://rspec-puppet.com/) and [rspec-system](https://github.com/puppetlabs/rspec-system) to verify functionality. For in-depth information please see their respective documentation. + +Quickstart: + + gem install bundler + bundle install + bundle exec rake spec + bundle exec rspec spec/acceptance + +##Copyright and License + +Copyright (C) 2012 [Puppet Labs](https://www.puppetlabs.com/) Inc + +Puppet Labs can be contacted at: info@puppetlabs.com + +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. diff --git a/apache/README.passenger.md b/apache/README.passenger.md new file mode 100644 index 000000000..4b36149dc --- /dev/null +++ b/apache/README.passenger.md @@ -0,0 +1,93 @@ +# Passenger + +Just enabling the Passenger module is insufficient for the use of Passenger in production. Passenger should be tunable to better fit the environment in which it is run while being aware of the resources it required. + +To this end the Apache passenger module has been modified to apply system wide Passenger tuning declarations to `passenger.conf`. Declarations specific to a virtual host should be passed through when defining a `vhost` (e.g. `rack_base_uris' parameter on the `apache::vhost` class, check `README.md`). + +# Parameters for `apache::mod::passenger` + +The following declarations are supported and can be passed to `apache::mod::passenger` as parameters, for example: + +``` +class {'apache::mod::passenger': + passenger_high_performance => 'on', + rails_autodetect => 'off', +} +``` + +The general form is using the all lower case version of the declaration. + +If you pass a default value to `apache::mod::passenger` it will be ignored and not passed through to the configuration file. + +## passenger_high_performance + +Default is `off`, when turned `on` Passenger runs in a higher performance mode that can be less compatible with other Apache modules. + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerHighPerformance + +## passenger_max_pool_size + +Set's the maximum number of Passenger application processes that may simultaneously run. The default value is 6. + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengermaxpoolsize_lt_integer_gt + +## passenger_pool_idle_time + +The maximum number of seconds a Passenger Application process will be allowed to remain idle before being shut down. The default value is 300. + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerPoolIdleTime + +## passenger_max_requests + +The maximum number of request a Passenger application will process before being restarted. The default value is 0, which indicates that a process will only shut down if the Pool Idle Time (see above) expires. + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#PassengerMaxRequests + +## passenger_stat_throttle_rate + +Sets how often Passenger performs file system checks, at most once every _x_ seconds. Default is 0, which means the checks are performed with every request. + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#_passengerstatthrottlerate_lt_integer_gt + +## rack_auto_detect + +Should Passenger automatically detect if the document root of a virtual host is a Rack application. The default is `on` + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#_rackautodetect_lt_on_off_gt + +## rails_auto_detect + +Should Passenger automatically detect if the document root of a virtual host is a Rails application. The default is on. + +http://www.modrails.com/documentation/Users%20guide%20Apache.html#_railsautodetect_lt_on_off_gt + +## passenger_use_global_queue + +Allows toggling of PassengerUseGlobalQueue. NOTE: PassengerUseGlobalQueue is the default in Passenger 4.x and the versions >= 4.x have disabled this configuration option altogether. Use with caution. + +# Attribution + +The Passenger tuning parameters for the `apache::mod::puppet` Puppet class was modified by Aaron Hicks (hicksa@landcareresearch.co.nz) for work on the NeSI Project and the Tuakiri New Zealand Access Federation as a fork from the PuppetLabs Apache module on GitHub. + +* https://github.com/puppetlabs/puppetlabs-apache +* https://github.com/nesi/puppetlabs-apache +* http://www.nesi.org.nz// +* https://tuakiri.ac.nz/confluence/display/Tuakiri/Home + +# Copyright and License + +Copyright (C) 2012 [Puppet Labs](https://www.puppetlabs.com/) Inc + +Puppet Labs can be contacted at: info@puppetlabs.com + +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. diff --git a/apache/Rakefile b/apache/Rakefile new file mode 100644 index 000000000..902062317 --- /dev/null +++ b/apache/Rakefile @@ -0,0 +1,9 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' + +PuppetLint.configuration.fail_on_warnings +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.send('disable_class_inherits_from_params_class') +PuppetLint.configuration.send('disable_class_parameter_defaults') +PuppetLint.configuration.send('disable_documentation') +PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"] diff --git a/apache/files/httpd b/apache/files/httpd new file mode 100644 index 000000000..d65a8d445 --- /dev/null +++ b/apache/files/httpd @@ -0,0 +1,24 @@ +# Configuration file for the httpd service. + +# +# The default processing model (MPM) is the process-based +# 'prefork' model. A thread-based model, 'worker', is also +# available, but does not work with some modules (such as PHP). +# The service must be stopped before changing this variable. +# +#HTTPD=/usr/sbin/httpd.worker + +# +# To pass additional options (for instance, -D definitions) to the +# httpd binary at startup, set OPTIONS here. +# +#OPTIONS= +#OPTIONS=-DDOWN + +# +# By default, the httpd process is started in the C locale; to +# change the locale in which the server runs, the HTTPD_LANG +# variable can be set. +# +#HTTPD_LANG=C +export SHORTHOST=`hostname -s` diff --git a/apache/lib/puppet/provider/a2mod.rb b/apache/lib/puppet/provider/a2mod.rb new file mode 100644 index 000000000..670aca3d0 --- /dev/null +++ b/apache/lib/puppet/provider/a2mod.rb @@ -0,0 +1,34 @@ +class Puppet::Provider::A2mod < Puppet::Provider + def self.prefetch(mods) + instances.each do |prov| + if mod = mods[prov.name] + mod.provider = prov + end + end + end + + def flush + @property_hash.clear + end + + def properties + if @property_hash.empty? + @property_hash = query || {:ensure => :absent} + @property_hash[:ensure] = :absent if @property_hash.empty? + end + @property_hash.dup + end + + def query + self.class.instances.each do |mod| + if mod.name == self.name or mod.name.downcase == self.name + return mod.properties + end + end + nil + end + + def exists? + properties[:ensure] != :absent + end +end diff --git a/apache/lib/puppet/provider/a2mod/a2mod.rb b/apache/lib/puppet/provider/a2mod/a2mod.rb new file mode 100644 index 000000000..e257a579e --- /dev/null +++ b/apache/lib/puppet/provider/a2mod/a2mod.rb @@ -0,0 +1,35 @@ +require 'puppet/provider/a2mod' + +Puppet::Type.type(:a2mod).provide(:a2mod, :parent => Puppet::Provider::A2mod) do + desc "Manage Apache 2 modules on Debian and Ubuntu" + + optional_commands :encmd => "a2enmod" + optional_commands :discmd => "a2dismod" + commands :apache2ctl => "apache2ctl" + + confine :osfamily => :debian + defaultfor :operatingsystem => [:debian, :ubuntu] + + def self.instances + modules = apache2ctl("-M").lines.collect { |line| + m = line.match(/(\w+)_module \(shared\)$/) + m[1] if m + }.compact + + modules.map do |mod| + new( + :name => mod, + :ensure => :present, + :provider => :a2mod + ) + end + end + + def create + encmd resource[:name] + end + + def destroy + discmd resource[:name] + end +end diff --git a/apache/lib/puppet/provider/a2mod/gentoo.rb b/apache/lib/puppet/provider/a2mod/gentoo.rb new file mode 100644 index 000000000..07319dfdc --- /dev/null +++ b/apache/lib/puppet/provider/a2mod/gentoo.rb @@ -0,0 +1,116 @@ +require 'puppet/util/filetype' +Puppet::Type.type(:a2mod).provide(:gentoo, :parent => Puppet::Provider) do + desc "Manage Apache 2 modules on Gentoo" + + confine :operatingsystem => :gentoo + defaultfor :operatingsystem => :gentoo + + attr_accessor :property_hash + + def create + @property_hash[:ensure] = :present + end + + def exists? + (!(@property_hash[:ensure].nil?) and @property_hash[:ensure] == :present) + end + + def destroy + @property_hash[:ensure] = :absent + end + + def flush + self.class.flush + end + + class << self + attr_reader :conf_file + end + + def self.clear + @mod_resources = [] + @modules = [] + @other_args = "" + end + + def self.initvars + @conf_file = "/etc/conf.d/apache2" + @filetype = Puppet::Util::FileType.filetype(:flat).new(conf_file) + @mod_resources = [] + @modules = [] + @other_args = "" + end + + self.initvars + + # Retrieve an array of all existing modules + def self.modules + if @modules.length <= 0 + # Locate the APACHE_OPTS variable + records = filetype.read.split(/\n/) + apache2_opts = records.grep(/^\s*APACHE2_OPTS=/).first + + # Extract all defines + while apache2_opts.sub!(/-D\s+(\w+)/, '') + @modules << $1.downcase + end + + # Hang on to any remaining options. + if apache2_opts.match(/APACHE2_OPTS="(.+)"/) + @other_args = $1.strip + end + + @modules.sort!.uniq! + end + + @modules + end + + def self.prefetch(resources={}) + # Match resources with existing providers + instances.each do |provider| + if resource = resources[provider.name] + resource.provider = provider + end + end + + # Store all resources using this provider for flushing + resources.each do |name, resource| + @mod_resources << resource + end + end + + def self.instances + modules.map {|mod| new(:name => mod, :provider => :gentoo, :ensure => :present)} + end + + def self.flush + + mod_list = modules + mods_to_remove = @mod_resources.select {|mod| mod.should(:ensure) == :absent}.map {|mod| mod[:name]} + mods_to_add = @mod_resources.select {|mod| mod.should(:ensure) == :present}.map {|mod| mod[:name]} + + mod_list -= mods_to_remove + mod_list += mods_to_add + mod_list.sort!.uniq! + + if modules != mod_list + opts = @other_args + " " + opts << mod_list.map {|mod| "-D #{mod.upcase}"}.join(" ") + opts.strip! + opts.gsub!(/\s+/, ' ') + + apache2_opts = %Q{APACHE2_OPTS="#{opts}"} + Puppet.debug("Writing back \"#{apache2_opts}\" to #{conf_file}") + + records = filetype.read.split(/\n/) + + opts_index = records.find_index {|i| i.match(/^\s*APACHE2_OPTS/)} + records[opts_index] = apache2_opts + + filetype.backup + filetype.write(records.join("\n")) + @modules = mod_list + end + end +end diff --git a/apache/lib/puppet/provider/a2mod/modfix.rb b/apache/lib/puppet/provider/a2mod/modfix.rb new file mode 100644 index 000000000..8f35b2e4a --- /dev/null +++ b/apache/lib/puppet/provider/a2mod/modfix.rb @@ -0,0 +1,12 @@ +Puppet::Type.type(:a2mod).provide :modfix do + desc "Dummy provider for A2mod. + + Fake nil resources when there is no crontab binary available. Allows + puppetd to run on a bootstrapped machine before a Cron package has been + installed. Workaround for: http://projects.puppetlabs.com/issues/2384 + " + + def self.instances + [] + end +end \ No newline at end of file diff --git a/apache/lib/puppet/provider/a2mod/redhat.rb b/apache/lib/puppet/provider/a2mod/redhat.rb new file mode 100644 index 000000000..ea5494cb4 --- /dev/null +++ b/apache/lib/puppet/provider/a2mod/redhat.rb @@ -0,0 +1,60 @@ +require 'puppet/provider/a2mod' + +Puppet::Type.type(:a2mod).provide(:redhat, :parent => Puppet::Provider::A2mod) do + desc "Manage Apache 2 modules on RedHat family OSs" + + commands :apachectl => "apachectl" + + confine :osfamily => :redhat + defaultfor :osfamily => :redhat + + require 'pathname' + + # modpath: Path to default apache modules directory /etc/httpd/mod.d + # modfile: Path to module load configuration file; Default: resides under modpath directory + # libfile: Path to actual apache module library. Added in modfile LoadModule + + attr_accessor :modfile, :libfile + class << self + attr_accessor :modpath + def preinit + @modpath = "/etc/httpd/mod.d" + end + end + + self.preinit + + def create + File.open(modfile,'w') do |f| + f.puts "LoadModule #{resource[:identifier]} #{libfile}" + end + end + + def destroy + File.delete(modfile) + end + + def self.instances + modules = apachectl("-M").lines.collect { |line| + m = line.match(/(\w+)_module \(shared\)$/) + m[1] if m + }.compact + + modules.map do |mod| + new( + :name => mod, + :ensure => :present, + :provider => :redhat + ) + end + end + + def modfile + modfile ||= "#{self.class.modpath}/#{resource[:name]}.load" + end + + # Set libfile path: If absolute path is passed, then maintain it. Else, make it default from 'modules' dir. + def libfile + libfile = Pathname.new(resource[:lib]).absolute? ? resource[:lib] : "modules/#{resource[:lib]}" + end +end diff --git a/apache/lib/puppet/type/a2mod.rb b/apache/lib/puppet/type/a2mod.rb new file mode 100644 index 000000000..07a911e5e --- /dev/null +++ b/apache/lib/puppet/type/a2mod.rb @@ -0,0 +1,30 @@ +Puppet::Type.newtype(:a2mod) do + @doc = "Manage Apache 2 modules" + + ensurable + + newparam(:name) do + Puppet.warning "The a2mod provider is deprecated, please use apache::mod instead" + desc "The name of the module to be managed" + + isnamevar + + end + + newparam(:lib) do + desc "The name of the .so library to be loaded" + + defaultto { "mod_#{@resource[:name]}.so" } + end + + newparam(:identifier) do + desc "Module identifier string used by LoadModule. Default: module-name_module" + + # http://httpd.apache.org/docs/2.2/mod/module-dict.html#ModuleIdentifier + + defaultto { "#{resource[:name]}_module" } + end + + autorequire(:package) { catalog.resource(:package, 'httpd')} + +end diff --git a/apache/manifests/balancer.pp b/apache/manifests/balancer.pp new file mode 100644 index 000000000..1e4130fa3 --- /dev/null +++ b/apache/manifests/balancer.pp @@ -0,0 +1,80 @@ +# == Define Resource Type: apache::balancer +# +# This type will create an apache balancer cluster file inside the conf.d +# directory. Each balancer cluster needs one or more balancer members (that can +# be declared with the apache::balancermember defined resource type). Using +# storeconfigs, you can export the apache::balancermember resources on all +# balancer members, and then collect them on a single apache load balancer +# server. +# +# === Requirement/Dependencies: +# +# Currently requires the puppetlabs/concat module on the Puppet Forge and uses +# storeconfigs on the Puppet Master to export/collect resources from all +# balancer members. +# +# === Parameters +# +# [*name*] +# The namevar of the defined resource type is the balancer clusters name. +# This name is also used in the name of the conf.d file +# +# [*proxy_set*] +# Hash, default empty. If given, each key-value pair will be used as a ProxySet +# line in the configuration. +# +# [*collect_exported*] +# Boolean, default 'true'. True means 'collect exported @@balancermember +# resources' (for the case when every balancermember node exports itself), +# false means 'rely on the existing declared balancermember resources' (for the +# case when you know the full set of balancermembers in advance and use +# apache::balancermember with array arguments, which allows you to deploy +# everything in 1 run) +# +# +# === Examples +# +# Exporting the resource for a balancer member: +# +# apache::balancer { 'puppet00': } +# +define apache::balancer ( + $proxy_set = {}, + $collect_exported = true, +) { + include concat::setup + include apache::mod::proxy_balancer + + $target = "${::apache::params::confd_dir}/balancer_${name}.conf" + + concat { $target: + owner => '0', + group => '0', + mode => '0644', + notify => Service['httpd'], + } + + concat::fragment { "00-${name}-header": + target => $target, + order => '01', + content => "\n", + } + + if $collect_exported { + Apache::Balancermember <<| balancer_cluster == $name |>> + } + # else: the resources have been created and they introduced their + # concat fragments. We don't have to do anything about them. + + concat::fragment { "01-${name}-proxyset": + target => $target, + order => '19', + content => inline_template("<% proxy_set.each do |key, value| %> Proxyset <%= key %>=<%= value %>\n<% end %>"), + } + + concat::fragment { "01-${name}-footer": + target => $target, + order => '20', + content => "\n", + } +} diff --git a/apache/manifests/balancermember.pp b/apache/manifests/balancermember.pp new file mode 100644 index 000000000..c48cb1ebb --- /dev/null +++ b/apache/manifests/balancermember.pp @@ -0,0 +1,52 @@ +# == Define Resource Type: apache::balancermember +# +# This type will setup a balancer member inside a listening service +# configuration block in /etc/apache/apache.cfg on the load balancer. +# currently it only has the ability to specify the instance name, url and an +# array of options. More features can be added as needed. The best way to +# implement this is to export this resource for all apache balancer member +# servers, and then collect them on the main apache load balancer. +# +# === Requirement/Dependencies: +# +# Currently requires the puppetlabs/concat module on the Puppet Forge and +# uses storeconfigs on the Puppet Master to export/collect resources +# from all balancer members. +# +# === Parameters +# +# [*name*] +# The title of the resource is arbitrary and only utilized in the concat +# fragment name. +# +# [*balancer_cluster*] +# The apache service's instance name (or, the title of the apache::balancer +# resource). This must match up with a declared apache::balancer resource. +# +# [*url*] +# The url used to contact the balancer member server. +# +# [*options*] +# An array of options to be specified after the url. +# +# === Examples +# +# Exporting the resource for a balancer member: +# +# @@apache::balancermember { 'apache': +# balancer_cluster => 'puppet00', +# url => "ajp://${::fqdn}:8009" +# options => ['ping=5', 'disablereuse=on', 'retry=5', 'ttl=120'], +# } +# +define apache::balancermember( + $balancer_cluster, + $url = "http://${::fqdn}/", + $options = [], +) { + + concat::fragment { "BalancerMember ${url}": + target => "${::apache::params::confd_dir}/balancer_${balancer_cluster}.conf", + content => inline_template(" BalancerMember ${url} <%= @options.join ' ' %>\n"), + } +} diff --git a/apache/manifests/confd/no_accf.pp b/apache/manifests/confd/no_accf.pp new file mode 100644 index 000000000..5f86eab10 --- /dev/null +++ b/apache/manifests/confd/no_accf.pp @@ -0,0 +1,10 @@ +class apache::confd::no_accf { + # Template uses no variables + file { 'no-accf.conf': + ensure => 'file', + path => "${apache::confd_dir}/no-accf.conf", + content => template('apache/confd/no-accf.conf.erb'), + require => Exec["mkdir ${apache::confd_dir}"], + before => File[$apache::confd_dir], + } +} diff --git a/apache/manifests/default_confd_files.pp b/apache/manifests/default_confd_files.pp new file mode 100644 index 000000000..e40840e33 --- /dev/null +++ b/apache/manifests/default_confd_files.pp @@ -0,0 +1,15 @@ +class apache::default_confd_files ( + $all = true, +) { + # The rest of the conf.d/* files only get loaded if we want them + if $all { + case $::osfamily { + 'freebsd': { + include apache::confd::no_accf + } + default: { + # do nothing + } + } + } +} diff --git a/apache/manifests/default_mods.pp b/apache/manifests/default_mods.pp new file mode 100644 index 000000000..f1f271eae --- /dev/null +++ b/apache/manifests/default_mods.pp @@ -0,0 +1,111 @@ +class apache::default_mods ( + $all = true, + $mods = undef, +) { + # These are modules required to run the default configuration. + # They are not configurable at this time, so we just include + # them to make sure it works. + case $::osfamily { + 'redhat', 'freebsd': { + apache::mod { 'log_config': } + } + default: {} + } + apache::mod { 'authz_host': } + + # The rest of the modules only get loaded if we want all modules enabled + if $all { + case $::osfamily { + 'debian': { + include apache::mod::reqtimeout + } + 'redhat': { + include apache::mod::cache + include apache::mod::mime + include apache::mod::mime_magic + include apache::mod::vhost_alias + include apache::mod::rewrite + apache::mod { 'actions': } + apache::mod { 'auth_digest': } + apache::mod { 'authn_alias': } + apache::mod { 'authn_anon': } + apache::mod { 'authn_dbm': } + apache::mod { 'authn_default': } + apache::mod { 'authz_dbm': } + apache::mod { 'authz_owner': } + apache::mod { 'expires': } + apache::mod { 'ext_filter': } + apache::mod { 'include': } + apache::mod { 'logio': } + apache::mod { 'speling': } + apache::mod { 'substitute': } + apache::mod { 'suexec': } + apache::mod { 'usertrack': } + apache::mod { 'version': } + } + 'freebsd': { + include apache::mod::cache + include apache::mod::disk_cache + include apache::mod::headers + include apache::mod::info + include apache::mod::mime_magic + include apache::mod::reqtimeout + include apache::mod::rewrite + include apache::mod::userdir + include apache::mod::vhost_alias + + apache::mod { 'actions': } + apache::mod { 'asis': } + apache::mod { 'auth_digest': } + apache::mod { 'authn_alias': } + apache::mod { 'authn_anon': } + apache::mod { 'authn_dbm': } + apache::mod { 'authn_default': } + apache::mod { 'authz_dbm': } + apache::mod { 'authz_owner': } + apache::mod { 'cern_meta': } + apache::mod { 'charset_lite': } + apache::mod { 'dumpio': } + apache::mod { 'expires': } + apache::mod { 'file_cache': } + apache::mod { 'filter':} + apache::mod { 'imagemap':} + apache::mod { 'include': } + apache::mod { 'logio': } + apache::mod { 'speling': } + apache::mod { 'unique_id': } + apache::mod { 'usertrack': } + apache::mod { 'version': } + } + default: {} + } + case $apache::mpm_module { + 'prefork': { + include apache::mod::cgi + } + 'worker': { + include apache::mod::cgid + } + default: { + # do nothing + } + } + include apache::mod::alias + include apache::mod::autoindex + include apache::mod::dav + include apache::mod::dav_fs + include apache::mod::deflate + include apache::mod::dir + include apache::mod::mime + include apache::mod::negotiation + include apache::mod::setenvif + apache::mod { 'auth_basic': } + apache::mod { 'authn_file': } + apache::mod { 'authz_default': } + apache::mod { 'authz_groupfile': } + apache::mod { 'authz_user': } + apache::mod { 'env': } + } elsif $mods { + apache::default_mods::load { $mods: } + } +} diff --git a/apache/manifests/default_mods/load.pp b/apache/manifests/default_mods/load.pp new file mode 100644 index 000000000..ae2f76e64 --- /dev/null +++ b/apache/manifests/default_mods/load.pp @@ -0,0 +1,8 @@ +# private define +define apache::default_mods::load ($module = $title) { + if defined("apache::mod::${module}") { + include "apache::mod::${module}" + } else { + apache::mod { $module: } + } +} diff --git a/apache/manifests/dev.pp b/apache/manifests/dev.pp new file mode 100644 index 000000000..057475b15 --- /dev/null +++ b/apache/manifests/dev.pp @@ -0,0 +1,11 @@ +class apache::dev { + if $::osfamily == 'FreeBSD' and !defined(Class['apache::package']) { + fail('apache::dev requires apache::package; please include apache or apache::package class first') + } + include apache::params + $packages = $apache::params::dev_packages + package { $packages: + ensure => present, + require => Package['httpd'], + } +} diff --git a/apache/manifests/init.pp b/apache/manifests/init.pp new file mode 100644 index 000000000..e7d6ce982 --- /dev/null +++ b/apache/manifests/init.pp @@ -0,0 +1,331 @@ +# Class: apache +# +# This class installs Apache +# +# Parameters: +# +# Actions: +# - Install Apache +# - Manage Apache service +# +# Requires: +# +# Sample Usage: +# +class apache ( + $service_name = $apache::params::service_name, + $default_mods = true, + $default_vhost = true, + $default_confd_files = true, + $default_ssl_vhost = false, + $default_ssl_cert = $apache::params::default_ssl_cert, + $default_ssl_key = $apache::params::default_ssl_key, + $default_ssl_chain = undef, + $default_ssl_ca = undef, + $default_ssl_crl_path = undef, + $default_ssl_crl = undef, + $ip = undef, + $service_enable = true, + $service_ensure = 'running', + $purge_configs = true, + $purge_vdir = false, + $serveradmin = 'root@localhost', + $sendfile = 'On', + $error_documents = false, + $timeout = '120', + $httpd_dir = $apache::params::httpd_dir, + $server_root = $apache::params::server_root, + $confd_dir = $apache::params::confd_dir, + $vhost_dir = $apache::params::vhost_dir, + $vhost_enable_dir = $apache::params::vhost_enable_dir, + $mod_dir = $apache::params::mod_dir, + $mod_enable_dir = $apache::params::mod_enable_dir, + $mpm_module = $apache::params::mpm_module, + $conf_template = $apache::params::conf_template, + $servername = $apache::params::servername, + $manage_user = true, + $manage_group = true, + $user = $apache::params::user, + $group = $apache::params::group, + $keepalive = $apache::params::keepalive, + $keepalive_timeout = $apache::params::keepalive_timeout, + $logroot = $apache::params::logroot, + $log_level = $apache::params::log_level, + $ports_file = $apache::params::ports_file, + $server_tokens = 'OS', + $server_signature = 'On', + $trace_enable = 'On', + $package_ensure = 'installed', +) inherits apache::params { + + validate_bool($default_vhost) + validate_bool($default_ssl_vhost) + validate_bool($default_confd_files) + # true/false is sufficient for both ensure and enable + validate_bool($service_enable) + + $valid_mpms_re = $::osfamily ? { + 'FreeBSD' => '(event|itk|peruser|prefork|worker)', + default => '(itk|prefork|worker)' + } + + if $mpm_module { + validate_re($mpm_module, $valid_mpms_re) + } + + # NOTE: on FreeBSD it's mpm module's responsibility to install httpd package. + # NOTE: the same strategy may be introduced for other OSes. For this, you + # should delete the 'if' block below and modify all MPM modules' manifests + # such that they include apache::package class (currently event.pp, itk.pp, + # peruser.pp, prefork.pp, worker.pp). + if $::osfamily != 'FreeBSD' { + package { 'httpd': + ensure => $package_ensure, + name => $apache::params::apache_name, + notify => Class['Apache::Service'], + } + } + validate_re($sendfile, [ '^[oO]n$' , '^[oO]ff$' ]) + + # declare the web server user and group + # Note: requiring the package means the package ought to create them and not puppet + validate_bool($manage_user) + if $manage_user { + user { $user: + ensure => present, + gid => $group, + require => Package['httpd'], + } + } + validate_bool($manage_group) + if $manage_group { + group { $group: + ensure => present, + require => Package['httpd'] + } + } + + $valid_log_level_re = '(emerg|alert|crit|error|warn|notice|info|debug)' + + validate_re($log_level, $valid_log_level_re, + "Log level '${log_level}' is not one of the supported Apache HTTP Server log levels.") + + class { 'apache::service': + service_name => $service_name, + service_enable => $service_enable, + service_ensure => $service_ensure, + } + + # Deprecated backwards-compatibility + if $purge_vdir { + warning('Class[\'apache\'] parameter purge_vdir is deprecated in favor of purge_configs') + $purge_confd = $purge_vdir + } else { + $purge_confd = $purge_configs + } + + Exec { + path => '/bin:/sbin:/usr/bin:/usr/sbin', + } + + exec { "mkdir ${confd_dir}": + creates => $confd_dir, + require => Package['httpd'], + } + file { $confd_dir: + ensure => directory, + recurse => true, + purge => $purge_confd, + notify => Class['Apache::Service'], + require => Package['httpd'], + } + + if ! defined(File[$mod_dir]) { + exec { "mkdir ${mod_dir}": + creates => $mod_dir, + require => Package['httpd'], + } + # Don't purge available modules if an enable dir is used + $purge_mod_dir = $purge_configs and !$mod_enable_dir + file { $mod_dir: + ensure => directory, + recurse => true, + purge => $purge_mod_dir, + notify => Class['Apache::Service'], + require => Package['httpd'], + } + } + + if $mod_enable_dir and ! defined(File[$mod_enable_dir]) { + $mod_load_dir = $mod_enable_dir + exec { "mkdir ${mod_enable_dir}": + creates => $mod_enable_dir, + require => Package['httpd'], + } + file { $mod_enable_dir: + ensure => directory, + recurse => true, + purge => $purge_configs, + notify => Class['Apache::Service'], + require => Package['httpd'], + } + } else { + $mod_load_dir = $mod_dir + } + + if ! defined(File[$vhost_dir]) { + exec { "mkdir ${vhost_dir}": + creates => $vhost_dir, + require => Package['httpd'], + } + file { $vhost_dir: + ensure => directory, + recurse => true, + purge => $purge_configs, + notify => Class['Apache::Service'], + require => Package['httpd'], + } + } + + if $vhost_enable_dir and ! defined(File[$vhost_enable_dir]) { + $vhost_load_dir = $vhost_enable_dir + exec { "mkdir ${vhost_load_dir}": + creates => $vhost_load_dir, + require => Package['httpd'], + } + file { $vhost_enable_dir: + ensure => directory, + recurse => true, + purge => $purge_configs, + notify => Class['Apache::Service'], + require => Package['httpd'], + } + } else { + $vhost_load_dir = $vhost_dir + } + + concat { $ports_file: + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + notify => Class['Apache::Service'], + require => Package['httpd'], + } + concat::fragment { 'Apache ports header': + target => $ports_file, + content => template('apache/ports_header.erb') + } + + if $apache::params::conf_dir and $apache::params::conf_file { + if $::osfamily == 'redhat' or $::operatingsystem == 'amazon' { + $docroot = '/var/www/html' + $pidfile = 'run/httpd.pid' + $error_log = 'error_log' + $error_documents_path = '/var/www/error' + $scriptalias = '/var/www/cgi-bin' + $access_log_file = 'access_log' + } elsif $::osfamily == 'debian' { + $docroot = '/var/www' + $pidfile = '${APACHE_PID_FILE}' + $error_log = 'error.log' + $error_documents_path = '/usr/share/apache2/error' + $scriptalias = '/usr/lib/cgi-bin' + $access_log_file = 'access.log' + } elsif $::osfamily == 'freebsd' { + $docroot = '/usr/local/www/apache22/data' + $pidfile = '/var/run/httpd.pid' + $error_log = 'httpd-error.log' + $error_documents_path = '/usr/local/www/apache22/error' + $scriptalias = '/usr/local/www/apache22/cgi-bin' + $access_log_file = 'httpd-access.log' + } else { + fail("Unsupported osfamily ${::osfamily}") + } + + $apxs_workaround = $::osfamily ? { + 'freebsd' => true, + default => false + } + + # Template uses: + # - $pidfile + # - $user + # - $group + # - $logroot + # - $error_log + # - $sendfile + # - $mod_dir + # - $ports_file + # - $confd_dir + # - $vhost_dir + # - $error_documents + # - $error_documents_path + # - $apxs_workaround + # - $keepalive + # - $keepalive_timeout + # - $server_root + # - $server_tokens + # - $server_signature + # - $trace_enable + file { "${apache::params::conf_dir}/${apache::params::conf_file}": + ensure => file, + content => template($conf_template), + notify => Class['Apache::Service'], + require => Package['httpd'], + } + + # preserve back-wards compatibility to the times when default_mods was + # only a boolean value. Now it can be an array (too) + if is_array($default_mods) { + class { 'apache::default_mods': + all => false, + mods => $default_mods, + } + } else { + class { 'apache::default_mods': + all => $default_mods, + } + } + class { 'apache::default_confd_files': + all => $default_confd_files + } + if $mpm_module { + class { "apache::mod::${mpm_module}": } + } + + $default_vhost_ensure = $default_vhost ? { + true => 'present', + false => 'absent' + } + $default_ssl_vhost_ensure = $default_ssl_vhost ? { + true => 'present', + false => 'absent' + } + + apache::vhost { 'default': + ensure => $default_vhost_ensure, + port => 80, + docroot => $docroot, + scriptalias => $scriptalias, + serveradmin => $serveradmin, + access_log_file => $access_log_file, + priority => '15', + ip => $ip, + } + $ssl_access_log_file = $::osfamily ? { + 'freebsd' => $access_log_file, + default => "ssl_${access_log_file}", + } + apache::vhost { 'default-ssl': + ensure => $default_ssl_vhost_ensure, + port => 443, + ssl => true, + docroot => $docroot, + scriptalias => $scriptalias, + serveradmin => $serveradmin, + access_log_file => $ssl_access_log_file, + priority => '15', + ip => $ip, + } + } +} diff --git a/apache/manifests/listen.pp b/apache/manifests/listen.pp new file mode 100644 index 000000000..3189fa8f7 --- /dev/null +++ b/apache/manifests/listen.pp @@ -0,0 +1,9 @@ +define apache::listen { + $listen_addr_port = $name + + # Template uses: $listen_addr_port + concat::fragment { "Listen ${listen_addr_port}": + target => $apache::ports_file, + content => template('apache/listen.erb'), + } +} diff --git a/apache/manifests/mod.pp b/apache/manifests/mod.pp new file mode 100644 index 000000000..e8ce8a9ab --- /dev/null +++ b/apache/manifests/mod.pp @@ -0,0 +1,120 @@ +define apache::mod ( + $package = undef, + $package_ensure = 'present', + $lib = undef, + $lib_path = $apache::params::lib_path, + $id = undef, + $path = undef, +) { + if ! defined(Class['apache']) { + fail('You must include the apache base class before using any apache defined resources') + } + + $mod = $name + #include apache #This creates duplicate resources in rspec-puppet + $mod_dir = $apache::mod_dir + + # Determine if we have special lib + $mod_libs = $apache::params::mod_libs + $mod_lib = $mod_libs[$mod] # 2.6 compatibility hack + if $lib { + $_lib = $lib + } elsif $mod_lib { + $_lib = $mod_lib + } else { + $_lib = "mod_${mod}.so" + } + + # Determine if declaration specified a path to the module + if $path { + $_path = $path + } else { + $_path = "${lib_path}/${_lib}" + } + + if $id { + $_id = $id + } else { + $_id = "${mod}_module" + } + + # Determine if we have a package + $mod_packages = $apache::params::mod_packages + $mod_package = $mod_packages[$mod] # 2.6 compatibility hack + if $package { + $_package = $package + } elsif $mod_package { + $_package = $mod_package + } + if $_package and ! defined(Package[$_package]) { + # note: FreeBSD/ports uses apxs tool to activate modules; apxs clutters + # httpd.conf with 'LoadModule' directives; here, by proper resource + # ordering, we ensure that our version of httpd.conf is reverted after + # the module gets installed. + $package_before = $::osfamily ? { + 'freebsd' => [ + File["${mod_dir}/${mod}.load"], + File["${apache::params::conf_dir}/${apache::params::conf_file}"] + ], + default => File["${mod_dir}/${mod}.load"], + } + # $_package may be an array + package { $_package: + ensure => $package_ensure, + require => Package['httpd'], + before => $package_before, + } + } + + file { "${mod}.load": + ensure => file, + path => "${mod_dir}/${mod}.load", + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + content => "LoadModule ${_id} ${_path}\n", + require => [ + Package['httpd'], + Exec["mkdir ${mod_dir}"], + ], + before => File[$mod_dir], + notify => Service['httpd'], + } + + if $::osfamily == 'Debian' { + $enable_dir = $apache::mod_enable_dir + file{ "${mod}.load symlink": + ensure => link, + path => "${enable_dir}/${mod}.load", + target => "${mod_dir}/${mod}.load", + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + require => [ + File["${mod}.load"], + Exec["mkdir ${enable_dir}"], + ], + before => File[$enable_dir], + notify => Service['httpd'], + } + # Each module may have a .conf file as well, which should be + # defined in the class apache::mod::module + # Some modules do not require this file. + if defined(File["${mod}.conf"]) { + file{ "${mod}.conf symlink": + ensure => link, + path => "${enable_dir}/${mod}.conf", + target => "${mod_dir}/${mod}.conf", + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + require => [ + File["${mod}.conf"], + Exec["mkdir ${enable_dir}"], + ], + before => File[$enable_dir], + notify => Service['httpd'], + } + } + } +} diff --git a/apache/manifests/mod/alias.pp b/apache/manifests/mod/alias.pp new file mode 100644 index 000000000..aab196eaf --- /dev/null +++ b/apache/manifests/mod/alias.pp @@ -0,0 +1,18 @@ +class apache::mod::alias { + $icons_path = $::osfamily or $::operatingsystem ? { + 'debian' => '/usr/share/apache2/icons', + 'redhat' => '/var/www/icons', + 'amazon' => '/var/www/icons', + 'freebsd' => '/usr/local/www/apache22/icons', + } + apache::mod { 'alias': } + # Template uses $icons_path + file { 'alias.conf': + ensure => file, + path => "${apache::mod_dir}/alias.conf", + content => template('apache/mod/alias.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/auth_basic.pp b/apache/manifests/mod/auth_basic.pp new file mode 100644 index 000000000..8c613eef7 --- /dev/null +++ b/apache/manifests/mod/auth_basic.pp @@ -0,0 +1,3 @@ +class apache::mod::auth_basic { + apache::mod { 'auth_basic': } +} diff --git a/apache/manifests/mod/auth_kerb.pp b/apache/manifests/mod/auth_kerb.pp new file mode 100644 index 000000000..76c2de5b7 --- /dev/null +++ b/apache/manifests/mod/auth_kerb.pp @@ -0,0 +1,5 @@ +class apache::mod::auth_kerb { + apache::mod { 'auth_kerb': } +} + + diff --git a/apache/manifests/mod/authnz_ldap.pp b/apache/manifests/mod/authnz_ldap.pp new file mode 100644 index 000000000..2ca2fc45f --- /dev/null +++ b/apache/manifests/mod/authnz_ldap.pp @@ -0,0 +1,19 @@ +class apache::mod::authnz_ldap ( + $verifyServerCert = true, +) { + include 'apache::mod::ldap' + apache::mod { 'authnz_ldap': } + + validate_bool($verifyServerCert) + + # Template uses: + # - $verifyServerCert + file { 'authnz_ldap.conf': + ensure => file, + path => "${apache::mod_dir}/authnz_ldap.conf", + content => template('apache/mod/authnz_ldap.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/autoindex.pp b/apache/manifests/mod/autoindex.pp new file mode 100644 index 000000000..85b4278ef --- /dev/null +++ b/apache/manifests/mod/autoindex.pp @@ -0,0 +1,12 @@ +class apache::mod::autoindex { + apache::mod { 'autoindex': } + # Template uses no variables + file { 'autoindex.conf': + ensure => file, + path => "${apache::mod_dir}/autoindex.conf", + content => template('apache/mod/autoindex.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/cache.pp b/apache/manifests/mod/cache.pp new file mode 100644 index 000000000..26d71bd06 --- /dev/null +++ b/apache/manifests/mod/cache.pp @@ -0,0 +1,3 @@ +class apache::mod::cache { + apache::mod { 'cache': } +} diff --git a/apache/manifests/mod/cgi.pp b/apache/manifests/mod/cgi.pp new file mode 100644 index 000000000..2ad368a0e --- /dev/null +++ b/apache/manifests/mod/cgi.pp @@ -0,0 +1,4 @@ +class apache::mod::cgi { + Class['apache::mod::prefork'] -> Class['apache::mod::cgi'] + apache::mod { 'cgi': } +} diff --git a/apache/manifests/mod/cgid.pp b/apache/manifests/mod/cgid.pp new file mode 100644 index 000000000..c932ae2be --- /dev/null +++ b/apache/manifests/mod/cgid.pp @@ -0,0 +1,23 @@ +class apache::mod::cgid { + Class['apache::mod::worker'] -> Class['apache::mod::cgid'] + + # Debian specifies it's cgid sock path, but RedHat uses the default value + # with no config file + $cgisock_path = $::osfamily ? { + 'debian' => '${APACHE_RUN_DIR}/cgisock', + 'freebsd' => 'cgisock', + default => undef, + } + apache::mod { 'cgid': } + if $cgisock_path { + # Template uses $cgisock_path + file { 'cgid.conf': + ensure => file, + path => "${apache::mod_dir}/cgid.conf", + content => template('apache/mod/cgid.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } + } +} diff --git a/apache/manifests/mod/dav.pp b/apache/manifests/mod/dav.pp new file mode 100644 index 000000000..06aa087e3 --- /dev/null +++ b/apache/manifests/mod/dav.pp @@ -0,0 +1,3 @@ +class apache::mod::dav { + apache::mod { 'dav': } +} diff --git a/apache/manifests/mod/dav_fs.pp b/apache/manifests/mod/dav_fs.pp new file mode 100644 index 000000000..53219be79 --- /dev/null +++ b/apache/manifests/mod/dav_fs.pp @@ -0,0 +1,20 @@ +class apache::mod::dav_fs { + $dav_lock = $::osfamily ? { + 'debian' => '${APACHE_LOCK_DIR}/DAVLock', + 'freebsd' => '/usr/local/var/DavLock', + default => '/var/lib/dav/lockdb', + } + + Class['apache::mod::dav'] -> Class['apache::mod::dav_fs'] + apache::mod { 'dav_fs': } + + # Template uses: $dav_lock + file { 'dav_fs.conf': + ensure => file, + path => "${apache::mod_dir}/dav_fs.conf", + content => template('apache/mod/dav_fs.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/dav_svn.pp b/apache/manifests/mod/dav_svn.pp new file mode 100644 index 000000000..4f34bd695 --- /dev/null +++ b/apache/manifests/mod/dav_svn.pp @@ -0,0 +1,5 @@ +class apache::mod::dav_svn { + Class['apache::mod::dav'] -> Class['apache::mod::dav_svn'] + include apache::mod::dav + apache::mod { 'dav_svn': } +} diff --git a/apache/manifests/mod/deflate.pp b/apache/manifests/mod/deflate.pp new file mode 100644 index 000000000..97d1fdd8a --- /dev/null +++ b/apache/manifests/mod/deflate.pp @@ -0,0 +1,12 @@ +class apache::mod::deflate { + apache::mod { 'deflate': } + # Template uses no variables + file { 'deflate.conf': + ensure => file, + path => "${apache::mod_dir}/deflate.conf", + content => template('apache/mod/deflate.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/dev.pp b/apache/manifests/mod/dev.pp new file mode 100644 index 000000000..b5d146fbe --- /dev/null +++ b/apache/manifests/mod/dev.pp @@ -0,0 +1,5 @@ +class apache::mod::dev { + # Development packages are not apache modules + warning('apache::mod::dev is deprecated; please use apache::dev') + include apache::dev +} diff --git a/apache/manifests/mod/dir.pp b/apache/manifests/mod/dir.pp new file mode 100644 index 000000000..39543e116 --- /dev/null +++ b/apache/manifests/mod/dir.pp @@ -0,0 +1,21 @@ +# Note: this sets the global DirectoryIndex directive, it may be necessary to consider being able to modify the apache::vhost to declare DirectoryIndex statements in a vhost configuration +# Parameters: +# - $indexes provides a string for the DirectoryIndex directive http://httpd.apache.org/docs/current/mod/mod_dir.html#directoryindex +class apache::mod::dir ( + $dir = 'public_html', + $indexes = ['index.html','index.html.var','index.cgi','index.pl','index.php','index.xhtml'], +) { + validate_array($indexes) + apache::mod { 'dir': } + + # Template uses + # - $indexes + file { 'dir.conf': + ensure => file, + path => "${apache::mod_dir}/dir.conf", + content => template('apache/mod/dir.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/disk_cache.pp b/apache/manifests/mod/disk_cache.pp new file mode 100644 index 000000000..d28196768 --- /dev/null +++ b/apache/manifests/mod/disk_cache.pp @@ -0,0 +1,24 @@ +class apache::mod::disk_cache { + $cache_root = $::osfamily ? { + 'debian' => '/var/cache/apache2/mod_disk_cache', + 'redhat' => '/var/cache/mod_proxy', + 'freebsd' => '/var/cache/mod_disk_cache', + } + if $::osfamily != 'FreeBSD' { + # FIXME: investigate why disk_cache was dependent on proxy + # NOTE: on FreeBSD disk_cache is compiled by default but proxy is not + Class['apache::mod::proxy'] -> Class['apache::mod::disk_cache'] + } + Class['apache::mod::cache'] -> Class['apache::mod::disk_cache'] + + apache::mod { 'disk_cache': } + # Template uses $cache_proxy + file { 'disk_cache.conf': + ensure => file, + path => "${apache::mod_dir}/disk_cache.conf", + content => template('apache/mod/disk_cache.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/event.pp b/apache/manifests/mod/event.pp new file mode 100644 index 000000000..473f7c24c --- /dev/null +++ b/apache/manifests/mod/event.pp @@ -0,0 +1,54 @@ +class apache::mod::event ( + $startservers = '2', + $maxclients = '150', + $minsparethreads = '25', + $maxsparethreads = '75', + $threadsperchild = '25', + $maxrequestsperchild = '0', + $serverlimit = '25', +) { + if defined(Class['apache::mod::itk']) { + fail('May not include both apache::mod::event and apache::mod::itk on the same node') + } + if defined(Class['apache::mod::peruser']) { + fail('May not include both apache::mod::event and apache::mod::peruser on the same node') + } + if defined(Class['apache::mod::prefork']) { + fail('May not include both apache::mod::event and apache::mod::prefork on the same node') + } + if defined(Class['apache::mod::worker']) { + fail('May not include both apache::mod::event and apache::mod::worker on the same node') + } + File { + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + } + + # Template uses: + # - $startservers + # - $maxclients + # - $minsparethreads + # - $maxsparethreads + # - $threadsperchild + # - $maxrequestsperchild + # - $serverlimit + file { "${apache::mod_dir}/event.conf": + ensure => file, + content => template('apache/mod/event.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } + + case $::osfamily { + 'freebsd' : { + class { 'apache::package': + mpm_module => 'event' + } + } + default: { + fail("Unsupported osfamily ${::osfamily}") + } + } +} diff --git a/apache/manifests/mod/expires.pp b/apache/manifests/mod/expires.pp new file mode 100644 index 000000000..6c4b30aca --- /dev/null +++ b/apache/manifests/mod/expires.pp @@ -0,0 +1,3 @@ +class apache::mod::expires { + apache::mod { 'expires': } +} diff --git a/apache/manifests/mod/fastcgi.pp b/apache/manifests/mod/fastcgi.pp new file mode 100644 index 000000000..28673d7bb --- /dev/null +++ b/apache/manifests/mod/fastcgi.pp @@ -0,0 +1,24 @@ +class apache::mod::fastcgi { + + # Debian specifies it's fastcgi lib path, but RedHat uses the default value + # with no config file + $fastcgi_lib_path = $apache::params::fastcgi_lib_path + + apache::mod { 'fastcgi': } + + if $fastcgi_lib_path { + # Template uses: + # - $fastcgi_server + # - $fastcgi_socket + # - $fastcgi_dir + file { 'fastcgi.conf': + ensure => file, + path => "${apache::mod_dir}/fastcgi.conf", + content => template('apache/mod/fastcgi.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } + } + +} diff --git a/apache/manifests/mod/fcgid.pp b/apache/manifests/mod/fcgid.pp new file mode 100644 index 000000000..4c777701e --- /dev/null +++ b/apache/manifests/mod/fcgid.pp @@ -0,0 +1,3 @@ +class apache::mod::fcgid { + apache::mod { 'fcgid': } +} diff --git a/apache/manifests/mod/headers.pp b/apache/manifests/mod/headers.pp new file mode 100644 index 000000000..5ff9887b1 --- /dev/null +++ b/apache/manifests/mod/headers.pp @@ -0,0 +1,3 @@ +class apache::mod::headers { + apache::mod { 'headers': } +} \ No newline at end of file diff --git a/apache/manifests/mod/include.pp b/apache/manifests/mod/include.pp new file mode 100644 index 000000000..9f2592f09 --- /dev/null +++ b/apache/manifests/mod/include.pp @@ -0,0 +1,3 @@ +class apache::mod::include { + apache::mod { 'include': } +} diff --git a/apache/manifests/mod/info.pp b/apache/manifests/mod/info.pp new file mode 100644 index 000000000..b76e1efb2 --- /dev/null +++ b/apache/manifests/mod/info.pp @@ -0,0 +1,14 @@ +class apache::mod::info ( + $allow_from = ['127.0.0.1','::1'], +){ + apache::mod { 'info': } + # Template uses $allow_from + file { 'info.conf': + ensure => file, + path => "${apache::mod_dir}/info.conf", + content => template('apache/mod/info.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/itk.pp b/apache/manifests/mod/itk.pp new file mode 100644 index 000000000..68ece8681 --- /dev/null +++ b/apache/manifests/mod/itk.pp @@ -0,0 +1,64 @@ +class apache::mod::itk ( + $startservers = '8', + $minspareservers = '5', + $maxspareservers = '20', + $serverlimit = '256', + $maxclients = '256', + $maxrequestsperchild = '4000', +) { + if defined(Class['apache::mod::event']) { + fail('May not include both apache::mod::itk and apache::mod::event on the same node') + } + if defined(Class['apache::mod::peruser']) { + fail('May not include both apache::mod::itk and apache::mod::peruser on the same node') + } + if defined(Class['apache::mod::prefork']) { + fail('May not include both apache::mod::itk and apache::mod::prefork on the same node') + } + if defined(Class['apache::mod::worker']) { + fail('May not include both apache::mod::itk and apache::mod::worker on the same node') + } + File { + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + } + + # Template uses: + # - $startservers + # - $minspareservers + # - $maxspareservers + # - $serverlimit + # - $maxclients + # - $maxrequestsperchild + file { "${apache::mod_dir}/itk.conf": + ensure => file, + content => template('apache/mod/itk.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } + + case $::osfamily { + 'debian' : { + file { "${apache::mod_enable_dir}/itk.conf": + ensure => link, + target => "${apache::mod_dir}/itk.conf", + require => Exec["mkdir ${apache::mod_enable_dir}"], + before => File[$apache::mod_enable_dir], + notify => Service['httpd'], + } + package { 'apache2-mpm-itk': + ensure => present, + } + } + 'freebsd' : { + class { 'apache::package': + mpm_module => 'itk' + } + } + default: { + fail("Unsupported osfamily ${::osfamily}") + } + } +} diff --git a/apache/manifests/mod/ldap.pp b/apache/manifests/mod/ldap.pp new file mode 100644 index 000000000..097622c51 --- /dev/null +++ b/apache/manifests/mod/ldap.pp @@ -0,0 +1,12 @@ +class apache::mod::ldap { + apache::mod { 'ldap': } + # Template uses no variables + file { 'ldap.conf': + ensure => file, + path => "${apache::mod_dir}/ldap.conf", + content => template('apache/mod/ldap.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/mime.pp b/apache/manifests/mod/mime.pp new file mode 100644 index 000000000..5c9f4b7ad --- /dev/null +++ b/apache/manifests/mod/mime.pp @@ -0,0 +1,21 @@ +class apache::mod::mime ( + $mime_support_package = $apache::params::mime_support_package, + $mime_types_config = $apache::params::mime_types_config, +) { + apache::mod { 'mime': } + # Template uses $mime_types_config + file { 'mime.conf': + ensure => file, + path => "${apache::mod_dir}/mime.conf", + content => template('apache/mod/mime.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } + if $mime_support_package { + package { $mime_support_package: + ensure => 'installed', + before => File["${apache::mod_dir}/mime.conf"], + } + } +} diff --git a/apache/manifests/mod/mime_magic.pp b/apache/manifests/mod/mime_magic.pp new file mode 100644 index 000000000..5539d20e6 --- /dev/null +++ b/apache/manifests/mod/mime_magic.pp @@ -0,0 +1,14 @@ +class apache::mod::mime_magic ( + $magic_file = "${apache::params::conf_dir}/magic" +) { + apache::mod { 'mime_magic': } + # Template uses $magic_file + file { 'mime_magic.conf': + ensure => file, + path => "${apache::mod_dir}/mime_magic.conf", + content => template('apache/mod/mime_magic.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/negotiation.pp b/apache/manifests/mod/negotiation.pp new file mode 100644 index 000000000..e10c4921d --- /dev/null +++ b/apache/manifests/mod/negotiation.pp @@ -0,0 +1,12 @@ +class apache::mod::negotiation { + apache::mod { 'negotiation': } + # Template uses no variables + file { 'negotiation.conf': + ensure => file, + path => "${apache::mod_dir}/negotiation.conf", + content => template('apache/mod/negotiation.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/nss.pp b/apache/manifests/mod/nss.pp new file mode 100644 index 000000000..a9a50a106 --- /dev/null +++ b/apache/manifests/mod/nss.pp @@ -0,0 +1,25 @@ +class apache::mod::nss ( + $transfer_log = "${::apache::params::logroot}/access.log", + $error_log = "${::apache::params::logroot}/error.log", + $passwd_file = undef + ) { + include apache::mod::mime + + apache::mod { 'nss': } + + $httpd_dir = $apache::httpd_dir + + # Template uses: + # $transfer_log + # $error_log + # $http_dir + # passwd_file + file { 'nss.conf': + ensure => file, + path => "${apache::mod_dir}/nss.conf", + content => template('apache/mod/nss.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/passenger.pp b/apache/manifests/mod/passenger.pp new file mode 100644 index 000000000..e7561651d --- /dev/null +++ b/apache/manifests/mod/passenger.pp @@ -0,0 +1,48 @@ +class apache::mod::passenger ( + $passenger_conf_file = $apache::params::passenger_conf_file, + $passenger_conf_package_file = $apache::params::passenger_conf_package_file, + $passenger_high_performance = undef, + $passenger_pool_idle_time = undef, + $passenger_max_requests = undef, + $passenger_stat_throttle_rate = undef, + $rack_autodetect = undef, + $rails_autodetect = undef, + $passenger_root = $apache::params::passenger_root, + $passenger_ruby = $apache::params::passenger_ruby, + $passenger_max_pool_size = undef, + $passenger_use_global_queue = undef, +) { + if $::osfamily == 'FreeBSD' { + apache::mod { 'passenger': + lib_path => "${passenger_root}/buildout/apache2" + } + } else { + apache::mod { 'passenger': } + } + + # Managed by the package, but declare it to avoid purging + if $passenger_conf_package_file { + file { 'passenger_package.conf': + path => "${apache::mod_dir}/${passenger_conf_package_file}", + } + } + + # Template uses: + # - $passenger_root + # - $passenger_ruby + # - $passenger_max_pool_size + # - $passenger_high_performance + # - $passenger_max_requests + # - $passenger_stat_throttle_rate + # - $passenger_use_global_queue + # - $rack_autodetect + # - $rails_autodetect + file { 'passenger.conf': + ensure => file, + path => "${apache::mod_dir}/${passenger_conf_file}", + content => template('apache/mod/passenger.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/perl.pp b/apache/manifests/mod/perl.pp new file mode 100644 index 000000000..65832a034 --- /dev/null +++ b/apache/manifests/mod/perl.pp @@ -0,0 +1,3 @@ +class apache::mod::perl { + apache::mod { 'perl': } +} diff --git a/apache/manifests/mod/peruser.pp b/apache/manifests/mod/peruser.pp new file mode 100644 index 000000000..fafeff23a --- /dev/null +++ b/apache/manifests/mod/peruser.pp @@ -0,0 +1,73 @@ +class apache::mod::peruser ( + $minspareprocessors = '2', + $minprocessors = '2', + $maxprocessors = '10', + $maxclients = '150', + $maxrequestsperchild = '1000', + $idletimeout = '120', + $expiretimeout = '120', + $keepalive = 'Off', +) { + if defined(Class['apache::mod::event']) { + fail('May not include both apache::mod::peruser and apache::mod::event on the same node') + } + if defined(Class['apache::mod::itk']) { + fail('May not include both apache::mod::peruser and apache::mod::itk on the same node') + } + if defined(Class['apache::mod::prefork']) { + fail('May not include both apache::mod::peruser and apache::mod::prefork on the same node') + } + if defined(Class['apache::mod::worker']) { + fail('May not include both apache::mod::peruser and apache::mod::worker on the same node') + } + File { + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + } + + $mod_dir = $apache::mod_dir + + # Template uses: + # - $minspareprocessors + # - $minprocessors + # - $maxprocessors + # - $maxclients + # - $maxrequestsperchild + # - $idletimeout + # - $expiretimeout + # - $keepalive + # - $mod_dir + file { "${apache::mod_dir}/peruser.conf": + ensure => file, + content => template('apache/mod/peruser.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } + file { "${apache::mod_dir}/peruser": + ensure => directory, + require => File[$apache::mod_dir], + } + file { "${apache::mod_dir}/peruser/multiplexers": + ensure => directory, + require => File["${apache::mod_dir}/peruser"], + } + file { "${apache::mod_dir}/peruser/processors": + ensure => directory, + require => File["${apache::mod_dir}/peruser"], + } + + apache::peruser::multiplexer { '01-default': } + + case $::osfamily { + 'freebsd' : { + class { 'apache::package': + mpm_module => 'peruser' + } + } + default: { + fail("Unsupported osfamily ${::osfamily}") + } + } +} diff --git a/apache/manifests/mod/php.pp b/apache/manifests/mod/php.pp new file mode 100644 index 000000000..f4e01fe3b --- /dev/null +++ b/apache/manifests/mod/php.pp @@ -0,0 +1,26 @@ +class apache::mod::php ( + $package_ensure = 'present', +) { + if ! defined(Class['apache::mod::prefork']) { + fail('apache::mod::php requires apache::mod::prefork; please enable mpm_module => \'prefork\' on Class[\'apache\']') + } + apache::mod { 'php5': + package_ensure => $package_ensure, + } + + include apache::mod::mime + include apache::mod::dir + Class['apache::mod::mime'] -> Class['apache::mod::dir'] -> Class['apache::mod::php'] + + file { 'php5.conf': + ensure => file, + path => "${apache::mod_dir}/php5.conf", + content => template('apache/mod/php5.conf.erb'), + require => [ + Class['apache::mod::prefork'], + Exec["mkdir ${apache::mod_dir}"], + ], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/prefork.pp b/apache/manifests/mod/prefork.pp new file mode 100644 index 000000000..ecbf809a2 --- /dev/null +++ b/apache/manifests/mod/prefork.pp @@ -0,0 +1,74 @@ +class apache::mod::prefork ( + $startservers = '8', + $minspareservers = '5', + $maxspareservers = '20', + $serverlimit = '256', + $maxclients = '256', + $maxrequestsperchild = '4000', +) { + if defined(Class['apache::mod::event']) { + fail('May not include both apache::mod::prefork and apache::mod::event on the same node') + } + if defined(Class['apache::mod::itk']) { + fail('May not include both apache::mod::prefork and apache::mod::itk on the same node') + } + if defined(Class['apache::mod::peruser']) { + fail('May not include both apache::mod::prefork and apache::mod::peruser on the same node') + } + if defined(Class['apache::mod::worker']) { + fail('May not include both apache::mod::prefork and apache::mod::worker on the same node') + } + File { + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + } + + # Template uses: + # - $startservers + # - $minspareservers + # - $maxspareservers + # - $serverlimit + # - $maxclients + # - $maxrequestsperchild + file { "${apache::mod_dir}/prefork.conf": + ensure => file, + content => template('apache/mod/prefork.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } + + case $::osfamily { + 'redhat': { + file_line { '/etc/sysconfig/httpd prefork enable': + ensure => present, + path => '/etc/sysconfig/httpd', + line => '#HTTPD=/usr/sbin/httpd.worker', + match => '#?HTTPD=/usr/sbin/httpd.worker', + require => Package['httpd'], + notify => Service['httpd'], + } + } + 'debian': { + file { "${apache::mod_enable_dir}/prefork.conf": + ensure => link, + target => "${apache::mod_dir}/prefork.conf", + require => Exec["mkdir ${apache::mod_enable_dir}"], + before => File[$apache::mod_enable_dir], + notify => Service['httpd'], + } + package { 'apache2-mpm-prefork': + ensure => present, + } + } + 'freebsd' : { + class { 'apache::package': + mpm_module => 'prefork' + } + } + default: { + fail("Unsupported osfamily ${::osfamily}") + } + } +} diff --git a/apache/manifests/mod/proxy.pp b/apache/manifests/mod/proxy.pp new file mode 100644 index 000000000..f91673430 --- /dev/null +++ b/apache/manifests/mod/proxy.pp @@ -0,0 +1,15 @@ +class apache::mod::proxy ( + $proxy_requests = 'Off', + $allow_from = undef, +) { + apache::mod { 'proxy': } + # Template uses $proxy_requests + file { 'proxy.conf': + ensure => file, + path => "${apache::mod_dir}/proxy.conf", + content => template('apache/mod/proxy.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/proxy_ajp.pp b/apache/manifests/mod/proxy_ajp.pp new file mode 100644 index 000000000..b366cb1df --- /dev/null +++ b/apache/manifests/mod/proxy_ajp.pp @@ -0,0 +1,4 @@ +class apache::mod::proxy_ajp { + Class['apache::mod::proxy'] -> Class['apache::mod::proxy_ajp'] + apache::mod { 'proxy_ajp': } +} diff --git a/apache/manifests/mod/proxy_balancer.pp b/apache/manifests/mod/proxy_balancer.pp new file mode 100644 index 000000000..f31e5eaff --- /dev/null +++ b/apache/manifests/mod/proxy_balancer.pp @@ -0,0 +1,10 @@ +class apache::mod::proxy_balancer { + + include apache::mod::proxy + include apache::mod::proxy_http + + Class['apache::mod::proxy'] -> Class['apache::mod::proxy_balancer'] + Class['apache::mod::proxy_http'] -> Class['apache::mod::proxy_balancer'] + apache::mod { 'proxy_balancer': } + +} diff --git a/apache/manifests/mod/proxy_html.pp b/apache/manifests/mod/proxy_html.pp new file mode 100644 index 000000000..050b65d4f --- /dev/null +++ b/apache/manifests/mod/proxy_html.pp @@ -0,0 +1,28 @@ +class apache::mod::proxy_html { + Class['apache::mod::proxy'] -> Class['apache::mod::proxy_html'] + Class['apache::mod::proxy_http'] -> Class['apache::mod::proxy_html'] + apache::mod { 'proxy_html': } + case $::osfamily { + 'RedHat': { + apache::mod { 'xml2enc': } + } + 'Debian': { + $proxy_html_loadfiles = $apache::params::distrelease ? { + '6' => '/usr/lib/libxml2.so.2', + default => "/usr/lib/${::hardwaremodel}-linux-gnu/libxml2.so.2", + } + } + 'FreeBSD': { + apache::mod { 'xml2enc': } + } + } + # Template uses $icons_path + file { 'proxy_html.conf': + ensure => file, + path => "${apache::mod_dir}/proxy_html.conf", + content => template('apache/mod/proxy_html.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/proxy_http.pp b/apache/manifests/mod/proxy_http.pp new file mode 100644 index 000000000..5b83df2c5 --- /dev/null +++ b/apache/manifests/mod/proxy_http.pp @@ -0,0 +1,4 @@ +class apache::mod::proxy_http { + Class['apache::mod::proxy'] -> Class['apache::mod::proxy_http'] + apache::mod { 'proxy_http': } +} diff --git a/apache/manifests/mod/python.pp b/apache/manifests/mod/python.pp new file mode 100644 index 000000000..8158b7e8a --- /dev/null +++ b/apache/manifests/mod/python.pp @@ -0,0 +1,5 @@ +class apache::mod::python { + apache::mod { 'python': } +} + + diff --git a/apache/manifests/mod/reqtimeout.pp b/apache/manifests/mod/reqtimeout.pp new file mode 100644 index 000000000..b763b3785 --- /dev/null +++ b/apache/manifests/mod/reqtimeout.pp @@ -0,0 +1,12 @@ +class apache::mod::reqtimeout { + apache::mod { 'reqtimeout': } + # Template uses no variables + file { 'reqtimeout.conf': + ensure => file, + path => "${apache::mod_dir}/reqtimeout.conf", + content => template('apache/mod/reqtimeout.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/rewrite.pp b/apache/manifests/mod/rewrite.pp new file mode 100644 index 000000000..147faab99 --- /dev/null +++ b/apache/manifests/mod/rewrite.pp @@ -0,0 +1,4 @@ +class apache::mod::rewrite { + include apache::params + apache::mod { 'rewrite': } +} diff --git a/apache/manifests/mod/rpaf.pp b/apache/manifests/mod/rpaf.pp new file mode 100644 index 000000000..94dc31cca --- /dev/null +++ b/apache/manifests/mod/rpaf.pp @@ -0,0 +1,20 @@ +class apache::mod::rpaf ( + $sethostname = true, + $proxy_ips = [ '127.0.0.1' ], + $header = 'X-Forwarded-For' +) { + apache::mod { 'rpaf': } + + # Template uses: + # - $sethostname + # - $proxy_ips + # - $header + file { 'rpaf.conf': + ensure => file, + path => "${apache::mod_dir}/rpaf.conf", + content => template('apache/mod/rpaf.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/setenvif.pp b/apache/manifests/mod/setenvif.pp new file mode 100644 index 000000000..1b60edde8 --- /dev/null +++ b/apache/manifests/mod/setenvif.pp @@ -0,0 +1,12 @@ +class apache::mod::setenvif { + apache::mod { 'setenvif': } + # Template uses no variables + file { 'setenvif.conf': + ensure => file, + path => "${apache::mod_dir}/setenvif.conf", + content => template('apache/mod/setenvif.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/ssl.pp b/apache/manifests/mod/ssl.pp new file mode 100644 index 000000000..3002d14be --- /dev/null +++ b/apache/manifests/mod/ssl.pp @@ -0,0 +1,26 @@ +class apache::mod::ssl ( + $ssl_compression = false, + $ssl_options = [ 'StdEnvVars' ], +) { + $session_cache = $::osfamily ? { + 'debian' => '${APACHE_RUN_DIR}/ssl_scache(512000)', + 'redhat' => '/var/cache/mod_ssl/scache(512000)', + 'freebsd' => '/var/run/ssl_scache(512000)', + } + $ssl_mutex = $::osfamily ? { + 'debian' => 'file:${APACHE_RUN_DIR}/ssl_mutex', + 'redhat' => 'default', + 'freebsd' => 'default', + } + apache::mod { 'ssl': } + + # Template uses $ssl_compression, $ssl_options, $session_cache, $ssl_mutex + file { 'ssl.conf': + ensure => file, + path => "${apache::mod_dir}/ssl.conf", + content => template('apache/mod/ssl.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/status.pp b/apache/manifests/mod/status.pp new file mode 100644 index 000000000..ed73b99fd --- /dev/null +++ b/apache/manifests/mod/status.pp @@ -0,0 +1,42 @@ +# Class: apache::mod::status +# +# This class enables and configures Apache mod_status +# See: http://httpd.apache.org/docs/current/mod/mod_status.html +# +# Parameters: +# - $allow_from is an array of hosts, ip addresses, partial network numbers +# or networks in CIDR notation specifying what hosts can view the special +# /server-status URL. Defaults to ['127.0.0.1', '::1']. +# - $extended_status track and display extended status information. Valid +# values are 'On' or 'Off'. Defaults to 'On'. +# +# Actions: +# - Enable and configure Apache mod_status +# +# Requires: +# - The apache class +# +# Sample Usage: +# +# # Simple usage allowing access from localhost and a private subnet +# class { 'apache::mod::status': +# $allow_from => ['127.0.0.1', '10.10.10.10/24'], +# } +# +class apache::mod::status ( + $allow_from = ['127.0.0.1','::1'], + $extended_status = 'On', +){ + validate_array($allow_from) + validate_re(downcase($extended_status), '^(on|off)$', "${extended_status} is not supported for extended_status. Allowed values are 'On' and 'Off'.") + apache::mod { 'status': } + # Template uses $allow_from, $extended_status + file { 'status.conf': + ensure => file, + path => "${apache::mod_dir}/status.conf", + content => template('apache/mod/status.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/suphp.pp b/apache/manifests/mod/suphp.pp new file mode 100644 index 000000000..26473cf2e --- /dev/null +++ b/apache/manifests/mod/suphp.pp @@ -0,0 +1,14 @@ +class apache::mod::suphp ( +){ + apache::mod { 'suphp': } + + file {'suphp.conf': + ensure => file, + path => "${apache::mod_dir}/suphp.conf", + content => template('apache/mod/suphp.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'] + } +} + diff --git a/apache/manifests/mod/userdir.pp b/apache/manifests/mod/userdir.pp new file mode 100644 index 000000000..69f4044fd --- /dev/null +++ b/apache/manifests/mod/userdir.pp @@ -0,0 +1,17 @@ +class apache::mod::userdir ( + $home = '/home', + $dir = 'public_html', + $disable_root = true, +) { + apache::mod { 'userdir': } + + # Template uses $home, $dir, $disable_root + file { 'userdir.conf': + ensure => file, + path => "${apache::mod_dir}/userdir.conf", + content => template('apache/mod/userdir.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/mod/vhost_alias.pp b/apache/manifests/mod/vhost_alias.pp new file mode 100644 index 000000000..ec40447a3 --- /dev/null +++ b/apache/manifests/mod/vhost_alias.pp @@ -0,0 +1,3 @@ +class apache::mod::vhost_alias { + apache::mod { 'vhost_alias': } +} diff --git a/apache/manifests/mod/worker.pp b/apache/manifests/mod/worker.pp new file mode 100644 index 000000000..272891176 --- /dev/null +++ b/apache/manifests/mod/worker.pp @@ -0,0 +1,75 @@ +class apache::mod::worker ( + $startservers = '2', + $maxclients = '150', + $minsparethreads = '25', + $maxsparethreads = '75', + $threadsperchild = '25', + $maxrequestsperchild = '0', + $serverlimit = '25', +) { + if defined(Class['apache::mod::event']) { + fail('May not include both apache::mod::worker and apache::mod::event on the same node') + } + if defined(Class['apache::mod::itk']) { + fail('May not include both apache::mod::worker and apache::mod::itk on the same node') + } + if defined(Class['apache::mod::peruser']) { + fail('May not include both apache::mod::worker and apache::mod::peruser on the same node') + } + if defined(Class['apache::mod::prefork']) { + fail('May not include both apache::mod::worker and apache::mod::prefork on the same node') + } + File { + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + } + + # Template uses: + # - $startservers + # - $maxclients + # - $minsparethreads + # - $maxsparethreads + # - $threadsperchild + # - $maxrequestsperchild + # - $serverlimit + file { "${apache::mod_dir}/worker.conf": + ensure => file, + content => template('apache/mod/worker.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'], + } + + case $::osfamily { + 'redhat': { + file_line { '/etc/sysconfig/httpd worker enable': + ensure => present, + path => '/etc/sysconfig/httpd', + line => 'HTTPD=/usr/sbin/httpd.worker', + match => '#?HTTPD=/usr/sbin/httpd.worker', + notify => Service['httpd'], + } + } + 'debian': { + file { "${apache::mod_enable_dir}/worker.conf": + ensure => link, + target => "${apache::mod_dir}/worker.conf", + require => Exec["mkdir ${apache::mod_enable_dir}"], + before => File[$apache::mod_enable_dir], + notify => Service['httpd'], + } + package { 'apache2-mpm-worker': + ensure => present, + } + } + 'freebsd' : { + class { 'apache::package': + mpm_module => 'worker' + } + } + default: { + fail("Unsupported osfamily ${::osfamily}") + } + } +} diff --git a/apache/manifests/mod/wsgi.pp b/apache/manifests/mod/wsgi.pp new file mode 100644 index 000000000..0d4271409 --- /dev/null +++ b/apache/manifests/mod/wsgi.pp @@ -0,0 +1,21 @@ +class apache::mod::wsgi ( + $wsgi_socket_prefix = undef, + $wsgi_python_path = undef, + $wsgi_python_home = undef, +){ + apache::mod { 'wsgi': } + + # Template uses: + # - $wsgi_socket_prefix + # - $wsgi_python_path + # - $wsgi_python_home + file {'wsgi.conf': + ensure => file, + path => "${apache::mod_dir}/wsgi.conf", + content => template('apache/mod/wsgi.conf.erb'), + require => Exec["mkdir ${apache::mod_dir}"], + before => File[$apache::mod_dir], + notify => Service['httpd'] + } +} + diff --git a/apache/manifests/mod/xsendfile.pp b/apache/manifests/mod/xsendfile.pp new file mode 100644 index 000000000..571501a03 --- /dev/null +++ b/apache/manifests/mod/xsendfile.pp @@ -0,0 +1,4 @@ +class apache::mod::xsendfile { + include apache::params + apache::mod { 'xsendfile': } +} diff --git a/apache/manifests/namevirtualhost.pp b/apache/manifests/namevirtualhost.pp new file mode 100644 index 000000000..f5f0dad53 --- /dev/null +++ b/apache/manifests/namevirtualhost.pp @@ -0,0 +1,9 @@ +define apache::namevirtualhost { + $addr_port = $name + + # Template uses: $addr_port + concat::fragment { "NameVirtualHost ${addr_port}": + target => $apache::ports_file, + content => template('apache/namevirtualhost.erb'), + } +} diff --git a/apache/manifests/package.pp b/apache/manifests/package.pp new file mode 100644 index 000000000..31bd31198 --- /dev/null +++ b/apache/manifests/package.pp @@ -0,0 +1,48 @@ +class apache::package ( + $ensure = 'present', + $mpm_module = $apache::params::mpm_module, +) { + case $::osfamily { + 'freebsd' : { + $all_mpms = [ + 'www/apache22', + 'www/apache22-worker-mpm', + 'www/apache22-event-mpm', + 'www/apache22-itk-mpm', + 'www/apache22-peruser-mpm', + ] + if $mpm_module { + $apache_package = $mpm_module ? { + 'prefork' => 'www/apache22', + default => "www/apache22-${mpm_module}-mpm" + } + } else { + $apache_package = 'www/apache22' + } + $other_mpms = delete($all_mpms, $apache_package) + # Configure ports to have apache module packages dependent on correct + # version of apache package (apache22, apache22-worker-mpm, ...) + file_line { 'APACHE_PORT in /etc/make.conf': + ensure => $ensure, + path => '/etc/make.conf', + line => "APACHE_PORT=${apache_package}", + match => "^\\s*#?\\s*APACHE_PORT\\s*=\\s*", + before => Package['httpd'], + } + # remove other packages + ensure_resource('package', $other_mpms, { + ensure => absent, + before => Package['httpd'], + require => File_line['APACHE_PORT in /etc/make.conf'], + }) + } + default: { + $apache_package = $apache::params::apache_name + } + } + package { 'httpd': + ensure => $ensure, + name => $apache_package, + notify => Class['Apache::Service'], + } +} diff --git a/apache/manifests/params.pp b/apache/manifests/params.pp new file mode 100644 index 000000000..2a0554db1 --- /dev/null +++ b/apache/manifests/params.pp @@ -0,0 +1,213 @@ +# Class: apache::params +# +# This class manages Apache parameters +# +# Parameters: +# - The $user that Apache runs as +# - The $group that Apache runs as +# - The $apache_name is the name of the package and service on the relevant +# distribution +# - The $php_package is the name of the package that provided PHP +# - The $ssl_package is the name of the Apache SSL package +# - The $apache_dev is the name of the Apache development libraries package +# - The $conf_contents is the contents of the Apache configuration file +# +# Actions: +# +# Requires: +# +# Sample Usage: +# +class apache::params { + # This will be 5 or 6 on RedHat, 6 or wheezy on Debian, 12 or quantal on Ubuntu, 3 on Amazon, etc. + $osr_array = split($::operatingsystemrelease,'[\/\.]') + $distrelease = $osr_array[0] + if ! $distrelease { + fail("Class['apache::params']: Unparsable \$::operatingsystemrelease: ${::operatingsystemrelease}") + } + + if($::fqdn) { + $servername = $::fqdn + } else { + $servername = $::hostname + } + + # The default error log level + $log_level = 'warn' + + if $::osfamily == 'RedHat' or $::operatingsystem == 'amazon' { + $user = 'apache' + $group = 'apache' + $root_group = 'root' + $apache_name = 'httpd' + $service_name = 'httpd' + $httpd_dir = '/etc/httpd' + $server_root = '/etc/httpd' + $conf_dir = "${httpd_dir}/conf" + $confd_dir = "${httpd_dir}/conf.d" + $mod_dir = "${httpd_dir}/conf.d" + $vhost_dir = "${httpd_dir}/conf.d" + $conf_file = 'httpd.conf' + $ports_file = "${conf_dir}/ports.conf" + $logroot = '/var/log/httpd' + $lib_path = 'modules' + $mpm_module = 'prefork' + $dev_packages = 'httpd-devel' + $default_ssl_cert = '/etc/pki/tls/certs/localhost.crt' + $default_ssl_key = '/etc/pki/tls/private/localhost.key' + $ssl_certs_dir = '/etc/pki/tls/certs' + $passenger_conf_file = 'passenger_extra.conf' + $passenger_conf_package_file = 'passenger.conf' + $passenger_root = undef + $passenger_ruby = undef + $suphp_addhandler = 'php5-script' + $suphp_engine = 'off' + $suphp_configpath = undef + $mod_packages = { + 'auth_kerb' => 'mod_auth_kerb', + 'authnz_ldap' => 'mod_authz_ldap', + 'fastcgi' => 'mod_fastcgi', + 'fcgid' => 'mod_fcgid', + 'passenger' => 'mod_passenger', + 'perl' => 'mod_perl', + 'php5' => $distrelease ? { + '5' => 'php53', + default => 'php', + }, + 'proxy_html' => 'mod_proxy_html', + 'python' => 'mod_python', + 'shibboleth' => 'shibboleth', + 'ssl' => 'mod_ssl', + 'wsgi' => 'mod_wsgi', + 'dav_svn' => 'mod_dav_svn', + 'suphp' => 'mod_suphp', + 'xsendfile' => 'mod_xsendfile', + 'nss' => 'mod_nss', + } + $mod_libs = { + 'php5' => 'libphp5.so', + 'nss' => 'libmodnss.so', + } + $conf_template = 'apache/httpd.conf.erb' + $keepalive = 'Off' + $keepalive_timeout = 15 + $fastcgi_lib_path = undef + $mime_support_package = 'mailcap' + $mime_types_config = '/etc/mime.types' + } elsif $::osfamily == 'Debian' { + $user = 'www-data' + $group = 'www-data' + $root_group = 'root' + $apache_name = 'apache2' + $service_name = 'apache2' + $httpd_dir = '/etc/apache2' + $server_root = '/etc/apache2' + $conf_dir = $httpd_dir + $confd_dir = "${httpd_dir}/conf.d" + $mod_dir = "${httpd_dir}/mods-available" + $mod_enable_dir = "${httpd_dir}/mods-enabled" + $vhost_dir = "${httpd_dir}/sites-available" + $vhost_enable_dir = "${httpd_dir}/sites-enabled" + $conf_file = 'apache2.conf' + $ports_file = "${conf_dir}/ports.conf" + $logroot = '/var/log/apache2' + $lib_path = '/usr/lib/apache2/modules' + $mpm_module = 'worker' + $dev_packages = ['libaprutil1-dev', 'libapr1-dev', 'apache2-prefork-dev'] + $default_ssl_cert = '/etc/ssl/certs/ssl-cert-snakeoil.pem' + $default_ssl_key = '/etc/ssl/private/ssl-cert-snakeoil.key' + $ssl_certs_dir = '/etc/ssl/certs' + $passenger_conf_file = 'passenger.conf' + $passenger_conf_package_file = undef + $passenger_root = '/usr' + $passenger_ruby = '/usr/bin/ruby' + $suphp_addhandler = 'x-httpd-php' + $suphp_engine = 'off' + $suphp_configpath = '/etc/php5/apache2' + $mod_packages = { + 'auth_kerb' => 'libapache2-mod-auth-kerb', + 'dav_svn' => 'libapache2-svn', + 'fastcgi' => 'libapache2-mod-fastcgi', + 'fcgid' => 'libapache2-mod-fcgid', + 'nss' => 'libapache2-mod-nss', + 'passenger' => 'libapache2-mod-passenger', + 'perl' => 'libapache2-mod-perl2', + 'php5' => 'libapache2-mod-php5', + 'proxy_html' => 'libapache2-mod-proxy-html', + 'python' => 'libapache2-mod-python', + 'rpaf' => 'libapache2-mod-rpaf', + 'suphp' => 'libapache2-mod-suphp', + 'wsgi' => 'libapache2-mod-wsgi', + 'xsendfile' => 'libapache2-mod-xsendfile', + } + $mod_libs = { + 'php5' => 'libphp5.so', + } + $conf_template = 'apache/httpd.conf.erb' + $keepalive = 'Off' + $keepalive_timeout = 15 + $fastcgi_lib_path = '/var/lib/apache2/fastcgi' + $mime_support_package = 'mime-support' + $mime_types_config = '/etc/mime.types' + } elsif $::osfamily == 'FreeBSD' { + $user = 'www' + $group = 'www' + $root_group = 'wheel' + $apache_name = 'apache22' + $service_name = 'apache22' + $httpd_dir = '/usr/local/etc/apache22' + $server_root = '/usr/local' + $conf_dir = $httpd_dir + $confd_dir = "${httpd_dir}/Includes" + $mod_dir = "${httpd_dir}/Modules" + $mod_enable_dir = undef + $vhost_dir = "${httpd_dir}/Vhosts" + $vhost_enable_dir = undef + $conf_file = 'httpd.conf' + $ports_file = "${conf_dir}/ports.conf" + $logroot = '/var/log/apache22' + $lib_path = '/usr/local/libexec/apache22' + $mpm_module = 'prefork' + $dev_packages = undef + $default_ssl_cert = '/usr/local/etc/apache22/server.crt' + $default_ssl_key = '/usr/local/etc/apache22/server.key' + $ssl_certs_dir = '/usr/local/etc/apache22' + $passenger_conf_file = 'passenger.conf' + $passenger_conf_package_file = undef + $passenger_root = '/usr/local/lib/ruby/gems/1.9/gems/passenger-4.0.10' + $passenger_ruby = '/usr/bin/ruby' + $suphp_addhandler = 'php5-script' + $suphp_engine = 'off' + $suphp_configpath = undef + $mod_packages = { + # NOTE: I list here only modules that are not included in www/apache22 + # NOTE: 'passenger' needs to enable APACHE_SUPPORT in make config + # NOTE: 'php' needs to enable APACHE option in make config + # NOTE: 'dav_svn' needs to enable MOD_DAV_SVN make config + # NOTE: not sure where the shibboleth should come from + # NOTE: don't know where the shibboleth module should come from + 'auth_kerb' => 'www/mod_auth_kerb2', + 'fcgid' => 'www/mod_fcgid', + 'passenger' => 'www/rubygem-passenger', + 'perl' => 'www/mod_perl2', + 'php5' => 'lang/php5', + 'proxy_html' => 'www/mod_proxy_html', + 'python' => 'www/mod_python3', + 'wsgi' => 'www/mod_wsgi', + 'dav_svn' => 'devel/subversion', + 'xsendfile' => 'www/mod_xsendfile', + 'rpaf' => 'www/mod_rpaf2' + } + $mod_libs = { + 'php5' => 'libphp5.so', + } + $conf_template = 'apache/httpd.conf.erb' + $keepalive = 'Off' + $keepalive_timeout = 15 + $fastcgi_lib_path = undef # TODO: revisit + $mime_support_package = 'misc/mime-support' + $mime_types_config = '/usr/local/etc/mime.types' + } else { + fail("Class['apache::params']: Unsupported osfamily: ${::osfamily}") + } +} diff --git a/apache/manifests/peruser/multiplexer.pp b/apache/manifests/peruser/multiplexer.pp new file mode 100644 index 000000000..260f1b5a3 --- /dev/null +++ b/apache/manifests/peruser/multiplexer.pp @@ -0,0 +1,17 @@ +define apache::peruser::multiplexer ( + $user = $apache::user, + $group = $apache::group, + $file = undef, +) { + if ! $file { + $filename = "${name}.conf" + } else { + $filename = $file + } + file { "${apache::mod_dir}/peruser/multiplexers/${filename}": + ensure => file, + content => "Multiplexer ${user} ${group}\n", + require => File["${apache::mod_dir}/peruser/multiplexers"], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/peruser/processor.pp b/apache/manifests/peruser/processor.pp new file mode 100644 index 000000000..c677cd6ca --- /dev/null +++ b/apache/manifests/peruser/processor.pp @@ -0,0 +1,17 @@ +define apache::peruser::processor ( + $user, + $group, + $file = undef, +) { + if ! $file { + $filename = "${name}.conf" + } else { + $filename = $file + } + file { "${apache::mod_dir}/peruser/processors/${filename}": + ensure => file, + content => "Processor ${user} ${group}\n", + require => File["${apache::mod_dir}/peruser/processors"], + notify => Service['httpd'], + } +} diff --git a/apache/manifests/php.pp b/apache/manifests/php.pp new file mode 100644 index 000000000..feb903e7b --- /dev/null +++ b/apache/manifests/php.pp @@ -0,0 +1,18 @@ +# Class: apache::php +# +# This class installs PHP for Apache +# +# Parameters: +# - $php_package +# +# Actions: +# - Install Apache PHP package +# +# Requires: +# +# Sample Usage: +# +class apache::php { + warning('apache::php is deprecated; please use apache::mod::php') + include apache::mod::php +} diff --git a/apache/manifests/proxy.pp b/apache/manifests/proxy.pp new file mode 100644 index 000000000..0f4fde540 --- /dev/null +++ b/apache/manifests/proxy.pp @@ -0,0 +1,15 @@ +# Class: apache::proxy +# +# This class enabled the proxy module for Apache +# +# Actions: +# - Enables Apache Proxy module +# +# Requires: +# +# Sample Usage: +# +class apache::proxy { + warning('apache::proxy is deprecated; please use apache::mod::proxy') + include apache::mod::proxy +} diff --git a/apache/manifests/python.pp b/apache/manifests/python.pp new file mode 100644 index 000000000..99ef28987 --- /dev/null +++ b/apache/manifests/python.pp @@ -0,0 +1,18 @@ +# Class: apache::python +# +# This class installs Python for Apache +# +# Parameters: +# - $php_package +# +# Actions: +# - Install Apache Python package +# +# Requires: +# +# Sample Usage: +# +class apache::python { + warning('apache::python is deprecated; please use apache::mod::python') + include apache::mod::python +} diff --git a/apache/manifests/service.pp b/apache/manifests/service.pp new file mode 100644 index 000000000..82a0b8ca6 --- /dev/null +++ b/apache/manifests/service.pp @@ -0,0 +1,35 @@ +# Class: apache::service +# +# Manages the Apache daemon +# +# Parameters: +# +# Actions: +# - Manage Apache service +# +# Requires: +# +# Sample Usage: +# +# sometype { 'foo': +# notify => Class['apache::service], +# } +# +# +class apache::service ( + $service_name = $apache::params::service_name, + $service_enable = true, + $service_ensure = 'running', +) { + # The base class must be included first because parameter defaults depend on it + if ! defined(Class['apache::params']) { + fail('You must include the apache::params class before using any apache defined resources') + } + validate_bool($service_enable) + + service { 'httpd': + ensure => $service_ensure, + name => $service_name, + enable => $service_enable, + } +} diff --git a/apache/manifests/ssl.pp b/apache/manifests/ssl.pp new file mode 100644 index 000000000..21662e168 --- /dev/null +++ b/apache/manifests/ssl.pp @@ -0,0 +1,18 @@ +# Class: apache::ssl +# +# This class installs Apache SSL capabilities +# +# Parameters: +# - The $ssl_package name from the apache::params class +# +# Actions: +# - Install Apache SSL capabilities +# +# Requires: +# +# Sample Usage: +# +class apache::ssl { + warning('apache::ssl is deprecated; please use apache::mod::ssl') + include apache::mod::ssl +} diff --git a/apache/manifests/vhost.pp b/apache/manifests/vhost.pp new file mode 100644 index 000000000..b339134ca --- /dev/null +++ b/apache/manifests/vhost.pp @@ -0,0 +1,523 @@ +# Definition: apache::vhost +# +# This class installs Apache Virtual Hosts +# +# Parameters: +# - The $port to configure the host on +# - The $docroot provides the DocumentRoot variable +# - The $virtual_docroot provides VirtualDocumentationRoot variable +# - The $serveradmin will specify an email address for Apache that it will +# display when it renders one of it's error pages +# - The $ssl option is set true or false to enable SSL for this Virtual Host +# - The $priority of the site +# - The $servername is the primary name of the virtual host +# - The $serveraliases of the site +# - The $ip to configure the host on, defaulting to * +# - The $options for the given vhost +# - The $override for the given vhost (list of AllowOverride arguments) +# - The $vhost_name for name based virtualhosting, defaulting to * +# - The $logroot specifies the location of the virtual hosts logfiles, default +# to /var/log// +# - The $log_level specifies the verbosity of the error log for this vhost. Not +# set by default for the vhost, instead the global server configuration default +# of 'warn' is used. +# - The $access_log specifies if *_access.log directives should be configured. +# - The $ensure specifies if vhost file is present or absent. +# - The $headers is a list of Header statement strings as per http://httpd.apache.org/docs/2.2/mod/mod_headers.html#header +# - The $request_headers is a list of RequestHeader statement strings as per http://httpd.apache.org/docs/2.2/mod/mod_headers.html#requestheader +# - $aliases is a list of Alias hashes for mod_alias as per http://httpd.apache.org/docs/current/mod/mod_alias.html +# each statement is a hash in the form of { alias => '/alias', path => '/real/path/to/directory' } +# - $directories is a lost of hashes for creating statements as per http://httpd.apache.org/docs/2.2/mod/core.html#directory +# each statement is a hash in the form of { path => '/path/to/directory', => } +# see README.md for list of supported directives. +# +# Actions: +# - Install Apache Virtual Hosts +# +# Requires: +# - The apache class +# +# Sample Usage: +# +# # Simple vhost definition: +# apache::vhost { 'site.name.fqdn': +# port => '80', +# docroot => '/path/to/docroot', +# } +# +# # Multiple Mod Rewrites: +# apache::vhost { 'site.name.fqdn': +# port => '80', +# docroot => '/path/to/docroot', +# rewrites => [ +# { +# comment => 'force www domain', +# rewrite_cond => ['%{HTTP_HOST} ^([a-z.]+)?example.com$ [NC]', '%{HTTP_HOST} !^www. [NC]'], +# rewrite_rule => ['.? http://www.%1example.com%{REQUEST_URI} [R=301,L]'] +# }, +# { +# comment => 'prevent image hotlinking', +# rewrite_cond => ['%{HTTP_REFERER} !^$', '%{HTTP_REFERER} !^http://(www.)?example.com/ [NC]'], +# rewrite_rule => ['.(gif|jpg|png)$ - [F]'] +# }, +# ] +# } +# +# # SSL vhost with non-SSL rewrite: +# apache::vhost { 'site.name.fqdn': +# port => '443', +# ssl => true, +# docroot => '/path/to/docroot', +# } +# apache::vhost { 'site.name.fqdn': +# port => '80', +# rewrites => [ +# { +# comment => "redirect non-SSL traffic to SSL site", +# rewrite_cond => ['%{HTTPS} off'], +# rewrite_rule => ['(.*) https://%{HTTPS_HOST}%{REQUEST_URI}'] +# } +# ] +# } +# apache::vhost { 'site.name.fqdn': +# port => '80', +# docroot => '/path/to/other_docroot', +# custom_fragment => template("${module_name}/my_fragment.erb"), +# } +# +define apache::vhost( + $docroot, + $virtual_docroot = false, + $port = undef, + $ip = undef, + $ip_based = false, + $add_listen = true, + $docroot_owner = 'root', + $docroot_group = $apache::params::root_group, + $serveradmin = false, + $ssl = false, + $ssl_cert = $apache::default_ssl_cert, + $ssl_key = $apache::default_ssl_key, + $ssl_chain = $apache::default_ssl_chain, + $ssl_ca = $apache::default_ssl_ca, + $ssl_crl_path = $apache::default_ssl_crl_path, + $ssl_crl = $apache::default_ssl_crl, + $ssl_certs_dir = $apache::params::ssl_certs_dir, + $ssl_protocol = undef, + $ssl_cipher = undef, + $ssl_honorcipherorder = undef, + $ssl_verify_client = undef, + $ssl_verify_depth = undef, + $ssl_options = undef, + $ssl_proxyengine = false, + $priority = undef, + $default_vhost = false, + $servername = $name, + $serveraliases = [], + $options = ['Indexes','FollowSymLinks','MultiViews'], + $override = ['None'], + $directoryindex = '', + $vhost_name = '*', + $logroot = $apache::logroot, + $log_level = undef, + $access_log = true, + $access_log_file = undef, + $access_log_pipe = undef, + $access_log_syslog = undef, + $access_log_format = undef, + $access_log_env_var = undef, + $aliases = undef, + $directories = undef, + $error_log = true, + $error_log_file = undef, + $error_log_pipe = undef, + $error_log_syslog = undef, + $error_documents = [], + $fallbackresource = undef, + $scriptalias = undef, + $scriptaliases = [], + $proxy_dest = undef, + $proxy_pass = undef, + $suphp_addhandler = $apache::params::suphp_addhandler, + $suphp_engine = $apache::params::suphp_engine, + $suphp_configpath = $apache::params::suphp_configpath, + $php_admin_flags = [], + $php_admin_values = [], + $no_proxy_uris = [], + $redirect_source = '/', + $redirect_dest = undef, + $redirect_status = undef, + $redirectmatch_status = undef, + $redirectmatch_regexp = undef, + $rack_base_uris = undef, + $headers = undef, + $request_headers = undef, + $rewrites = undef, + $rewrite_rule = undef, + $rewrite_cond = undef, + $setenv = [], + $setenvif = [], + $block = [], + $ensure = 'present', + $wsgi_daemon_process = undef, + $wsgi_daemon_process_options = undef, + $wsgi_process_group = undef, + $wsgi_script_aliases = undef, + $custom_fragment = undef, + $itk = undef, + $fastcgi_server = undef, + $fastcgi_socket = undef, + $fastcgi_dir = undef, + $additional_includes = [], + ) { + # The base class must be included first because it is used by parameter defaults + if ! defined(Class['apache']) { + fail('You must include the apache base class before using any apache defined resources') + } + $apache_name = $apache::params::apache_name + + validate_re($ensure, '^(present|absent)$', + "${ensure} is not supported for ensure. + Allowed values are 'present' and 'absent'.") + validate_re($suphp_engine, '^(on|off)$', + "${suphp_engine} is not supported for suphp_engine. + Allowed values are 'on' and 'off'.") + validate_bool($ip_based) + validate_bool($access_log) + validate_bool($error_log) + validate_bool($ssl) + validate_bool($default_vhost) + validate_bool($ssl_proxyengine) + if $rewrites { + validate_array($rewrites) + validate_hash($rewrites[0]) + } + + # Deprecated backwards-compatibility + if $rewrite_rule { + warning('Apache::Vhost: parameter rewrite_rule is deprecated in favor of rewrites') + } + if $rewrite_cond { + warning('Apache::Vhost parameter rewrite_cond is deprecated in favor of rewrites') + } + + if $wsgi_script_aliases { + validate_hash($wsgi_script_aliases) + } + if $wsgi_daemon_process_options { + validate_hash($wsgi_daemon_process_options) + } + if $itk { + validate_hash($itk) + } + + if $log_level { + validate_re($log_level, '^(emerg|alert|crit|error|warn|notice|info|debug)$', + "Log level '${log_level}' is not one of the supported Apache HTTP Server log levels.") + } + + if $access_log_file and $access_log_pipe { + fail("Apache::Vhost[${name}]: 'access_log_file' and 'access_log_pipe' cannot be defined at the same time") + } + + if $error_log_file and $error_log_pipe { + fail("Apache::Vhost[${name}]: 'error_log_file' and 'error_log_pipe' cannot be defined at the same time") + } + + if $fallbackresource { + validate_re($fallbackresource, '^/|disabled', 'Please make sure fallbackresource starts with a / (or is "disabled")') + } + + if $ssl and $ensure == 'present' { + include apache::mod::ssl + # Required for the AddType lines. + include apache::mod::mime + } + + if $virtual_docroot { + include apache::mod::vhost_alias + } + + # This ensures that the docroot exists + # But enables it to be specified across multiple vhost resources + if ! defined(File[$docroot]) { + file { $docroot: + ensure => directory, + owner => $docroot_owner, + group => $docroot_group, + require => Package['httpd'], + } + } + + # Same as above, but for logroot + if ! defined(File[$logroot]) { + file { $logroot: + ensure => directory, + require => Package['httpd'], + } + } + + + # Is apache::mod::passenger enabled (or apache::mod['passenger']) + $passenger_enabled = defined(Apache::Mod['passenger']) + + # Define log file names + if $access_log_file { + $access_log_destination = "${logroot}/${access_log_file}" + } elsif $access_log_pipe { + $access_log_destination = "\"${access_log_pipe}\"" + } elsif $access_log_syslog { + $access_log_destination = $access_log_syslog + } else { + if $ssl { + $access_log_destination = "${logroot}/${name}_access_ssl.log" + } else { + $access_log_destination = "${logroot}/${name}_access.log" + } + } + + if $error_log_file { + $error_log_destination = "${logroot}/${error_log_file}" + } elsif $error_log_pipe { + $error_log_destination = "\"${error_log_pipe}\"" + } elsif $error_log_syslog { + $error_log_destination = $error_log_syslog + } else { + if $ssl { + $error_log_destination = "${logroot}/${name}_error_ssl.log" + } else { + $error_log_destination = "${logroot}/${name}_error.log" + } + } + + # Set access log format + if $access_log_format { + $_access_log_format = "\"${access_log_format}\"" + } else { + $_access_log_format = 'combined' + } + + if $access_log_env_var { + $_access_log_env_var = "env=${access_log_env_var}" + } + + if $ip { + if $port { + $listen_addr_port = "${ip}:${port}" + $nvh_addr_port = "${ip}:${port}" + } else { + $nvh_addr_port = $ip + if ! $servername and ! $ip_based { + fail("Apache::Vhost[${name}]: must pass 'ip' and/or 'port' parameters for name-based vhosts") + } + } + } else { + if $port { + $listen_addr_port = $port + $nvh_addr_port = "${vhost_name}:${port}" + } else { + $nvh_addr_port = $name + if ! $servername { + fail("Apache::Vhost[${name}]: must pass 'ip' and/or 'port' parameters, and/or 'servername' parameter") + } + } + } + if $add_listen { + if $ip and defined(Apache::Listen[$port]) { + fail("Apache::Vhost[${name}]: Mixing IP and non-IP Listen directives is not possible; check the add_listen parameter of the apache::vhost define to disable this") + } + if ! defined(Apache::Listen[$listen_addr_port]) and $listen_addr_port and $ensure == 'present' { + apache::listen { $listen_addr_port: } + } + } + if ! $ip_based { + if ! defined(Apache::Namevirtualhost[$nvh_addr_port]) and $ensure == 'present' { + apache::namevirtualhost { $nvh_addr_port: } + } + } + + # Load mod_rewrite if needed and not yet loaded + if $rewrites or $rewrite_cond { + if ! defined(Apache::Mod['rewrite']) { + apache::mod { 'rewrite': } + } + } + + # Load mod_alias if needed and not yet loaded + if ($scriptalias or $scriptaliases != []) or ($redirect_source and $redirect_dest) { + if ! defined(Class['apache::mod::alias']) { + include apache::mod::alias + } + } + + # Load mod_proxy if needed and not yet loaded + if ($proxy_dest or $proxy_pass) { + if ! defined(Class['apache::mod::proxy']) { + include apache::mod::proxy + } + if ! defined(Class['apache::mod::proxy_http']) { + include apache::mod::proxy_http + } + } + + # Load mod_passenger if needed and not yet loaded + if $rack_base_uris { + if ! defined(Class['apache::mod::passenger']) { + include apache::mod::passenger + } + } + + # Load mod_fastci if needed and not yet loaded + if $fastcgi_server and $fastcgi_socket { + if ! defined(Class['apache::mod::fastcgi']) { + include apache::mod::fastcgi + } + } + + # Configure the defaultness of a vhost + if $priority { + $priority_real = $priority + } elsif $default_vhost { + $priority_real = '10' + } else { + $priority_real = '25' + } + + # Check if mod_headers is required to process $headers/$request_headers + if $headers or $request_headers { + if ! defined(Class['apache::mod::headers']) { + include apache::mod::headers + } + } + + ## Apache include does not always work with spaces in the filename + $filename = regsubst($name, ' ', '_', 'G') + + ## Create a default directory list if none defined + if $directories { + if !is_hash($directories) and !(is_array($directories) and is_hash($directories[0])) { + fail("Apache::Vhost[${name}]: 'directories' must be either a Hash or an Array of Hashes") + } + $_directories = $directories + } else { + $_directories = [ { + provider => 'directory', + path => $docroot, + options => $options, + allow_override => $override, + directoryindex => $directoryindex, + order => 'allow,deny', + allow => 'from all', + } ] + } + + # Template uses: + # - $nvh_addr_port + # - $servername + # - $serveradmin + # - $docroot + # - $virtual_docroot + # - $options + # - $override + # - $logroot + # - $name + # - $aliases + # - $_directories + # - $log_level + # - $access_log + # - $access_log_destination + # - $_access_log_format + # - $_access_log_env_var + # - $error_log + # - $error_log_destination + # - $error_documents + # - $fallbackresource + # - $custom_fragment + # - $additional_includes + # block fragment: + # - $block + # directories fragment: + # - $passenger_enabled + # - $php_admin_flags + # - $php_admin_values + # - $directories (a list of key-value hashes is expected) + # fastcgi fragment: + # - $fastcgi_server + # - $fastcgi_socket + # - $fastcgi_dir + # proxy fragment: + # - $proxy_dest + # - $no_proxy_uris + # rack fragment: + # - $rack_base_uris + # redirect fragment: + # - $redirect_source + # - $redirect_dest + # - $redirect_status + # header fragment + # - $headers + # requestheader fragment: + # - $request_headers + # rewrite fragment: + # - $rewrites + # scriptalias fragment: + # - $scriptalias + # - $scriptaliases + # - $ssl + # serveralias fragment: + # - $serveraliases + # setenv fragment: + # - $setenv + # - $setenvif + # ssl fragment: + # - $ssl + # - $ssl_cert + # - $ssl_key + # - $ssl_chain + # - $ssl_certs_dir + # - $ssl_ca + # - $ssl_crl + # - $ssl_crl_path + # - $ssl_verify_client + # - $ssl_verify_depth + # - $ssl_options + # suphp fragment: + # - $suphp_addhandler + # - $suphp_engine + # - $suphp_configpath + # wsgi fragment: + # - $wsgi_daemon_process + # - $wsgi_process_group + # - $wsgi_script_aliases + file { "${priority_real}-${filename}.conf": + ensure => $ensure, + path => "${apache::vhost_dir}/${priority_real}-${filename}.conf", + content => template('apache/vhost.conf.erb'), + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + require => [ + Package['httpd'], + File[$docroot], + File[$logroot], + ], + notify => Service['httpd'], + } + if $::osfamily == 'Debian' { + $vhost_enable_dir = $apache::vhost_enable_dir + $vhost_symlink_ensure = $ensure ? { + present => link, + default => $ensure, + } + file{ "${priority_real}-${filename}.conf symlink": + ensure => $vhost_symlink_ensure, + path => "${vhost_enable_dir}/${priority_real}-${filename}.conf", + target => "${apache::vhost_dir}/${priority_real}-${filename}.conf", + owner => 'root', + group => $apache::params::root_group, + mode => '0644', + require => File["${priority_real}-${filename}.conf"], + notify => Service['httpd'], + } + } +} diff --git a/apache/spec/acceptance/apache_parameters_spec.rb b/apache/spec/acceptance/apache_parameters_spec.rb new file mode 100644 index 000000000..be398f016 --- /dev/null +++ b/apache/spec/acceptance/apache_parameters_spec.rb @@ -0,0 +1,408 @@ +require 'spec_helper_acceptance' + +case fact('osfamily') +when 'RedHat' + confd_dir = '/etc/httpd/conf.d' + conf_file = '/etc/httpd/conf/httpd.conf' + ports_file = '/etc/httpd/conf/ports.conf' + vhost = '/etc/httpd/conf.d/15-default.conf' + service_name = 'httpd' + package_name = 'httpd' + error_log = 'error_log' +when 'Debian' + confd_dir = '/etc/apache2/mods-available' + conf_file = '/etc/apache2/apache2.conf' + ports_file = '/etc/apache2/ports.conf' + vhost = '/etc/apache2/sites-available/15-default.conf' + service_name = 'apache2' + package_name = 'apache2' + error_log = 'error.log' +end + +describe 'apache parameters' do + + # Currently this test only does something on FreeBSD. + describe 'default_confd_files => false' do + it 'doesnt do anything' do + pp = "class { 'apache': default_confd_files => false }" + apply_manifest(pp, :catch_failures => true) + end + + if fact('osfamily') == 'FreeBSD' + describe file("#{confd_dir}/no-accf.conf.erb") do + it { should_not be_file } + end + end + end + describe 'default_confd_files => true' do + it 'copies conf.d files' do + pp = "class { 'apache': default_confd_files => true }" + apply_manifest(pp, :catch_failures => true) + end + + if fact('osfamily') == 'FreeBSD' + describe file("#{confd_dir}/no-accf.conf.erb") do + it { should be_file } + end + end + end + + describe 'when set adds a listen statement' do + it 'applys cleanly' do + pp = "class { 'apache': ip => '10.1.1.1', service_ensure => stopped }" + apply_manifest(pp, :catch_failures => true) + end + + describe file(ports_file) do + it { should be_file } + it { should contain 'Listen 10.1.1.1' } + end + end + + describe 'service tests => true' do + it 'starts the service' do + pp = <<-EOS + class { 'apache': + service_enable => true, + service_ensure => running, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { should be_running } + it { should be_enabled } + end + end + + describe 'service tests => false' do + it 'stops the service' do + pp = <<-EOS + class { 'apache': + service_enable => false, + service_ensure => stopped, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { should_not be_running } + it { should_not be_enabled } + end + end + + describe 'purge parameters => false' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': + purge_configs => false, + purge_vdir => false, + } + EOS + shell("touch #{confd_dir}/test.conf") + apply_manifest(pp, :catch_failures => true) + end + + # Ensure the file didn't disappear. + describe file("#{confd_dir}/test.conf") do + it { should be_file } + end + end + + describe 'purge parameters => true' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': + purge_configs => true, + purge_vdir => true, + } + EOS + shell("touch #{confd_dir}/test.conf") + apply_manifest(pp, :catch_failures => true) + end + + # File should be gone + describe file("#{confd_dir}/test.conf") do + it { should_not be_file } + end + end + + describe 'serveradmin' do + it 'applies cleanly' do + pp = "class { 'apache': serveradmin => 'test@example.com' }" + apply_manifest(pp, :catch_failures => true) + end + + describe file(vhost) do + it { should be_file } + it { should contain 'ServerAdmin test@example.com' } + end + end + + describe 'sendfile' do + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': sendfile => 'On' }" + apply_manifest(pp, :catch_failures => true) + end + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'EnableSendfile On' } + end + + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': sendfile => 'Off' }" + apply_manifest(pp, :catch_failures => true) + end + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'Sendfile Off' } + end + end + + describe 'error_documents' do + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': error_documents => true }" + apply_manifest(pp, :catch_failures => true) + end + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'Alias /error/' } + end + end + + describe 'timeout' do + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': timeout => '1234' }" + apply_manifest(pp, :catch_failures => true) + end + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'Timeout 1234' } + end + end + + describe 'httpd_dir' do + describe 'setup' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': httpd_dir => '/tmp', service_ensure => stopped } + include 'apache::mod::mime' + EOS + apply_manifest(pp, :catch_failures => true) + end + end + + describe file("#{confd_dir}/mime.conf") do + it { should be_file } + it { should contain 'AddLanguage eo .eo' } + end + end + + describe 'server_root' do + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': server_root => '/tmp/root', service_ensure => stopped }" + apply_manifest(pp, :catch_failures => true) + end + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'ServerRoot "/tmp/root"' } + end + end + + describe 'confd_dir' do + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': confd_dir => '/tmp/root', service_ensure => stopped }" + apply_manifest(pp, :catch_failures => true) + end + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'Include /tmp/root/*.conf' } + end + end + + describe 'conf_template' do + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': conf_template => 'another/test.conf.erb', service_ensure => stopped }" + shell("mkdir -p #{default['distmoduledir']}/another/templates") + shell("echo 'testcontent' >> #{default['distmoduledir']}/another/templates/test.conf.erb") + apply_manifest(pp, :catch_failures => true) + end + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'testcontent' } + end + end + + describe 'servername' do + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': servername => 'test.server', service_ensure => stopped }" + apply_manifest(pp, :catch_failures => true) + end + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'ServerName "test.server"' } + end + end + + describe 'user' do + describe 'setup' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': + manage_user => true, + manage_group => true, + user => 'testweb', + group => 'testweb', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + end + + describe user('testweb') do + it { should exist } + it { should belong_to_group 'testweb' } + end + + describe group('testweb') do + it { should exist } + end + end + + describe 'keepalive' do + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': keepalive => 'On', keepalive_timeout => '30' }" + apply_manifest(pp, :catch_failures => true) + end + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'KeepAlive On' } + it { should contain 'KeepAliveTimeout 30' } + end + end + + describe 'logging' do + describe 'setup' do + it 'applies cleanly' do + pp = "class { 'apache': logroot => '/tmp' }" + apply_manifest(pp, :catch_failures => true) + end + end + + describe file("/tmp/#{error_log}") do + it { should be_file } + end + end + + describe 'ports_file' do + it 'applys cleanly' do + pp = <<-EOS + class { 'apache': + ports_file => '/tmp/ports_file', + ip => '10.1.1.1', + service_ensure => stopped + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file('/tmp/ports_file') do + it { should be_file } + it { should contain 'Listen 10.1.1.1' } + end + end + + describe 'server_tokens' do + it 'applys cleanly' do + pp = <<-EOS + class { 'apache': + server_tokens => 'testtokens', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'ServerTokens testtokens' } + end + end + + describe 'server_signature' do + it 'applys cleanly' do + pp = <<-EOS + class { 'apache': + server_signature => 'testsig', + service_ensure => stopped, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'ServerSignature testsig' } + end + end + + describe 'trace_enable' do + it 'applys cleanly' do + pp = <<-EOS + class { 'apache': + trace_enable => 'Off', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file(conf_file) do + it { should be_file } + it { should contain 'TraceEnable Off' } + end + end + + describe 'package_ensure' do + it 'applys cleanly' do + pp = <<-EOS + class { 'apache': + package_ensure => present, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe package(package_name) do + it { should be_installed } + end + end + +end diff --git a/apache/spec/acceptance/apache_ssl_spec.rb b/apache/spec/acceptance/apache_ssl_spec.rb new file mode 100644 index 000000000..f577e5e93 --- /dev/null +++ b/apache/spec/acceptance/apache_ssl_spec.rb @@ -0,0 +1,87 @@ +require 'spec_helper_acceptance' + +case fact('osfamily') +when 'RedHat' + vhostd = '/etc/httpd/conf.d' +when 'Debian' + vhostd = '/etc/apache2/sites-available' +end + +describe 'apache ssl' do + + describe 'ssl parameters' do + it 'runs without error' do + pp = <<-EOS + class { 'apache': + service_ensure => stopped, + default_ssl_vhost => true, + default_ssl_cert => '/tmp/ssl_cert', + default_ssl_key => '/tmp/ssl_key', + default_ssl_chain => '/tmp/ssl_chain', + default_ssl_ca => '/tmp/ssl_ca', + default_ssl_crl_path => '/tmp/ssl_crl_path', + default_ssl_crl => '/tmp/ssl_crl', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhostd}/15-default-ssl.conf") do + it { should be_file } + it { should contain 'SSLCertificateFile /tmp/ssl_cert' } + it { should contain 'SSLCertificateKeyFile /tmp/ssl_key' } + it { should contain 'SSLCertificateChainFile /tmp/ssl_chain' } + it { should contain 'SSLCACertificateFile /tmp/ssl_ca' } + it { should contain 'SSLCARevocationPath /tmp/ssl_crl_path' } + it { should contain 'SSLCARevocationFile /tmp/ssl_crl' } + end + end + + describe 'vhost ssl parameters' do + it 'runs without error' do + pp = <<-EOS + class { 'apache': + service_ensure => stopped, + } + + apache::vhost { 'test_ssl': + docroot => '/tmp/test', + ssl => true, + ssl_cert => '/tmp/ssl_cert', + ssl_key => '/tmp/ssl_key', + ssl_chain => '/tmp/ssl_chain', + ssl_ca => '/tmp/ssl_ca', + ssl_crl_path => '/tmp/ssl_crl_path', + ssl_crl => '/tmp/ssl_crl', + ssl_certs_dir => '/tmp', + ssl_protocol => 'test', + ssl_cipher => 'test', + ssl_honorcipherorder => 'test', + ssl_verify_client => 'test', + ssl_verify_depth => 'test', + ssl_options => ['test', 'test1'], + ssl_proxyengine => true, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhostd}/25-test_ssl.conf") do + it { should be_file } + it { should contain 'SSLCertificateFile /tmp/ssl_cert' } + it { should contain 'SSLCertificateKeyFile /tmp/ssl_key' } + it { should contain 'SSLCertificateChainFile /tmp/ssl_chain' } + it { should contain 'SSLCACertificateFile /tmp/ssl_ca' } + it { should contain 'SSLCARevocationPath /tmp/ssl_crl_path' } + it { should contain 'SSLCARevocationFile /tmp/ssl_crl' } + it { should contain 'SSLProxyEngine On' } + it { should contain 'SSLProtocol test' } + it { should contain 'SSLCipherSuite test' } + it { should contain 'SSLHonorCipherOrder test' } + it { should contain 'SSLVerifyClient test' } + it { should contain 'SSLVerifyDepth test' } + it { should contain 'SSLOptions test test1' } + end + end + +end diff --git a/apache/spec/acceptance/basic_spec.rb b/apache/spec/acceptance/basic_spec.rb new file mode 100644 index 000000000..bc456e9ed --- /dev/null +++ b/apache/spec/acceptance/basic_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper_acceptance' + +describe 'disable selinux:' do + it "because otherwise apache won't work" do + apply_manifest(%{ + exec { "setenforce 0": + path => "/bin:/sbin:/usr/bin:/usr/sbin", + onlyif => "which setenforce && getenforce | grep Enforcing", + } + }, :catch_failures => true) + end +end diff --git a/apache/spec/acceptance/class_spec.rb b/apache/spec/acceptance/class_spec.rb new file mode 100644 index 000000000..62107ed61 --- /dev/null +++ b/apache/spec/acceptance/class_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper_acceptance' + +describe 'apache class' do + case fact('osfamily') + when 'RedHat' + package_name = 'httpd' + service_name = 'httpd' + when 'Debian' + package_name = 'apache2' + service_name = 'apache2' + when 'FreeBSD' + package_name = 'apache22' + service_name = 'apache22' + end + + context 'default parameters' do + it 'should work with no errors' do + pp = <<-EOS + class { 'apache': } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe package(package_name) do + it { should be_installed } + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + end + + context 'custom site/mod dir parameters' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + file { '/apache': ensure => directory, } + class { 'apache': + mod_dir => '/apache/mods', + vhost_dir => '/apache/vhosts', + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + end +end diff --git a/apache/spec/acceptance/default_mods_spec.rb b/apache/spec/acceptance/default_mods_spec.rb new file mode 100644 index 000000000..df6463eca --- /dev/null +++ b/apache/spec/acceptance/default_mods_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper_acceptance' + +case fact('osfamily') +when 'RedHat' + servicename = 'httpd' +when 'Debian' + servicename = 'apache2' +when 'FreeBSD' + servicename = 'apache22' +else + raise "Unconfigured OS for apache service on #{fact('osfamily')}" +end + +describe 'apache::default_mods class' do + describe 'no default mods' do + # Using puppet_apply as a helper + it 'should apply with no errors' do + pp = <<-EOS + class { 'apache': + default_mods => false, + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe service(servicename) do + it { should be_running } + end + end + + describe 'no default mods and failing' do + # Using puppet_apply as a helper + it 'should apply with errors' do + pp = <<-EOS + class { 'apache': + default_mods => false, + } + apache::vhost { 'defaults.example.com': + docroot => '/var/www/defaults', + aliases => { + alias => '/css', + path => '/var/www/css', + }, + setenv => 'TEST1 one', + } + EOS + + apply_manifest(pp, { :expect_failures => true }) + end + + # Are these the same? + describe service(servicename) do + it { should_not be_running } + end + describe "service #{servicename}" do + it 'should not be running' do + shell("pidof #{servicename}", {:acceptable_exit_codes => 1}) + end + end + end + + describe 'alternative default mods' do + # Using puppet_apply as a helper + it 'should apply with no errors' do + pp = <<-EOS + class { 'apache': + default_mods => [ + 'info', + 'alias', + 'mime', + 'env', + 'expires', + ], + } + apache::vhost { 'defaults.example.com': + docroot => '/var/www/defaults', + aliases => { + alias => '/css', + path => '/var/www/css', + }, + setenv => 'TEST1 one', + } + EOS + + apply_manifest(pp, :catch_failures => true) + shell('sleep 10') + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + describe service(servicename) do + it { should be_running } + end + end +end diff --git a/apache/spec/acceptance/itk_spec.rb b/apache/spec/acceptance/itk_spec.rb new file mode 100644 index 000000000..cabd71c5c --- /dev/null +++ b/apache/spec/acceptance/itk_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper_acceptance' + +case fact('osfamily') +when 'Debian' + service_name = 'apache2' +when 'FreeBSD' + service_name = 'apache22' +else + # Not implemented yet + service_name = :skip +end + +unless service_name.equal? :skip + describe 'apache::mod::itk class' do + describe 'running puppet code' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'apache': + mpm_module => 'itk', + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + end + + describe service(service_name) do + it { should be_running } + it { should be_enabled } + end + end +end diff --git a/apache/spec/acceptance/mod_php_spec.rb b/apache/spec/acceptance/mod_php_spec.rb new file mode 100644 index 000000000..3f3faaf9f --- /dev/null +++ b/apache/spec/acceptance/mod_php_spec.rb @@ -0,0 +1,95 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::php class' do + case fact('osfamily') + when 'Debian' + vhost_dir = '/etc/apache2/sites-enabled' + mod_dir = '/etc/apache2/mods-available' + service_name = 'apache2' + when 'RedHat' + vhost_dir = '/etc/httpd/conf.d' + mod_dir = '/etc/httpd/conf.d' + service_name = 'httpd' + when 'FreeBSD' + vhost_dir = '/usr/local/etc/apache22/Vhosts' + mod_dir = '/usr/local/etc/apache22/Modules' + service_name = 'apache22' + end + + context "default php config" do + it 'succeeds in puppeting php' do + pp= <<-EOS + class { 'apache': + mpm_module => 'prefork', + } + class { 'apache::mod::php': } + apache::vhost { 'php.example.com': + port => '80', + docroot => '/var/www/php', + } + host { 'php.example.com': ip => '127.0.0.1', } + file { '/var/www/php/index.php': + ensure => file, + content => "\\n", + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + + describe file("#{mod_dir}/php5.conf") do + it { should contain "DirectoryIndex index.php" } + end + + it 'should answer to php.example.com' do + shell("/usr/bin/curl php.example.com:80") do |r| + r.stdout.should =~ /PHP Version/ + r.exit_code.should == 0 + end + end + end + + context "custom php admin_flag and php_admin_value" do + it 'succeeds in puppeting php' do + pp= <<-EOS + class { 'apache': + mpm_module => 'prefork', + } + class { 'apache::mod::php': } + apache::vhost { 'php.example.com': + port => '80', + docroot => '/var/www/php', + php_admin_values => { 'open_basedir' => '/var/www/php/:/usr/share/pear/', }, + php_admin_flags => { 'engine' => 'on', }, + } + host { 'php.example.com': ip => '127.0.0.1', } + file { '/var/www/php/index.php': + ensure => file, + content => "\\n", + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + + describe file("#{vhost_dir}/25-php.example.com.conf") do + it { should contain " php_admin_flag engine on" } + it { should contain " php_admin_value open_basedir /var/www/php/:/usr/share/pear/" } + end + + it 'should answer to php.example.com' do + shell("/usr/bin/curl php.example.com:80") do |r| + r.stdout.should =~ /\/usr\/share\/pear\// + r.exit_code.should == 0 + end + end + end +end diff --git a/apache/spec/acceptance/mod_suphp_spec.rb b/apache/spec/acceptance/mod_suphp_spec.rb new file mode 100644 index 000000000..725ec2adb --- /dev/null +++ b/apache/spec/acceptance/mod_suphp_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper_acceptance' + +describe 'apache::mod::suphp class' do + case fact('osfamily') + when 'Debian' + context "default suphp config" do + it 'succeeds in puppeting suphp' do + pp = <<-EOS + class { 'apache': + mpm_module => 'prefork', + } + class { 'apache::mod::php': } + class { 'apache::mod::suphp': } + apache::vhost { 'suphp.example.com': + port => '80', + docroot => '/var/www/suphp', + } + host { 'suphp.example.com': ip => '127.0.0.1', } + file { '/var/www/suphp/index.php': + ensure => file, + owner => 'daemon', + group => 'daemon', + content => "\\n", + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service('apache2') do + it { should be_enabled } + it { should be_running } + end + + it 'should answer to suphp.example.com' do + shell("/usr/bin/curl suphp.example.com:80") do |r| + r.stdout.should =~ /^daemon$/ + r.exit_code.should == 0 + end + end + end + when 'RedHat' + # Not implemented yet + end +end diff --git a/apache/spec/acceptance/nodesets/centos-59-x64.yml b/apache/spec/acceptance/nodesets/centos-59-x64.yml new file mode 100644 index 000000000..cde1fe5a8 --- /dev/null +++ b/apache/spec/acceptance/nodesets/centos-59-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + centos-59-x64: + roles: + - master + platform: el-5-x86_64 + box : centos-59-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/centos-64-x64-pe.yml b/apache/spec/acceptance/nodesets/centos-64-x64-pe.yml new file mode 100644 index 000000000..e408d1be7 --- /dev/null +++ b/apache/spec/acceptance/nodesets/centos-64-x64-pe.yml @@ -0,0 +1,13 @@ +HOSTS: + centos-64-x64: + roles: + - master + - database + - dashboard + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: pe diff --git a/apache/spec/acceptance/nodesets/centos-64-x64.yml b/apache/spec/acceptance/nodesets/centos-64-x64.yml new file mode 100644 index 000000000..ce47212a8 --- /dev/null +++ b/apache/spec/acceptance/nodesets/centos-64-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + centos-64-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/debian-607-x64.yml b/apache/spec/acceptance/nodesets/debian-607-x64.yml new file mode 100644 index 000000000..e642e0992 --- /dev/null +++ b/apache/spec/acceptance/nodesets/debian-607-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + debian-607-x64: + roles: + - master + platform: debian-6-amd64 + box : debian-607-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-607-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/debian-70rc1-x64.yml b/apache/spec/acceptance/nodesets/debian-70rc1-x64.yml new file mode 100644 index 000000000..cbbbfb2cc --- /dev/null +++ b/apache/spec/acceptance/nodesets/debian-70rc1-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + debian-70rc1-x64: + roles: + - master + platform: debian-7-amd64 + box : debian-70rc1-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/default.yml b/apache/spec/acceptance/nodesets/default.yml new file mode 120000 index 000000000..2719644a6 --- /dev/null +++ b/apache/spec/acceptance/nodesets/default.yml @@ -0,0 +1 @@ +centos-64-x64.yml \ No newline at end of file diff --git a/apache/spec/acceptance/nodesets/fedora-18-x64.yml b/apache/spec/acceptance/nodesets/fedora-18-x64.yml new file mode 100644 index 000000000..086cae995 --- /dev/null +++ b/apache/spec/acceptance/nodesets/fedora-18-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + fedora-18-x64: + roles: + - master + platform: fedora-18-x86_64 + box : fedora-18-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/fedora-18-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/sles-11sp1-x64.yml b/apache/spec/acceptance/nodesets/sles-11sp1-x64.yml new file mode 100644 index 000000000..a9f01d5f4 --- /dev/null +++ b/apache/spec/acceptance/nodesets/sles-11sp1-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + sles-11sp1-x64: + roles: + - master + platform: sles-11-x86_64 + box : sles-11sp1-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/sles-11sp1-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml b/apache/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml new file mode 100644 index 000000000..c1b8bdf8f --- /dev/null +++ b/apache/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + ubuntu-server-10044-x64: + roles: + - master + platform: ubuntu-10.04-amd64 + box : ubuntu-server-10044-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml b/apache/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml new file mode 100644 index 000000000..f7df2ccce --- /dev/null +++ b/apache/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml @@ -0,0 +1,11 @@ +HOSTS: + ubuntu-server-12042-x64: + roles: + - master + platform: ubuntu-12.04-amd64 + box : ubuntu-server-12042-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + log_level: debug + type: git diff --git a/apache/spec/acceptance/prefork_worker_spec.rb b/apache/spec/acceptance/prefork_worker_spec.rb new file mode 100644 index 000000000..8427fd045 --- /dev/null +++ b/apache/spec/acceptance/prefork_worker_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper_acceptance' + +case fact('osfamily') +when 'RedHat' + servicename = 'httpd' +when 'Debian' + servicename = 'apache2' +when 'FreeBSD' + servicename = 'apache22' +else + raise "Unconfigured OS for apache service on #{fact('osfamily')}" +end + +case fact('osfamily') +when 'FreeBSD' + describe 'apache::mod::event class' do + describe 'running puppet code' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'apache': + mpm_module => 'event', + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + end + + describe service(servicename) do + it { should be_running } + it { should be_enabled } + end + end +end + +describe 'apache::mod::worker class' do + describe 'running puppet code' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'apache': + mpm_module => 'worker', + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + end + + describe service(servicename) do + it { should be_running } + it { should be_enabled } + end +end + +describe 'apache::mod::prefork class' do + describe 'running puppet code' do + # Using puppet_apply as a helper + it 'should work with no errors' do + pp = <<-EOS + class { 'apache': + mpm_module => 'prefork', + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + end + + describe service(servicename) do + it { should be_running } + it { should be_enabled } + end +end diff --git a/apache/spec/acceptance/service_spec.rb b/apache/spec/acceptance/service_spec.rb new file mode 100644 index 000000000..c3124c846 --- /dev/null +++ b/apache/spec/acceptance/service_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper_acceptance' + +describe 'apache::service class' do + describe 'adding dependencies in between the base class and service class' do + it 'should work with no errors' do + pp = <<-EOS + class { 'apache': } + file { '/tmp/test': + require => Class['apache'], + notify => Class['apache::service'], + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + end +end diff --git a/apache/spec/acceptance/vhost_spec.rb b/apache/spec/acceptance/vhost_spec.rb new file mode 100644 index 000000000..9fc24ff17 --- /dev/null +++ b/apache/spec/acceptance/vhost_spec.rb @@ -0,0 +1,974 @@ +require 'spec_helper_acceptance' + +describe 'apache::vhost define' do + case fact('osfamily') + when 'RedHat' + vhost_dir = '/etc/httpd/conf.d' + package_name = 'httpd' + service_name = 'httpd' + ports_file = '/etc/httpd/conf/ports.conf' + suphp_handler = 'php5-script' + suphp_configpath = 'undef' + when 'FreeBSD' + vhost_dir = '/usr/local/etc/apache22/Vhosts' + package_name = 'apache22' + service_name = 'apache22' + ports_file = '/usr/local/etc/apache22/ports.conf' + when 'Debian' + vhost_dir = '/etc/apache2/sites-enabled' + package_name = 'apache2' + service_name = 'apache2' + ports_file = '/etc/apache2/ports.conf' + suphp_handler = 'x-httpd-php' + suphp_configpath = '/etc/php5/apache2' + end + + context 'no default vhosts' do + it 'should create no default vhosts' do + pp = <<-EOS + class { 'apache': + default_vhost => false, + default_ssl_vhost => false, + service_ensure => stopped + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/15-default.conf") do + it { should_not be_file } + end + + describe file("#{vhost_dir}/15-default-ssl.conf") do + it { should_not be_file } + end + end + + context "default vhost without ssl" do + it 'should create a default vhost config' do + pp = <<-EOS + class { 'apache': } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/15-default.conf") do + it { should contain '' } + end + + describe file("#{vhost_dir}/15-default-ssl.conf") do + it { should_not be_file } + end + end + + context 'default vhost with ssl' do + it 'should create default vhost configs' do + # Doesn't work on Ubuntu 10.04 because ssl.conf should contain + # 'file:/var/run/apache2/ssl_mutex' but contains + # 'file:${APACHE_RUN_DIR}/ssl_mutex' + pp = <<-EOS + class { 'apache': + default_ssl_vhost => true, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/15-default.conf") do + it { should contain '' } + end + + describe file("#{vhost_dir}/15-default-ssl.conf") do + it { should contain '' } + it { should contain "SSLEngine on" } + end + end + + context 'new vhost on port 80' do + it 'should configure an apache vhost' do + pp = <<-EOS + class { 'apache': } + apache::vhost { 'first.example.com': + port => '80', + docroot => '/var/www/first', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-first.example.com.conf") do + it { should contain '' } + it { should contain "ServerName first.example.com" } + end + end + + context 'new proxy vhost on port 80' do + it 'should configure an apache proxy vhost' do + pp = <<-EOS + class { 'apache': } + apache::vhost { 'proxy.example.com': + port => '80', + docroot => '/var/www/proxy', + proxy_pass => [ + { 'path' => '/foo', 'url' => 'http://backend-foo/'}, + ], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-proxy.example.com.conf") do + it { should contain '' } + it { should contain "ServerName proxy.example.com" } + it { should contain "ProxyPass" } + it { should_not contain "" } + end + end + + context 'new vhost on port 80' do + it 'should configure two apache vhosts' do + pp = <<-EOS + class { 'apache': } + apache::vhost { 'first.example.com': + port => '80', + docroot => '/var/www/first', + } + host { 'first.example.com': ip => '127.0.0.1', } + file { '/var/www/first/index.html': + ensure => file, + content => "Hello from first\\n", + } + apache::vhost { 'second.example.com': + port => '80', + docroot => '/var/www/second', + } + host { 'second.example.com': ip => '127.0.0.1', } + file { '/var/www/second/index.html': + ensure => file, + content => "Hello from second\\n", + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + + it 'should answer to first.example.com' do + shell("/usr/bin/curl first.example.com:80", {:acceptable_exit_codes => 0}) do |r| + r.stdout.should == "Hello from first\n" + end + end + + it 'should answer to second.example.com' do + shell("/usr/bin/curl second.example.com:80", {:acceptable_exit_codes => 0}) do |r| + r.stdout.should == "Hello from second\n" + end + end + end + + context 'apache_directories' do + describe 'readme example, adapted' do + it 'should configure a vhost with Files' do + pp = <<-EOS + class { 'apache': } + apache::vhost { 'files.example.net': + docroot => '/var/www/files', + directories => [ + { 'path' => '/var/www/files', }, + { 'path' => '~ "(\.swp|\.bak|~)$"', 'provider' => 'files', 'deny' => 'from all' }, + ], + } + file { '/var/www/files/index.html': + ensure => file, + content => "Hello World\\n", + } + file { '/var/www/files/index.html.bak': + ensure => file, + content => "Hello World\\n", + } + host { 'files.example.net': ip => '127.0.0.1', } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + + it 'should answer to files.example.net' do + shell("/usr/bin/curl -sSf files.example.net:80/index.html").stdout.should eq("Hello World\n") + shell("/usr/bin/curl -sSf files.example.net:80/index.html.bak", {:acceptable_exit_codes => 22}).stderr.should match(/curl: \(22\) The requested URL returned error: 403/) + end + end + + describe 'other Directory options' do + it 'should configure a vhost with multiple Directory sections' do + pp = <<-EOS + class { 'apache': } + apache::vhost { 'files.example.net': + docroot => '/var/www/files', + directories => [ + { 'path' => '/var/www/files', }, + { + 'provider' => 'location', + 'path' => '/foo/', + 'directoryindex' => 'notindex.html', + }, + { + 'provider' => 'files', + 'path' => '~ "private.html$"', + 'deny' => 'from all', + }, + ], + } + file { '/var/www/files/foo': + ensure => directory, + } + file { '/var/www/files/foo/notindex.html': + ensure => file, + content => "Hello Foo\\n", + } + file { '/var/www/files/private.html': + ensure => file, + content => "Hello World\\n", + } + host { 'files.example.net': ip => '127.0.0.1', } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + + it 'should answer to files.example.net' do + shell("/usr/bin/curl -sSf files.example.net:80/").stdout.should eq("Hello World\n") + shell("/usr/bin/curl -sSf files.example.net:80/foo/").stdout.should eq("Hello Foo\n") + shell("/usr/bin/curl -sSf files.example.net:80/private.html", {:acceptable_exit_codes => 22}).stderr.should match(/curl: \(22\) The requested URL returned error: 403/) + end + end + end + + case fact('lsbdistcodename') + when 'precise', 'wheezy' + context 'vhost fallbackresouce example' do + it 'should configure a vhost with Fallbackresource' do + pp = <<-EOS + class { 'apache': } + apache::vhost { 'fallback.example.net': + docroot => '/var/www/fallback', + fallbackresource => '/index.html' + } + file { '/var/www/fallback/index.html': + ensure => file, + content => "Hello World\\n", + } + host { 'fallback.example.net': ip => '127.0.0.1', } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + + it 'should answer to fallback.example.net' do + shell("/usr/bin/curl fallback.example.net:80/Does/Not/Exist") do |r| + r.stdout.should == "Hello World\n" + end + end + + end + else + # The current stable RHEL release (6.4) comes with Apache httpd 2.2.15 + # That was released March 6, 2010. + # FallbackResource was backported to 2.2.16, and released July 25, 2010. + # Ubuntu Lucid (10.04) comes with apache2 2.2.14, released October 3, 2009. + # https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x/STATUS + end + + context 'virtual_docroot hosting separate sites' do + it 'should configure a vhost with VirtualDocumentRoot' do + pp = <<-EOS + class { 'apache': } + apache::vhost { 'virt.example.com': + vhost_name => '*', + serveraliases => '*virt.example.com', + port => '80', + docroot => '/var/www/virt', + virtual_docroot => '/var/www/virt/%1', + } + host { 'virt.example.com': ip => '127.0.0.1', } + host { 'a.virt.example.com': ip => '127.0.0.1', } + host { 'b.virt.example.com': ip => '127.0.0.1', } + file { [ '/var/www/virt/a', '/var/www/virt/b', ]: ensure => directory, } + file { '/var/www/virt/a/index.html': ensure => file, content => "Hello from a.virt\\n", } + file { '/var/www/virt/b/index.html': ensure => file, content => "Hello from b.virt\\n", } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + + it 'should answer to a.virt.example.com' do + shell("/usr/bin/curl a.virt.example.com:80", {:acceptable_exit_codes => 0}) do |r| + r.stdout.should == "Hello from a.virt\n" + end + end + + it 'should answer to b.virt.example.com' do + shell("/usr/bin/curl b.virt.example.com:80", {:acceptable_exit_codes => 0}) do |r| + r.stdout.should == "Hello from b.virt\n" + end + end + end + + context 'proxy_pass for alternative vhost' do + it 'should configure a local vhost and a proxy vhost' do + apply_manifest(%{ + class { 'apache': default_vhost => false, } + apache::vhost { 'localhost': + docroot => '/var/www/local', + ip => '127.0.0.1', + port => '8888', + } + apache::listen { '*:80': } + apache::vhost { 'proxy.example.com': + docroot => '/var/www', + port => '80', + add_listen => false, + proxy_pass => { + 'path' => '/', + 'url' => 'http://localhost:8888/subdir/', + }, + } + host { 'proxy.example.com': ip => '127.0.0.1', } + file { ['/var/www/local', '/var/www/local/subdir']: ensure => directory, } + file { '/var/www/local/subdir/index.html': + ensure => file, + content => "Hello from localhost\\n", + } + }, :catch_failures => true) + end + + describe service(service_name) do + it { should be_enabled } + it { should be_running } + end + + it 'should get a response from the back end' do + shell("/usr/bin/curl --max-redirs 0 proxy.example.com:80") do |r| + r.stdout.should == "Hello from localhost\n" + r.exit_code.should == 0 + end + end + end + + describe 'ip_based' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + ip_based => true, + servername => 'test.server', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file(ports_file) do + it { should be_file } + it { should_not contain 'NameVirtualHost test.server' } + end + end + + describe 'add_listen' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': default_vhost => false } + host { 'testlisten.server': ip => '127.0.0.1' } + apache::listen { '81': } + apache::vhost { 'testlisten.server': + docroot => '/tmp', + port => '80', + add_listen => false, + servername => 'testlisten.server', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file(ports_file) do + it { should be_file } + it { should_not contain 'Listen 80' } + it { should contain 'Listen 81' } + end + end + + describe 'docroot' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp/test', + docroot_owner => 'vagrant', + docroot_group => 'vagrant', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file('/tmp/test') do + it { should be_directory } + it { should be_owned_by 'vagrant' } + it { should be_grouped_into 'vagrant' } + end + end + + describe 'default_vhost' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + default_vhost => true, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/10-test.server.conf") do + it { should be_file } + end + end + + describe 'options' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + options => ['Indexes','FollowSymLinks', 'ExecCGI'], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'Options Indexes FollowSymLinks ExecCGI' } + end + end + + describe 'override' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + override => ['All'], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'AllowOverride All' } + end + end + + describe 'logroot' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + logroot => '/tmp', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'CustomLog /tmp' } + end + end + + ['access', 'error'].each do |logtype| + case logtype + when 'access' + logname = 'CustomLog' + when 'error' + logname = 'ErrorLog' + end + + describe "#{logtype}_log" do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + logroot => '/tmp', + #{logtype}_log => false, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should_not contain "#{logname} /tmp" } + end + end + + describe "#{logtype}_log_pipe" do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + logroot => '/tmp', + #{logtype}_log_pipe => '|test', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain "#{logname} \"|test\"" } + end + end + + describe "#{logtype}_log_syslog" do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + logroot => '/tmp', + #{logtype}_log_syslog => 'syslog', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain "#{logname} syslog" } + end + end + end + + describe 'access_log_format' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + logroot => '/tmp', + access_log_syslog => 'syslog', + access_log_format => '%h %l', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'CustomLog syslog "%h %l"' } + end + end + + describe 'access_log_env_var' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + logroot => '/tmp', + access_log_syslog => 'syslog', + access_log_env_var => 'admin', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'CustomLog syslog combined env=admin' } + end + end + + describe 'aliases' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + aliases => [{ alias => '/image', path => '/ftp/pub/image' }], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'Alias /image /ftp/pub/image' } + end + end + + describe 'scriptaliases' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + scriptaliases => [{ alias => '/myscript', path => '/usr/share/myscript', }], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'ScriptAlias /myscript /usr/share/myscript' } + end + end + + describe 'proxy' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': service_ensure => stopped, } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + proxy_dest => 'test2', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'ProxyPass / test2/' } + end + end + + describe 'suphp' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': service_ensure => stopped, } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + suphp_addhandler => '#{suphp_handler}', + suphp_engine => 'on', + suphp_configpath => '#{suphp_configpath}', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain "suPHP_AddHandler #{suphp_handler}" } + it { should contain 'suPHP_Engine on' } + it { should contain "suPHP_ConfigPath #{suphp_configpath}" } + end + end + + describe 'no_proxy_uris' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': service_ensure => stopped, } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + proxy_dest => 'http://test2', + no_proxy_uris => [ 'http://test2/test' ], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'ProxyPass / http://test2/' } + it { should contain 'ProxyPass http://test2/test !' } + end + end + + describe 'redirect' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + redirect_source => ['/images'], + redirect_dest => ['http://test.server/'], + redirect_status => ['permanent'], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'Redirect permanent /images http://test.server/' } + end + end + + # Passenger isn't even in EPEL on el-5 + if default['platform'] !~ /^el-5/ + describe 'rack_base_uris' do + if fact('osfamily') == 'RedHat' + it 'adds epel' do + pp = "class { 'epel': }" + apply_manifest(pp, :catch_failures => true) + end + end + + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + rack_base_uris => ['/test'], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'RackBaseURI /test' } + end + end + end + + + describe 'request_headers' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + request_headers => ['append MirrorID "mirror 12"'], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'append MirrorID "mirror 12"' } + end + end + + describe 'rewrite rules' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + rewrites => [ + { comment => 'test', + rewrite_cond => '%{HTTP_USER_AGENT} ^Lynx/ [OR]', + rewrite_rule => ['^index\.html$ welcome.html'], + } + ], + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain '#test' } + it { should contain 'RewriteCond %{HTTP_USER_AGENT} ^Lynx/ [OR]' } + it { should contain 'RewriteRule ^index.html$ welcome.html' } + end + end + + describe 'setenv/setenvif' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + setenv => ['TEST /test'], + setenvif => ['Request_URI "\.gif$" object_is_image=gif'] + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'SetEnv TEST /test' } + it { should contain 'SetEnvIf Request_URI "\.gif$" object_is_image=gif' } + end + end + + describe 'block' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + block => 'scm', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain '' } + end + end + + describe 'wsgi' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + class { 'apache::mod::wsgi': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + wsgi_daemon_process => 'wsgi', + wsgi_daemon_process_options => {processes => '2'}, + wsgi_process_group => 'vagrant', + wsgi_script_aliases => { '/test' => '/test1' }, + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'WSGIDaemonProcess wsgi processes=2' } + it { should contain 'WSGIProcessGroup vagrant' } + it { should contain 'WSGIScriptAlias /test /test1' } + end + end + + describe 'custom_fragment' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + custom_fragment => inline_template('#weird test string'), + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain '#weird test string' } + end + end + + describe 'itk' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + itk => { user => 'vagrant', group => 'vagrant' } + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'AssignUserId vagrant vagrant' } + end + end + + # So what does this work on? + if default['platform'] !~ /^(debian-(6|7)|el-(5|6))/ + describe 'fastcgi' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + class { 'apache::mod::fastcgi': } + host { 'test.server': ip => '127.0.0.1' } + apache::vhost { 'test.server': + docroot => '/tmp', + fastcgi_server => 'localhost', + fastcgi_socket => '/tmp/fast/1234', + fastcgi_dir => '/tmp/fast', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'FastCgiExternalServer localhost -socket /tmp/fast/1234' } + it { should contain '' } + end + end + end + + describe 'additional_includes' do + it 'applies cleanly' do + pp = <<-EOS + class { 'apache': } + host { 'test.server': ip => '127.0.0.1' } + file { '/tmp/include': ensure => present, content => '#additional_includes' } + apache::vhost { 'test.server': + docroot => '/tmp', + additional_includes => '/tmp/include', + } + EOS + apply_manifest(pp, :catch_failures => true) + end + + describe file("#{vhost_dir}/25-test.server.conf") do + it { should be_file } + it { should contain 'Include /tmp/include' } + end + end + +end diff --git a/apache/spec/classes/apache_spec.rb b/apache/spec/classes/apache_spec.rb new file mode 100644 index 000000000..7dd82d351 --- /dev/null +++ b/apache/spec/classes/apache_spec.rb @@ -0,0 +1,464 @@ +require 'spec_helper' + +describe 'apache', :type => :class do + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_package("httpd").with( + 'notify' => 'Class[Apache::Service]', + 'ensure' => 'installed' + ) + } + it { should contain_user("www-data") } + it { should contain_group("www-data") } + it { should contain_class("apache::service") } + it { should contain_file("/etc/apache2/sites-enabled").with( + 'ensure' => 'directory', + 'recurse' => 'true', + 'purge' => 'true', + 'notify' => 'Class[Apache::Service]', + 'require' => 'Package[httpd]' + ) + } + it { should contain_file("/etc/apache2/mods-enabled").with( + 'ensure' => 'directory', + 'recurse' => 'true', + 'purge' => 'true', + 'notify' => 'Class[Apache::Service]', + 'require' => 'Package[httpd]' + ) + } + it { should contain_file("/etc/apache2/mods-available").with( + 'ensure' => 'directory', + 'recurse' => 'true', + 'purge' => 'false', + 'notify' => 'Class[Apache::Service]', + 'require' => 'Package[httpd]' + ) + } + it { should contain_concat("/etc/apache2/ports.conf").with( + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + 'notify' => 'Class[Apache::Service]' + ) + } + # Assert that load files are placed and symlinked for these mods, but no conf file. + [ + 'auth_basic', + 'authn_file', + 'authz_default', + 'authz_groupfile', + 'authz_host', + 'authz_user', + 'dav', + 'env' + ].each do |modname| + it { should contain_file("#{modname}.load").with( + 'path' => "/etc/apache2/mods-available/#{modname}.load", + 'ensure' => 'file' + ) } + it { should contain_file("#{modname}.load symlink").with( + 'path' => "/etc/apache2/mods-enabled/#{modname}.load", + 'ensure' => 'link', + 'target' => "/etc/apache2/mods-available/#{modname}.load" + ) } + it { should_not contain_file("#{modname}.conf") } + it { should_not contain_file("#{modname}.conf symlink") } + end + + # Assert that both load files and conf files are placed and symlinked for these mods + [ + 'alias', + 'autoindex', + 'dav_fs', + 'deflate', + 'dir', + 'mime', + 'negotiation', + 'setenvif', + ].each do |modname| + it { should contain_file("#{modname}.load").with( + 'path' => "/etc/apache2/mods-available/#{modname}.load", + 'ensure' => 'file' + ) } + it { should contain_file("#{modname}.load symlink").with( + 'path' => "/etc/apache2/mods-enabled/#{modname}.load", + 'ensure' => 'link', + 'target' => "/etc/apache2/mods-available/#{modname}.load" + ) } + it { should contain_file("#{modname}.conf").with( + 'path' => "/etc/apache2/mods-available/#{modname}.conf", + 'ensure' => 'file' + ) } + it { should contain_file("#{modname}.conf symlink").with( + 'path' => "/etc/apache2/mods-enabled/#{modname}.conf", + 'ensure' => 'link', + 'target' => "/etc/apache2/mods-available/#{modname}.conf" + ) } + end + describe "Don't create user resource" do + context "when parameter manage_user is false" do + let :params do + { :manage_user => false } + end + + it { should_not contain_user('www-data') } + it { should contain_file("/etc/apache2/apache2.conf").with_content %r{^User www-data\n} } + end + end + describe "Don't create group resource" do + context "when parameter manage_group is false" do + let :params do + { :manage_group => false } + end + + it { should_not contain_group('www-data') } + it { should contain_file("/etc/apache2/apache2.conf").with_content %r{^Group www-data\n} } + end + end + end + context "on a RedHat 5 OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '5', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_package("httpd").with( + 'notify' => 'Class[Apache::Service]', + 'ensure' => 'installed' + ) + } + it { should contain_user("apache") } + it { should contain_group("apache") } + it { should contain_class("apache::service") } + it { should contain_file("/etc/httpd/conf.d").with( + 'ensure' => 'directory', + 'recurse' => 'true', + 'purge' => 'true', + 'notify' => 'Class[Apache::Service]', + 'require' => 'Package[httpd]' + ) + } + it { should contain_concat("/etc/httpd/conf/ports.conf").with( + 'owner' => 'root', + 'group' => 'root', + 'mode' => '0644', + 'notify' => 'Class[Apache::Service]' + ) + } + describe "Alternate confd/mod/vhosts directory" do + let :params do + { + :vhost_dir => '/etc/httpd/site.d', + :confd_dir => '/etc/httpd/conf.d', + :mod_dir => '/etc/httpd/mod.d', + } + end + + ['mod.d','site.d','conf.d'].each do |dir| + it { should contain_file("/etc/httpd/#{dir}").with( + 'ensure' => 'directory', + 'recurse' => 'true', + 'purge' => 'true', + 'notify' => 'Class[Apache::Service]', + 'require' => 'Package[httpd]' + ) } + end + + # Assert that load files are placed for these mods, but no conf file. + [ + 'auth_basic', + 'authn_file', + 'authz_default', + 'authz_groupfile', + 'authz_host', + 'authz_user', + 'dav', + 'env', + ].each do |modname| + it { should contain_file("#{modname}.load").with_path( + "/etc/httpd/mod.d/#{modname}.load" + ) } + it { should_not contain_file("#{modname}.conf").with_path( + "/etc/httpd/mod.d/#{modname}.conf" + ) } + end + + # Assert that both load files and conf files are placed for these mods + [ + 'alias', + 'autoindex', + 'dav_fs', + 'deflate', + 'dir', + 'mime', + 'negotiation', + 'setenvif', + ].each do |modname| + it { should contain_file("#{modname}.load").with_path( + "/etc/httpd/mod.d/#{modname}.load" + ) } + it { should contain_file("#{modname}.conf").with_path( + "/etc/httpd/mod.d/#{modname}.conf" + ) } + end + + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include /etc/httpd/conf\.d/\*\.conf$} } + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include /etc/httpd/site\.d/\*\.conf$} } + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include /etc/httpd/mod\.d/\*\.conf$} } + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Include /etc/httpd/mod\.d/\*\.load$} } + end + + describe "Alternate conf.d directory" do + let :params do + { :confd_dir => '/etc/httpd/special_conf.d' } + end + + it { should contain_file("/etc/httpd/special_conf.d").with( + 'ensure' => 'directory', + 'recurse' => 'true', + 'purge' => 'true', + 'notify' => 'Class[Apache::Service]', + 'require' => 'Package[httpd]' + ) } + end + + describe "Alternate mpm_modules" do + context "when declaring mpm_module is false" do + let :params do + { :mpm_module => false } + end + it 'should not declare mpm modules' do + should_not contain_class('apache::mod::event') + should_not contain_class('apache::mod::itk') + should_not contain_class('apache::mod::peruser') + should_not contain_class('apache::mod::prefork') + should_not contain_class('apache::mod::worker') + end + end + context "when declaring mpm_module => prefork" do + let :params do + { :mpm_module => 'prefork' } + end + it { should contain_class('apache::mod::prefork') } + it { should_not contain_class('apache::mod::event') } + it { should_not contain_class('apache::mod::itk') } + it { should_not contain_class('apache::mod::peruser') } + it { should_not contain_class('apache::mod::worker') } + end + context "when declaring mpm_module => worker" do + let :params do + { :mpm_module => 'worker' } + end + it { should contain_class('apache::mod::worker') } + it { should_not contain_class('apache::mod::event') } + it { should_not contain_class('apache::mod::itk') } + it { should_not contain_class('apache::mod::peruser') } + it { should_not contain_class('apache::mod::prefork') } + end + context "when declaring mpm_module => breakme" do + let :params do + { :mpm_module => 'breakme' } + end + it { expect { subject }.to raise_error Puppet::Error, /does not match/ } + end + end + + describe "different templates for httpd.conf" do + context "with default" do + let :params do + { :conf_template => 'apache/httpd.conf.erb' } + end + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^# Security\n} } + end + context "with non-default" do + let :params do + { :conf_template => 'site_apache/fake.conf.erb' } + end + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Fake template for rspec.$} } + end + end + + describe "default mods" do + context "without" do + let :params do + { :default_mods => false } + end + + it { should contain_apache__mod('authz_host') } + it { should_not contain_apache__mod('env') } + end + context "custom" do + let :params do + { :default_mods => [ + 'info', + 'alias', + 'mime', + 'env', + 'setenv', + 'expires', + ]} + end + + it { should contain_apache__mod('authz_host') } + it { should contain_apache__mod('env') } + it { should contain_class('apache::mod::info') } + it { should contain_class('apache::mod::mime') } + end + end + describe "Don't create user resource" do + context "when parameter manage_user is false" do + let :params do + { :manage_user => false } + end + + it { should_not contain_user('apache') } + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^User apache\n} } + end + end + describe "Don't create group resource" do + context "when parameter manage_group is false" do + let :params do + { :manage_group => false } + end + + it { should_not contain_group('apache') } + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^Group apache\n} } + + end + end + describe "sendfile" do + context "with invalid value" do + let :params do + { :sendfile => 'foo' } + end + it "should fail" do + expect do + subject + end.to raise_error(Puppet::Error, /"foo" does not match/) + end + end + context "On" do + let :params do + { :sendfile => 'On' } + end + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^EnableSendfile On\n} } + end + context "Off" do + let :params do + { :sendfile => 'Off' } + end + it { should contain_file("/etc/httpd/conf/httpd.conf").with_content %r{^EnableSendfile Off\n} } + end + end + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_class("apache::package").with({'ensure' => 'present'}) } + it { should contain_user("www") } + it { should contain_group("www") } + it { should contain_class("apache::service") } + it { should contain_file("/usr/local/etc/apache22/Vhosts").with( + 'ensure' => 'directory', + 'recurse' => 'true', + 'purge' => 'true', + 'notify' => 'Class[Apache::Service]', + 'require' => 'Package[httpd]' + ) } + it { should contain_file("/usr/local/etc/apache22/Modules").with( + 'ensure' => 'directory', + 'recurse' => 'true', + 'purge' => 'true', + 'notify' => 'Class[Apache::Service]', + 'require' => 'Package[httpd]' + ) } + it { should contain_concat("/usr/local/etc/apache22/ports.conf").with( + 'owner' => 'root', + 'group' => 'wheel', + 'mode' => '0644', + 'notify' => 'Class[Apache::Service]' + ) } + # Assert that load files are placed for these mods, but no conf file. + [ + 'auth_basic', + 'authn_file', + 'authz_default', + 'authz_groupfile', + 'authz_host', + 'authz_user', + 'dav', + 'env' + ].each do |modname| + it { should contain_file("#{modname}.load").with( + 'path' => "/usr/local/etc/apache22/Modules/#{modname}.load", + 'ensure' => 'file' + ) } + it { should_not contain_file("#{modname}.conf") } + end + + # Assert that both load files and conf files are placed for these mods + [ + 'alias', + 'autoindex', + 'dav_fs', + 'deflate', + 'dir', + 'mime', + 'negotiation', + 'setenvif', + ].each do |modname| + it { should contain_file("#{modname}.load").with( + 'path' => "/usr/local/etc/apache22/Modules/#{modname}.load", + 'ensure' => 'file' + ) } + it { should contain_file("#{modname}.conf").with( + 'path' => "/usr/local/etc/apache22/Modules/#{modname}.conf", + 'ensure' => 'file' + ) } + end + end + context 'on all OSes' do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + context 'default vhost defaults' do + it { should contain_apache__vhost('default').with_ensure('present') } + it { should contain_apache__vhost('default-ssl').with_ensure('absent') } + end + context 'without default non-ssl vhost' do + let :params do { + :default_vhost => false + } + end + it { should contain_apache__vhost('default').with_ensure('absent') } + end + context 'with default ssl vhost' do + let :params do { + :default_ssl_vhost => true + } + end + it { should contain_apache__vhost('default-ssl').with_ensure('present') } + end + end +end diff --git a/apache/spec/classes/dev_spec.rb b/apache/spec/classes/dev_spec.rb new file mode 100644 index 000000000..e3d7dee39 --- /dev/null +++ b/apache/spec/classes/dev_spec.rb @@ -0,0 +1,38 @@ +require 'spec_helper' + +describe 'apache::dev', :type => :class do + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + } + end + it { should contain_class("apache::params") } + it { should contain_package("libaprutil1-dev") } + it { should contain_package("libapr1-dev") } + it { should contain_package("apache2-prefork-dev") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + } + end + it { should contain_class("apache::params") } + it { should contain_package("httpd-devel") } + end + context "on a FreeBSD OS" do + let :pre_condition do + 'include apache::package' + end + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + } + end + it { should contain_class("apache::params") } + end +end diff --git a/apache/spec/classes/mod/auth_kerb_spec.rb b/apache/spec/classes/mod/auth_kerb_spec.rb new file mode 100644 index 000000000..71c2349c3 --- /dev/null +++ b/apache/spec/classes/mod/auth_kerb_spec.rb @@ -0,0 +1,41 @@ +describe 'apache::mod::auth_kerb', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod("auth_kerb") } + it { should contain_package("libapache2-mod-auth-kerb") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod("auth_kerb") } + it { should contain_package("mod_auth_kerb") } + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod("auth_kerb") } + it { should contain_package("www/mod_auth_kerb2") } + end +end diff --git a/apache/spec/classes/mod/authnz_ldap_spec.rb b/apache/spec/classes/mod/authnz_ldap_spec.rb new file mode 100644 index 000000000..c8e832d95 --- /dev/null +++ b/apache/spec/classes/mod/authnz_ldap_spec.rb @@ -0,0 +1,65 @@ +describe 'apache::mod::authnz_ldap', :type => :class do + let :pre_condition do + 'include apache' + end + + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_class("apache::mod::ldap") } + it { should contain_apache__mod('authnz_ldap') } + + context 'default verifyServerCert' do + it { should contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert On$/) } + end + + context 'verifyServerCert = false' do + let(:params) { { :verifyServerCert => false } } + it { should contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert Off$/) } + end + + context 'verifyServerCert = wrong' do + let(:params) { { :verifyServerCert => 'wrong' } } + it 'should raise an error' do + expect { should raise_error Puppet::Error } + end + end + end #Debian + + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_class("apache::mod::ldap") } + it { should contain_apache__mod('authnz_ldap') } + + context 'default verifyServerCert' do + it { should contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert On$/) } + end + + context 'verifyServerCert = false' do + let(:params) { { :verifyServerCert => false } } + it { should contain_file('authnz_ldap.conf').with_content(/^LDAPVerifyServerCert Off$/) } + end + + context 'verifyServerCert = wrong' do + let(:params) { { :verifyServerCert => 'wrong' } } + it 'should raise an error' do + expect { should raise_error Puppet::Error } + end + end + end # Redhat + +end + diff --git a/apache/spec/classes/mod/dav_svn_spec.rb b/apache/spec/classes/mod/dav_svn_spec.rb new file mode 100644 index 000000000..fe11bb8cb --- /dev/null +++ b/apache/spec/classes/mod/dav_svn_spec.rb @@ -0,0 +1,41 @@ +describe 'apache::mod::dav_svn', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('dav_svn') } + it { should contain_package("libapache2-svn") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('dav_svn') } + it { should contain_package("mod_dav_svn") } + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('dav_svn') } + it { should contain_package("devel/subversion") } + end +end diff --git a/apache/spec/classes/mod/dev_spec.rb b/apache/spec/classes/mod/dev_spec.rb new file mode 100644 index 000000000..b72217aad --- /dev/null +++ b/apache/spec/classes/mod/dev_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' +describe 'apache::mod::dev', :type => :class do + [ + ['RedHat', '6'], + ['Debian', '6'], + ['FreeBSD', '9'], + ].each do |osfamily, operatingsystemrelease| + if osfamily == 'FreeBSD' + let :pre_condition do + 'include apache::package' + end + end + context "on a #{osfamily} OS" do + let :facts do + { + :osfamily => osfamily, + :operatingsystemrelease => operatingsystemrelease, + } + end + it { should contain_class('apache::dev') } + end + end +end diff --git a/apache/spec/classes/mod/dir_spec.rb b/apache/spec/classes/mod/dir_spec.rb new file mode 100644 index 000000000..b195eda0f --- /dev/null +++ b/apache/spec/classes/mod/dir_spec.rb @@ -0,0 +1,88 @@ +describe 'apache::mod::dir', :type => :class do + let :pre_condition do + 'class { "apache": + default_mods => false, + }' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + context "passing no parameters" do + it { should contain_class("apache::params") } + it { should contain_apache__mod('dir') } + it { should contain_file('dir.conf').with_content(/^DirectoryIndex /) } + it { should contain_file('dir.conf').with_content(/ index\.html /) } + it { should contain_file('dir.conf').with_content(/ index\.html\.var /) } + it { should contain_file('dir.conf').with_content(/ index\.cgi /) } + it { should contain_file('dir.conf').with_content(/ index\.pl /) } + it { should contain_file('dir.conf').with_content(/ index\.php /) } + it { should contain_file('dir.conf').with_content(/ index\.xhtml$/) } + end + context "passing indexes => ['example.txt','fearsome.aspx']" do + let :params do + {:indexes => ['example.txt','fearsome.aspx']} + end + it { should contain_file('dir.conf').with_content(/ example\.txt /) } + it { should contain_file('dir.conf').with_content(/ fearsome\.aspx$/) } + end + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + context "passing no parameters" do + it { should contain_class("apache::params") } + it { should contain_apache__mod('dir') } + it { should contain_file('dir.conf').with_content(/^DirectoryIndex /) } + it { should contain_file('dir.conf').with_content(/ index\.html /) } + it { should contain_file('dir.conf').with_content(/ index\.html\.var /) } + it { should contain_file('dir.conf').with_content(/ index\.cgi /) } + it { should contain_file('dir.conf').with_content(/ index\.pl /) } + it { should contain_file('dir.conf').with_content(/ index\.php /) } + it { should contain_file('dir.conf').with_content(/ index\.xhtml$/) } + end + context "passing indexes => ['example.txt','fearsome.aspx']" do + let :params do + {:indexes => ['example.txt','fearsome.aspx']} + end + it { should contain_file('dir.conf').with_content(/ example\.txt /) } + it { should contain_file('dir.conf').with_content(/ fearsome\.aspx$/) } + end + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + context "passing no parameters" do + it { should contain_class("apache::params") } + it { should contain_apache__mod('dir') } + it { should contain_file('dir.conf').with_content(/^DirectoryIndex /) } + it { should contain_file('dir.conf').with_content(/ index\.html /) } + it { should contain_file('dir.conf').with_content(/ index\.html\.var /) } + it { should contain_file('dir.conf').with_content(/ index\.cgi /) } + it { should contain_file('dir.conf').with_content(/ index\.pl /) } + it { should contain_file('dir.conf').with_content(/ index\.php /) } + it { should contain_file('dir.conf').with_content(/ index\.xhtml$/) } + end + context "passing indexes => ['example.txt','fearsome.aspx']" do + let :params do + {:indexes => ['example.txt','fearsome.aspx']} + end + it { should contain_file('dir.conf').with_content(/ example\.txt /) } + it { should contain_file('dir.conf').with_content(/ fearsome\.aspx$/) } + end + end +end diff --git a/apache/spec/classes/mod/event_spec.rb b/apache/spec/classes/mod/event_spec.rb new file mode 100644 index 000000000..7363e2fc9 --- /dev/null +++ b/apache/spec/classes/mod/event_spec.rb @@ -0,0 +1,17 @@ +describe 'apache::mod::event', :type => :class do + let :pre_condition do + 'class { "apache": mpm_module => false, }' + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('event') } + it { should contain_file("/usr/local/etc/apache22/Modules/event.conf").with_ensure('file') } + end +end diff --git a/apache/spec/classes/mod/fastcgi_spec.rb b/apache/spec/classes/mod/fastcgi_spec.rb new file mode 100644 index 000000000..8138bbab7 --- /dev/null +++ b/apache/spec/classes/mod/fastcgi_spec.rb @@ -0,0 +1,32 @@ +describe 'apache::mod::fastcgi', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('fastcgi') } + it { should contain_package("libapache2-mod-fastcgi") } + it { should contain_file('fastcgi.conf') } + end + + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('fastcgi') } + it { should contain_package("mod_fastcgi") } + it { should_not contain_file('fastcgi.conf') } + end +end diff --git a/apache/spec/classes/mod/fcgid_spec.rb b/apache/spec/classes/mod/fcgid_spec.rb new file mode 100644 index 000000000..5cc337291 --- /dev/null +++ b/apache/spec/classes/mod/fcgid_spec.rb @@ -0,0 +1,41 @@ +describe 'apache::mod::fcgid', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('fcgid') } + it { should contain_package("libapache2-mod-fcgid") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('fcgid') } + it { should contain_package("mod_fcgid") } + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('fcgid') } + it { should contain_package("www/mod_fcgid") } + end +end diff --git a/apache/spec/classes/mod/info_spec.rb b/apache/spec/classes/mod/info_spec.rb new file mode 100644 index 000000000..21d253e98 --- /dev/null +++ b/apache/spec/classes/mod/info_spec.rb @@ -0,0 +1,99 @@ +# This function is called inside the OS specific contexts +def general_info_specs + it { should contain_apache__mod("info") } + + it do + should contain_file("info.conf").with_content( + "\n"\ + " SetHandler server-info\n"\ + " Order deny,allow\n"\ + " Deny from all\n"\ + " Allow from 127.0.0.1 ::1\n"\ + "\n" + ) + end +end + +describe 'apache::mod::info', :type => :class do + let :pre_condition do + 'include apache' + end + + context "On a Debian OS with default params" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + + # Load the more generic tests for this context + general_info_specs() + + it { should contain_file("info.conf").with({ + :ensure => 'file', + :path => '/etc/apache2/mods-available/info.conf', + } ) } + it { should contain_file("info.conf symlink").with({ + :ensure => 'link', + :path => '/etc/apache2/mods-enabled/info.conf', + } ) } + end + + context "on a RedHat OS with default params" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + + # Load the more generic tests for this context + general_info_specs() + + it { should contain_file("info.conf").with_path("/etc/httpd/conf.d/info.conf") } + end + + context "On a FreeBSD OS with default params" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + + # Load the more generic tests for this context + general_info_specs() + + it { should contain_file("info.conf").with({ + :ensure => 'file', + :path => '/usr/local/etc/apache22/Modules/info.conf', + } ) } + end + + context "with $allow_from => ['10.10.10.10','11.11.11.11']" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + let :params do + { :allow_from => ['10.10.10.10','11.11.11.11'] } + end + it do + should contain_file("info.conf").with_content( + "\n"\ + " SetHandler server-info\n"\ + " Order deny,allow\n"\ + " Deny from all\n"\ + " Allow from 10.10.10.10 11.11.11.11\n"\ + "\n" + ) + end + end +end diff --git a/apache/spec/classes/mod/itk_spec.rb b/apache/spec/classes/mod/itk_spec.rb new file mode 100644 index 000000000..443ace0cc --- /dev/null +++ b/apache/spec/classes/mod/itk_spec.rb @@ -0,0 +1,31 @@ +describe 'apache::mod::itk', :type => :class do + let :pre_condition do + 'class { "apache": mpm_module => false, }' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('itk') } + it { should contain_file("/etc/apache2/mods-available/itk.conf").with_ensure('file') } + it { should contain_file("/etc/apache2/mods-enabled/itk.conf").with_ensure('link') } + it { should contain_package("apache2-mpm-itk") } + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('itk') } + it { should contain_file("/usr/local/etc/apache22/Modules/itk.conf").with_ensure('file') } + end +end diff --git a/apache/spec/classes/mod/mime_magic_spec.rb b/apache/spec/classes/mod/mime_magic_spec.rb new file mode 100644 index 000000000..30e91ef09 --- /dev/null +++ b/apache/spec/classes/mod/mime_magic_spec.rb @@ -0,0 +1,93 @@ +# This function is called inside the OS specific contexts +def general_mime_magic_specs + it { should contain_apache__mod("mime_magic") } +end + +describe 'apache::mod::mime_magic', :type => :class do + let :pre_condition do + 'include apache' + end + + context "On a Debian OS with default params" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + + general_mime_magic_specs() + + it do + should contain_file("mime_magic.conf").with_content( + "MIMEMagicFile /etc/apache2/magic\n" + ) + end + + it { should contain_file("mime_magic.conf").with({ + :ensure => 'file', + :path => '/etc/apache2/mods-available/mime_magic.conf', + } ) } + it { should contain_file("mime_magic.conf symlink").with({ + :ensure => 'link', + :path => '/etc/apache2/mods-enabled/mime_magic.conf', + } ) } + + context "with magic_file => /tmp/Debian_magic" do + let :params do + { :magic_file => "/tmp/Debian_magic" } + end + + it do + should contain_file("mime_magic.conf").with_content( + "MIMEMagicFile /tmp/Debian_magic\n" + ) + end + end + + end + + context "on a RedHat OS with default params" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + + general_mime_magic_specs() + + it do + should contain_file("mime_magic.conf").with_content( + "MIMEMagicFile /etc/httpd/conf/magic\n" + ) + end + + it { should contain_file("mime_magic.conf").with_path("/etc/httpd/conf.d/mime_magic.conf") } + + end + + context "with magic_file => /tmp/magic" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + + let :params do + { :magic_file => "/tmp/magic" } + end + + it do + should contain_file("mime_magic.conf").with_content( + "MIMEMagicFile /tmp/magic\n" + ) + end + end + + +end diff --git a/apache/spec/classes/mod/passenger_spec.rb b/apache/spec/classes/mod/passenger_spec.rb new file mode 100644 index 000000000..ace307b23 --- /dev/null +++ b/apache/spec/classes/mod/passenger_spec.rb @@ -0,0 +1,129 @@ +describe 'apache::mod::passenger', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('passenger') } + it { should contain_package("libapache2-mod-passenger") } + it { should contain_file('passenger.conf').with({ + 'path' => '/etc/apache2/mods-available/passenger.conf', + }) } + it { should contain_file('passenger.conf').with_content(/^ PassengerRoot \/usr$/) } + it { should contain_file('passenger.conf').with_content(/^ PassengerRuby \/usr\/bin\/ruby$/) } + describe "with passenger_high_performance => true" do + let :params do + { :passenger_high_performance => 'true' } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerHighPerformance true$/) } + end + describe "with passenger_pool_idle_time => 1200" do + let :params do + { :passenger_pool_idle_time => 1200 } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerPoolIdleTime 1200$/) } + end + describe "with passenger_max_requests => 20" do + let :params do + { :passenger_max_requests => 20 } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerMaxRequests 20$/) } + end + describe "with passenger_stat_throttle_rate => 10" do + let :params do + { :passenger_stat_throttle_rate => 10 } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerStatThrottleRate 10$/) } + end + describe "with passenger_max_pool_size => 16" do + let :params do + { :passenger_max_pool_size => 16 } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerMaxPoolSize 16$/) } + end + describe "with rack_autodetect => true" do + let :params do + { :rack_autodetect => true } + end + it { should contain_file('passenger.conf').with_content(/^ RackAutoDetect true$/) } + end + describe "with rails_autodetect => true" do + let :params do + { :rails_autodetect => true } + end + it { should contain_file('passenger.conf').with_content(/^ RailsAutoDetect true$/) } + end + describe "with passenger_root => '/usr/lib/example'" do + let :params do + { :passenger_root => '/usr/lib/example' } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerRoot \/usr\/lib\/example$/) } + end + describe "with passenger_ruby => /user/lib/example/ruby" do + let :params do + { :passenger_ruby => '/user/lib/example/ruby' } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerRuby \/user\/lib\/example\/ruby$/) } + end + describe "with passenger_use_global_queue => true" do + let :params do + { :passenger_use_global_queue => 'true' } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerUseGlobalQueue true$/) } + end + + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('passenger') } + it { should contain_package("mod_passenger") } + it { should contain_file('passenger_package.conf').with({ + 'path' => '/etc/httpd/conf.d/passenger.conf', + }) } + it { should contain_file('passenger_package.conf').without_content } + it { should contain_file('passenger_package.conf').without_source } + it { should contain_file('passenger.conf').with({ + 'path' => '/etc/httpd/conf.d/passenger_extra.conf', + }) } + it { should contain_file('passenger.conf').without_content(/PassengerRoot/) } + it { should contain_file('passenger.conf').without_content(/PassengerRuby/) } + describe "with passenger_root => '/usr/lib/example'" do + let :params do + { :passenger_root => '/usr/lib/example' } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerRoot \/usr\/lib\/example$/) } + end + describe "with passenger_ruby => /user/lib/example/ruby" do + let :params do + { :passenger_ruby => '/user/lib/example/ruby' } + end + it { should contain_file('passenger.conf').with_content(/^ PassengerRuby \/user\/lib\/example\/ruby$/) } + end + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('passenger') } + it { should contain_package("www/rubygem-passenger") } + end +end diff --git a/apache/spec/classes/mod/perl_spec.rb b/apache/spec/classes/mod/perl_spec.rb new file mode 100644 index 000000000..3cb7a3e67 --- /dev/null +++ b/apache/spec/classes/mod/perl_spec.rb @@ -0,0 +1,41 @@ +describe 'apache::mod::perl', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('perl') } + it { should contain_package("libapache2-mod-perl2") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('perl') } + it { should contain_package("mod_perl") } + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('perl') } + it { should contain_package("www/mod_perl2") } + end +end diff --git a/apache/spec/classes/mod/peruser_spec.rb b/apache/spec/classes/mod/peruser_spec.rb new file mode 100644 index 000000000..062905ccc --- /dev/null +++ b/apache/spec/classes/mod/peruser_spec.rb @@ -0,0 +1,17 @@ +describe 'apache::mod::peruser', :type => :class do + let :pre_condition do + 'class { "apache": mpm_module => false, }' + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('peruser') } + it { should contain_file("/usr/local/etc/apache22/Modules/peruser.conf").with_ensure('file') } + end +end diff --git a/apache/spec/classes/mod/php_spec.rb b/apache/spec/classes/mod/php_spec.rb new file mode 100644 index 000000000..57708a398 --- /dev/null +++ b/apache/spec/classes/mod/php_spec.rb @@ -0,0 +1,99 @@ +describe 'apache::mod::php', :type => :class do + describe "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + context "with mpm_module => prefork" do + let :pre_condition do + 'class { "apache": mpm_module => prefork, }' + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('php5') } + it { should contain_package("libapache2-mod-php5") } + it { should contain_file("php5.load").with( + :content => "LoadModule php5_module /usr/lib/apache2/modules/libphp5.so\n" + ) } + end + context 'with mpm_module => worker' do + let :pre_condition do + 'class { "apache": mpm_module => worker, }' + end + it 'should raise an error' do + expect { subject }.to raise_error Puppet::Error, /mpm_module => 'prefork'/ + end + end + end + describe "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + context "with default params" do + let :pre_condition do + 'class { "apache": }' + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('php5') } + it { should contain_package("php") } + it { should contain_file("php5.load").with( + :content => "LoadModule php5_module modules/libphp5.so\n" + ) } + end + context "with specific version" do + let :pre_condition do + 'class { "apache": }' + end + let :params do + { :package_ensure => '5.3.13'} + end + it { should contain_package("php").with( + :ensure => '5.3.13' + ) } + end + context "with mpm_module => prefork" do + let :pre_condition do + 'class { "apache": mpm_module => prefork, }' + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('php5') } + it { should contain_package("php") } + it { should contain_file("php5.load").with( + :content => "LoadModule php5_module modules/libphp5.so\n" + ) } + end + end + describe "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + context "with mpm_module => prefork" do + let :pre_condition do + 'class { "apache": mpm_module => prefork, }' + end + it { should contain_class('apache::params') } + it { should contain_apache__mod('php5') } + it { should contain_package("lang/php5") } + it { should contain_file('php5.load') } + end + # FIXME: not sure about the following context + context 'with mpm_module => worker' do + let :pre_condition do + 'class { "apache": mpm_module => worker, }' + end + it 'should raise an error' do + expect { subject.should contain_apache__mod('php5') }.to raise_error Puppet::Error, /mpm_module => 'prefork'/ + end + end + end +end diff --git a/apache/spec/classes/mod/prefork_spec.rb b/apache/spec/classes/mod/prefork_spec.rb new file mode 100644 index 000000000..543689422 --- /dev/null +++ b/apache/spec/classes/mod/prefork_spec.rb @@ -0,0 +1,47 @@ +describe 'apache::mod::prefork', :type => :class do + let :pre_condition do + 'class { "apache": mpm_module => false, }' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('prefork') } + it { should contain_file("/etc/apache2/mods-available/prefork.conf").with_ensure('file') } + it { should contain_file("/etc/apache2/mods-enabled/prefork.conf").with_ensure('link') } + it { should contain_package("apache2-mpm-prefork") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('prefork') } + it { should contain_file("/etc/httpd/conf.d/prefork.conf").with_ensure('file') } + it { should contain_file_line("/etc/sysconfig/httpd prefork enable").with({ + 'require' => 'Package[httpd]', + }) + } + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('prefork') } + it { should contain_file("/usr/local/etc/apache22/Modules/prefork.conf").with_ensure('file') } + end +end diff --git a/apache/spec/classes/mod/proxy_html_spec.rb b/apache/spec/classes/mod/proxy_html_spec.rb new file mode 100644 index 000000000..90be60b0f --- /dev/null +++ b/apache/spec/classes/mod/proxy_html_spec.rb @@ -0,0 +1,45 @@ +describe 'apache::mod::proxy_html', :type => :class do + let :pre_condition do + [ + 'include apache', + 'include apache::mod::proxy', + 'include apache::mod::proxy_http', + ] + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('proxy_html') } + it { should contain_package("libapache2-mod-proxy-html") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('proxy_html') } + it { should contain_package("mod_proxy_html") } + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('proxy_html') } + it { should contain_package("www/mod_proxy_html") } + end +end diff --git a/apache/spec/classes/mod/python_spec.rb b/apache/spec/classes/mod/python_spec.rb new file mode 100644 index 000000000..9042d0f1b --- /dev/null +++ b/apache/spec/classes/mod/python_spec.rb @@ -0,0 +1,41 @@ +describe 'apache::mod::python', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod("python") } + it { should contain_package("libapache2-mod-python") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod("python") } + it { should contain_package("mod_python") } + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod("python") } + it { should contain_package("www/mod_python3") } + end +end diff --git a/apache/spec/classes/mod/rpaf_spec.rb b/apache/spec/classes/mod/rpaf_spec.rb new file mode 100644 index 000000000..d9c9015ab --- /dev/null +++ b/apache/spec/classes/mod/rpaf_spec.rb @@ -0,0 +1,77 @@ +describe 'apache::mod::rpaf', :type => :class do + let :pre_condition do + [ + 'include apache', + ] + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('rpaf') } + it { should contain_package("libapache2-mod-rpaf") } + it { should contain_file('rpaf.conf').with({ + 'path' => '/etc/apache2/mods-available/rpaf.conf', + }) } + it { should contain_file('rpaf.conf').with_content(/^RPAFenable On$/) } + + describe "with sethostname => true" do + let :params do + { :sethostname => 'true' } + end + it { should contain_file('rpaf.conf').with_content(/^RPAFsethostname On$/) } + end + describe "with proxy_ips => [ 10.42.17.8, 10.42.18.99 ]" do + let :params do + { :proxy_ips => [ '10.42.17.8', '10.42.18.99' ] } + end + it { should contain_file('rpaf.conf').with_content(/^RPAFproxy_ips 10.42.17.8 10.42.18.99$/) } + end + describe "with header => X-Real-IP" do + let :params do + { :header => 'X-Real-IP' } + end + it { should contain_file('rpaf.conf').with_content(/^RPAFheader X-Real-IP$/) } + end + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('rpaf') } + it { should contain_package("www/mod_rpaf2") } + it { should contain_file('rpaf.conf').with({ + 'path' => '/usr/local/etc/apache22/Modules/rpaf.conf', + }) } + it { should contain_file('rpaf.conf').with_content(/^RPAFenable On$/) } + + describe "with sethostname => true" do + let :params do + { :sethostname => 'true' } + end + it { should contain_file('rpaf.conf').with_content(/^RPAFsethostname On$/) } + end + describe "with proxy_ips => [ 10.42.17.8, 10.42.18.99 ]" do + let :params do + { :proxy_ips => [ '10.42.17.8', '10.42.18.99' ] } + end + it { should contain_file('rpaf.conf').with_content(/^RPAFproxy_ips 10.42.17.8 10.42.18.99$/) } + end + describe "with header => X-Real-IP" do + let :params do + { :header => 'X-Real-IP' } + end + it { should contain_file('rpaf.conf').with_content(/^RPAFheader X-Real-IP$/) } + end + end +end diff --git a/apache/spec/classes/mod/ssl_spec.rb b/apache/spec/classes/mod/ssl_spec.rb new file mode 100644 index 000000000..45005a191 --- /dev/null +++ b/apache/spec/classes/mod/ssl_spec.rb @@ -0,0 +1,53 @@ +describe 'apache::mod::ssl', :type => :class do + let :pre_condition do + 'include apache' + end + context 'on an unsupported OS' do + let :facts do + { + :osfamily => 'Magic', + :operatingsystemrelease => '0', + :concat_basedir => '/dne', + } + end + it { expect { subject }.to raise_error(Puppet::Error, /Unsupported osfamily:/) } + end + + context 'on a RedHat OS' do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class('apache::params') } + it { should contain_apache__mod('ssl') } + it { should contain_package('mod_ssl') } + end + + context 'on a Debian OS' do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class('apache::params') } + it { should contain_apache__mod('ssl') } + it { should_not contain_package('libapache2-mod-ssl') } + end + + context 'on a FreeBSD OS' do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class('apache::params') } + it { should contain_apache__mod('ssl') } + end +end diff --git a/apache/spec/classes/mod/status_spec.rb b/apache/spec/classes/mod/status_spec.rb new file mode 100644 index 000000000..0a0658879 --- /dev/null +++ b/apache/spec/classes/mod/status_spec.rb @@ -0,0 +1,166 @@ +require 'spec_helper' + +# Helper function for testing the contents of `status.conf` +def status_conf_spec(allow_from, extended_status) + it do + should contain_file("status.conf").with_content( + "\n"\ + " SetHandler server-status\n"\ + " Order deny,allow\n"\ + " Deny from all\n"\ + " Allow from #{Array(allow_from).join(' ')}\n"\ + "\n"\ + "ExtendedStatus #{extended_status}\n"\ + "\n"\ + "\n"\ + " # Show Proxy LoadBalancer status in mod_status\n"\ + " ProxyStatus On\n"\ + "\n" + ) + end +end + +describe 'apache::mod::status', :type => :class do + let :pre_condition do + 'include apache' + end + + context "on a Debian OS with default params" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + + it { should contain_apache__mod("status") } + + status_conf_spec(["127.0.0.1", "::1"], "On") + + it { should contain_file("status.conf").with({ + :ensure => 'file', + :path => '/etc/apache2/mods-available/status.conf', + } ) } + + it { should contain_file("status.conf symlink").with({ + :ensure => 'link', + :path => '/etc/apache2/mods-enabled/status.conf', + } ) } + + end + + context "on a RedHat OS with default params" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + + it { should contain_apache__mod("status") } + + status_conf_spec(["127.0.0.1", "::1"], "On") + + it { should contain_file("status.conf").with_path("/etc/httpd/conf.d/status.conf") } + + end + + context "with custom parameters $allow_from => ['10.10.10.10','11.11.11.11'], $extended_status => 'Off'" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + let :params do + { + :allow_from => ['10.10.10.10','11.11.11.11'], + :extended_status => 'Off', + } + end + + status_conf_spec(["10.10.10.10", "11.11.11.11"], "Off") + + end + + context "with valid parameter type $allow_from => ['10.10.10.10']" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + let :params do + { :allow_from => ['10.10.10.10'] } + end + it 'should expect to succeed array validation' do + expect { + should contain_file("status.conf") + }.not_to raise_error() + end + end + + context "with invalid parameter type $allow_from => '10.10.10.10'" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + let :params do + { :allow_from => '10.10.10.10' } + end + it 'should expect to fail array validation' do + expect { + should contain_file("status.conf") + }.to raise_error(Puppet::Error) + end + end + + # Only On or Off are valid options + ['On', 'Off'].each do |valid_param| + context "with valid value $extended_status => '#{valid_param}'" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + let :params do + { :extended_status => valid_param } + end + it 'should expect to succeed regular expression validation' do + expect { + should contain_file("status.conf") + }.not_to raise_error() + end + end + end + + ['Yes', 'No'].each do |invalid_param| + context "with invalid value $extended_status => '#{invalid_param}'" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + let :params do + { :extended_status => invalid_param } + end + it 'should expect to fail regular expression validation' do + expect { + should contain_file("status.conf") + }.to raise_error(Puppet::Error) + end + end + end + +end diff --git a/apache/spec/classes/mod/suphp_spec.rb b/apache/spec/classes/mod/suphp_spec.rb new file mode 100644 index 000000000..382314d9a --- /dev/null +++ b/apache/spec/classes/mod/suphp_spec.rb @@ -0,0 +1,27 @@ +describe 'apache::mod::suphp', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_package("libapache2-mod-suphp") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_package("mod_suphp") } + end +end diff --git a/apache/spec/classes/mod/worker_spec.rb b/apache/spec/classes/mod/worker_spec.rb new file mode 100644 index 000000000..1af8ff8c6 --- /dev/null +++ b/apache/spec/classes/mod/worker_spec.rb @@ -0,0 +1,44 @@ +describe 'apache::mod::worker', :type => :class do + let :pre_condition do + 'class { "apache": mpm_module => false, }' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('worker') } + it { should contain_file("/etc/apache2/mods-available/worker.conf").with_ensure('file') } + it { should contain_file("/etc/apache2/mods-enabled/worker.conf").with_ensure('link') } + it { should contain_package("apache2-mpm-worker") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('worker') } + it { should contain_file("/etc/httpd/conf.d/worker.conf").with_ensure('file') } + it { should contain_file_line("/etc/sysconfig/httpd worker enable") } + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should_not contain_apache__mod('worker') } + it { should contain_file("/usr/local/etc/apache22/Modules/worker.conf").with_ensure('file') } + end +end diff --git a/apache/spec/classes/mod/wsgi_spec.rb b/apache/spec/classes/mod/wsgi_spec.rb new file mode 100644 index 000000000..8d26526cb --- /dev/null +++ b/apache/spec/classes/mod/wsgi_spec.rb @@ -0,0 +1,54 @@ +describe 'apache::mod::wsgi', :type => :class do + let :pre_condition do + 'include apache' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('wsgi') } + it { should contain_package("libapache2-mod-wsgi") } + end + context "on a RedHat OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('wsgi') } + it { should contain_package("mod_wsgi") } + + describe "with custom WSGISocketPrefix" do + let :params do + { :wsgi_socket_prefix => 'run/wsgi' } + end + it {should contain_file('wsgi.conf').with_content(/^ WSGISocketPrefix run\/wsgi$/)} + end + describe "with custom WSGIPythonHome" do + let :params do + { :wsgi_python_home => '/path/to/virtenv' } + end + it {should contain_file('wsgi.conf').with_content(/^ WSGIPythonHome \/path\/to\/virtenv$/)} + end + end + context "on a FreeBSD OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_class("apache::params") } + it { should contain_apache__mod('wsgi') } + it { should contain_package("www/mod_wsgi") } + end +end diff --git a/apache/spec/classes/params_spec.rb b/apache/spec/classes/params_spec.rb new file mode 100644 index 000000000..39e16b6f3 --- /dev/null +++ b/apache/spec/classes/params_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe 'apache::params', :type => :class do + context "On a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_apache__params } + + # There are 4 resources in this class currently + # there should not be any more resources because it is a params class + # The resources are class[apache::params], class[main], class[settings], stage[main] + it "Should not contain any resources" do + subject.resources.size.should == 4 + end + end +end diff --git a/apache/spec/classes/service_spec.rb b/apache/spec/classes/service_spec.rb new file mode 100644 index 000000000..accc54946 --- /dev/null +++ b/apache/spec/classes/service_spec.rb @@ -0,0 +1,109 @@ +require 'spec_helper' + +describe 'apache::service', :type => :class do + let :pre_condition do + 'include apache::params' + end + context "on a Debian OS" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + it { should contain_service("httpd").with( + 'name' => 'apache2', + 'ensure' => 'running', + 'enable' => 'true' + ) + } + + context "with $service_name => 'foo'" do + let (:params) {{ :service_name => 'foo' }} + it { should contain_service("httpd").with( + 'name' => 'foo' + ) + } + end + + context "with $service_enable => true" do + let (:params) {{ :service_enable => true }} + it { should contain_service("httpd").with( + 'name' => 'apache2', + 'ensure' => 'running', + 'enable' => 'true' + ) + } + end + + context "with $service_enable => false" do + let (:params) {{ :service_enable => false }} + it { should contain_service("httpd").with( + 'name' => 'apache2', + 'ensure' => 'running', + 'enable' => 'false' + ) + } + end + + context "$service_enable must be a bool" do + let (:params) {{ :service_enable => 'not-a-boolean' }} + + it 'should fail' do + expect { subject }.to raise_error(Puppet::Error, /is not a boolean/) + end + end + + context "with $service_ensure => 'running'" do + let (:params) {{ :service_ensure => 'running', }} + it { should contain_service("httpd").with( + 'ensure' => 'running', + 'enable' => 'true' + ) + } + end + + context "with $service_ensure => 'stopped'" do + let (:params) {{ :service_ensure => 'stopped', }} + it { should contain_service("httpd").with( + 'ensure' => 'stopped', + 'enable' => 'true' + ) + } + end + end + + + context "on a RedHat 5 OS" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '5', + :concat_basedir => '/dne', + } + end + it { should contain_service("httpd").with( + 'name' => 'httpd', + 'ensure' => 'running', + 'enable' => 'true' + ) + } + end + + context "on a FreeBSD 5 OS" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + it { should contain_service("httpd").with( + 'name' => 'apache22', + 'ensure' => 'running', + 'enable' => 'true' + ) + } + end +end diff --git a/apache/spec/defines/mod_spec.rb b/apache/spec/defines/mod_spec.rb new file mode 100644 index 000000000..bbc5f0bdc --- /dev/null +++ b/apache/spec/defines/mod_spec.rb @@ -0,0 +1,105 @@ +require 'spec_helper' + +describe 'apache::mod', :type => :define do + let :pre_condition do + 'include apache' + end + context "on a RedHat osfamily" do + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + + describe "for non-special modules" do + let :title do + 'spec_m' + end + it { should contain_class("apache::params") } + it "should manage the module load file" do + should contain_file('spec_m.load').with({ + :path => '/etc/httpd/conf.d/spec_m.load', + :content => "LoadModule spec_m_module modules/mod_spec_m.so\n", + :owner => 'root', + :group => 'root', + :mode => '0644', + } ) + end + end + + describe "with shibboleth module and package param passed" do + # name/title for the apache::mod define + let :title do + 'xsendfile' + end + # parameters + let(:params) { {:package => 'mod_xsendfile'} } + + it { should contain_class("apache::params") } + it { should contain_package('mod_xsendfile') } + end + end + + context "on a Debian osfamily" do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + + describe "for non-special modules" do + let :title do + 'spec_m' + end + it { should contain_class("apache::params") } + it "should manage the module load file" do + should contain_file('spec_m.load').with({ + :path => '/etc/apache2/mods-available/spec_m.load', + :content => "LoadModule spec_m_module /usr/lib/apache2/modules/mod_spec_m.so\n", + :owner => 'root', + :group => 'root', + :mode => '0644', + } ) + end + it "should link the module load file" do + should contain_file('spec_m.load symlink').with({ + :path => '/etc/apache2/mods-enabled/spec_m.load', + :target => '/etc/apache2/mods-available/spec_m.load', + :owner => 'root', + :group => 'root', + :mode => '0644', + } ) + end + end + end + + context "on a FreeBSD osfamily" do + let :facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + + describe "for non-special modules" do + let :title do + 'spec_m' + end + it { should contain_class("apache::params") } + it "should manage the module load file" do + should contain_file('spec_m.load').with({ + :path => '/usr/local/etc/apache22/Modules/spec_m.load', + :content => "LoadModule spec_m_module /usr/local/libexec/apache22/mod_spec_m.so\n", + :owner => 'root', + :group => 'wheel', + :mode => '0644', + } ) + end + end + end +end diff --git a/apache/spec/defines/vhost_spec.rb b/apache/spec/defines/vhost_spec.rb new file mode 100644 index 000000000..b1ab27503 --- /dev/null +++ b/apache/spec/defines/vhost_spec.rb @@ -0,0 +1,1175 @@ +require 'spec_helper' + +describe 'apache::vhost', :type => :define do + let :pre_condition do + 'class { "apache": default_vhost => false, }' + end + let :title do + 'rspec.example.com' + end + let :default_params do + { + :docroot => '/rspec/docroot', + :port => '84', + } + end + describe 'os-dependent items' do + context "on RedHat based systems" do + let :default_facts do + { + :osfamily => 'RedHat', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + let :params do default_params end + let :facts do default_facts end + it { should contain_class("apache") } + it { should contain_class("apache::params") } + end + context "on Debian based systems" do + let :default_facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + let :params do default_params end + let :facts do default_facts end + it { should contain_class("apache") } + it { should contain_class("apache::params") } + it { should contain_file("25-rspec.example.com.conf").with( + :ensure => 'present', + :path => '/etc/apache2/sites-available/25-rspec.example.com.conf' + ) } + it { should contain_file("25-rspec.example.com.conf symlink").with( + :ensure => 'link', + :path => '/etc/apache2/sites-enabled/25-rspec.example.com.conf', + :target => '/etc/apache2/sites-available/25-rspec.example.com.conf' + ) } + end + context "on FreeBSD systems" do + let :default_facts do + { + :osfamily => 'FreeBSD', + :operatingsystemrelease => '9', + :concat_basedir => '/dne', + } + end + let :params do default_params end + let :facts do default_facts end + it { should contain_class("apache") } + it { should contain_class("apache::params") } + it { should contain_file("25-rspec.example.com.conf").with( + :ensure => 'present', + :path => '/usr/local/etc/apache22/Vhosts/25-rspec.example.com.conf' + ) } + end + end + describe 'os-independent items' do + let :facts do + { + :osfamily => 'Debian', + :operatingsystemrelease => '6', + :concat_basedir => '/dne', + } + end + describe 'basic assumptions' do + let :params do default_params end + it { should contain_class("apache") } + it { should contain_class("apache::params") } + it { should contain_apache__listen(params[:port]) } + it { should contain_apache__namevirtualhost("*:#{params[:port]}") } + end + + # All match and notmatch should be a list of regexs and exact match strings + context ".conf content" do + [ + { + :title => 'should contain docroot', + :attr => 'docroot', + :value => '/not/default', + :match => [/^ DocumentRoot \/not\/default$/,/ /], + }, + { + :title => 'should set a port', + :attr => 'port', + :value => '8080', + :match => [/^$/], + }, + { + :title => 'should set an ip', + :attr => 'ip', + :value => '10.0.0.1', + :match => [/^$/], + }, + { + :title => 'should set a serveradmin', + :attr => 'serveradmin', + :value => 'test@test.com', + :match => [/^ ServerAdmin test@test.com$/], + }, + { + :title => 'should enable ssl', + :attr => 'ssl', + :value => true, + :match => [/^ SSLEngine on$/], + }, + { + :title => 'should set a servername', + :attr => 'servername', + :value => 'param.test', + :match => [/^ ServerName param.test$/], + }, + { + :title => 'should accept server aliases', + :attr => 'serveraliases', + :value => ['one.com','two.com'], + :match => [ + /^ ServerAlias one\.com$/, + /^ ServerAlias two\.com$/ + ], + }, + { + :title => 'should accept setenv', + :attr => 'setenv', + :value => ['TEST1 one','TEST2 two'], + :match => [ + /^ SetEnv TEST1 one$/, + /^ SetEnv TEST2 two$/ + ], + }, + { + :title => 'should accept setenvif', + :attr => 'setenvif', + ## These are bugged in rspec-puppet; the $1 is droped + #:value => ['Host "^([^\.]*)\.website\.com$" CLIENT_NAME=$1'], + #:match => [' SetEnvIf Host "^([^\.]*)\.website\.com$" CLIENT_NAME=$1'], + :value => ['Host "^test\.com$" VHOST_ACCESS=test'], + :match => [/^ SetEnvIf Host "\^test\\.com\$" VHOST_ACCESS=test$/], + }, + { + :title => 'should accept options', + :attr => 'options', + :value => ['Fake','Options'], + :match => [/^ Options Fake Options$/], + }, + { + :title => 'should accept overrides', + :attr => 'override', + :value => ['Fake', 'Override'], + :match => [/^ AllowOverride Fake Override$/], + }, + { + :title => 'should accept logroot', + :attr => 'logroot', + :value => '/fake/log', + :match => [/CustomLog \/fake\/log\//,/ErrorLog \/fake\/log\//], + }, + { + :title => 'should accept log_level', + :attr => 'log_level', + :value => 'info', + :match => [/LogLevel info/], + }, + { + :title => 'should accept pipe destination for access log', + :attr => 'access_log_pipe', + :value => '| /bin/fake/logging', + :match => [/CustomLog "| \/bin\/fake\/logging" combined$/], + }, + { + :title => 'should accept pipe destination for error log', + :attr => 'error_log_pipe', + :value => '| /bin/fake/logging', + :match => [/ErrorLog "| \/bin\/fake\/logging" combined$/], + }, + { + :title => 'should accept syslog destination for access log', + :attr => 'access_log_syslog', + :value => 'syslog:local1', + :match => [/CustomLog syslog:local1 combined$/], + }, + { + :title => 'should accept syslog destination for error log', + :attr => 'error_log_syslog', + :value => 'syslog', + :match => [/ErrorLog syslog$/], + }, + { + :title => 'should accept custom format for access logs', + :attr => 'access_log_format', + :value => '%h %{X-Forwarded-For}i %l %u %t \"%r\" %s %b \"%{Referer}i\" \"%{User-agent}i\" \"Host: %{Host}i\" %T %D', + :match => [/CustomLog \/var\/log\/.+_access\.log "%h %\{X-Forwarded-For\}i %l %u %t \\"%r\\" %s %b \\"%\{Referer\}i\\" \\"%\{User-agent\}i\\" \\"Host: %\{Host\}i\\" %T %D"$/], + }, + { + :title => 'should contain access logs', + :attr => 'access_log', + :value => true, + :match => [/CustomLog \/var\/log\/.+_access\.log combined$/], + }, + { + :title => 'should not contain access logs', + :attr => 'access_log', + :value => false, + :notmatch => [/CustomLog \/var\/log\/.+_access\.log combined$/], + }, + { + :title => 'should contain error logs', + :attr => 'error_log', + :value => true, + :match => [/ErrorLog.+$/], + }, + { + :title => 'should not contain error logs', + :attr => 'error_log', + :value => false, + :notmatch => [/ErrorLog.+$/], + }, + { + :title => 'should set ErrorDocument 503', + :attr => 'error_documents', + :value => [ { 'error_code' => '503', 'document' => '"Go away, the backend is broken."'}], + :match => [/^ ErrorDocument 503 "Go away, the backend is broken."$/], + }, + { + :title => 'should set ErrorDocuments 503 407', + :attr => 'error_documents', + :value => [ + { 'error_code' => '503', 'document' => '/service-unavail'}, + { 'error_code' => '407', 'document' => 'https://example.com/proxy/login'}, + ], + :match => [ + /^ ErrorDocument 503 \/service-unavail$/, + /^ ErrorDocument 407 https:\/\/example\.com\/proxy\/login$/, + ], + }, + { + :title => 'should set ErrorDocument 503 in directory', + :attr => 'directories', + :value => { 'path' => '/srv/www', 'error_documents' => [{ 'error_code' => '503', 'document' => '"Go away, the backend is broken."'}] }, + :match => [/^ ErrorDocument 503 "Go away, the backend is broken."$/], + }, + { + :title => 'should set ErrorDocuments 503 407 in directory', + :attr => 'directories', + :value => { 'path' => '/srv/www', 'error_documents' => + [ + { 'error_code' => '503', 'document' => '/service-unavail'}, + { 'error_code' => '407', 'document' => 'https://example.com/proxy/login'}, + ]}, + :match => [ + /^ ErrorDocument 503 \/service-unavail$/, + /^ ErrorDocument 407 https:\/\/example\.com\/proxy\/login$/, + ], + }, + { + :title => 'should accept a scriptalias', + :attr => 'scriptalias', + :value => '/usr/scripts', + :match => [ + /^ ScriptAlias \/cgi-bin\/ \/usr\/scripts$/, + ], + }, + { + :title => 'should accept a single scriptaliases', + :attr => 'scriptaliases', + :value => { 'alias' => '/blah/', 'path' => '/usr/scripts' }, + :match => [ + /^ ScriptAlias \/blah\/ \/usr\/scripts$/, + ], + :nomatch => [/ScriptAlias \/cgi\-bin\//], + }, + { + :title => 'should accept multiple scriptaliases', + :attr => 'scriptaliases', + :value => [ { 'alias' => '/blah', 'path' => '/usr/scripts' }, { 'alias' => '/blah2', 'path' => '/usr/scripts' } ], + :match => [ + /^ ScriptAlias \/blah \/usr\/scripts$/, + /^ ScriptAlias \/blah2 \/usr\/scripts$/, + ], + :nomatch => [/ScriptAlias \/cgi\-bin\//], + }, + { + :title => 'should accept multiple scriptaliases with and without trailing slashes', + :attr => 'scriptaliases', + :value => [ { 'alias' => '/blah', 'path' => '/usr/scripts' }, { 'alias' => '/blah2/', 'path' => '/usr/scripts2/' } ], + :match => [ + /^ ScriptAlias \/blah \/usr\/scripts$/, + /^ ScriptAlias \/blah2\/ \/usr\/scripts2\/$/, + ], + :nomatch => [/ScriptAlias \/cgi\-bin\//], + }, + { + :title => 'should accept a ScriptAliasMatch directive', + :attr => 'scriptaliases', + ## XXX As mentioned above, rspec-puppet drops constructs like $1. + ## Thus, these tests don't work as they should. As a workaround we + ## use FOO instead of $1 here. + :value => [ { 'aliasmatch' => '^/cgi-bin(.*)', 'path' => '/usr/local/apache/cgi-binFOO' } ], + :match => [ + /^ ScriptAliasMatch \^\/cgi-bin\(\.\*\) \/usr\/local\/apache\/cgi-binFOO$/ + ], + }, + { + :title => 'should accept multiple ScriptAliasMatch directives', + :attr => 'scriptaliases', + ## XXX As mentioned above, rspec-puppet drops constructs like $1. + ## Thus, these tests don't work as they should. As a workaround we + ## use FOO instead of $1 here. + :value => [ + { 'aliasmatch' => '^/cgi-bin(.*)', 'path' => '/usr/local/apache/cgi-binFOO' }, + { 'aliasmatch' => '"(?x)^/git/(.*/(HEAD|info/refs|objects/(info/[^/]+|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))|git-(upload|receive)-pack))"', 'path' => '/var/www/bin/gitolite-suexec-wrapper/FOO' }, + ], + :match => [ + /^ ScriptAliasMatch \^\/cgi-bin\(\.\*\) \/usr\/local\/apache\/cgi-binFOO$/, + /^ ScriptAliasMatch "\(\?x\)\^\/git\/\(\.\*\/\(HEAD\|info\/refs\|objects\/\(info\/\[\^\/\]\+\|\[0-9a-f\]\{2\}\/\[0-9a-f\]\{38\}\|pack\/pack-\[0-9a-f\]\{40\}\\\.\(pack\|idx\)\)\|git-\(upload\|receive\)-pack\)\)" \/var\/www\/bin\/gitolite-suexec-wrapper\/FOO$/, + ], + }, + { + :title => 'should accept mixed ScriptAlias and ScriptAliasMatch directives', + :attr => 'scriptaliases', + ## XXX As mentioned above, rspec-puppet drops constructs like $1. + ## Thus, these tests don't work as they should. As a workaround we + ## use FOO instead of $1 here. + :value => [ + { 'aliasmatch' => '"(?x)^/git/(.*/(HEAD|info/refs|objects/(info/[^/]+|[0-9a-f]{2}/[0-9a-f]{38}|pack/pack-[0-9a-f]{40}\.(pack|idx))|git-(upload|receive)-pack))"', 'path' => '/var/www/bin/gitolite-suexec-wrapper/FOO' }, + { 'alias' => '/git', 'path' => '/var/www/gitweb/index.cgi' }, + { 'aliasmatch' => '^/cgi-bin(.*)', 'path' => '/usr/local/apache/cgi-binFOO' }, + { 'alias' => '/trac', 'path' => '/etc/apache2/trac.fcgi' }, + ], + :match => [ + /^ ScriptAliasMatch "\(\?x\)\^\/git\/\(\.\*\/\(HEAD\|info\/refs\|objects\/\(info\/\[\^\/\]\+\|\[0-9a-f\]\{2\}\/\[0-9a-f\]\{38\}\|pack\/pack-\[0-9a-f\]\{40\}\\\.\(pack\|idx\)\)\|git-\(upload\|receive\)-pack\)\)" \/var\/www\/bin\/gitolite-suexec-wrapper\/FOO$/, + /^ ScriptAlias \/git \/var\/www\/gitweb\/index\.cgi$/, + /^ ScriptAliasMatch \^\/cgi-bin\(\.\*\) \/usr\/local\/apache\/cgi-binFOO$/, + /^ ScriptAlias \/trac \/etc\/apache2\/trac.fcgi$/, + ], + }, + { + :title => 'should accept proxy destinations', + :attr => 'proxy_dest', + :value => 'http://fake.com', + :match => [ + /^ ProxyPass \/ http:\/\/fake.com\/$/, + /^ $/, + /^ ProxyPassReverse \/$/, + /^ <\/Location>$/, + ], + :notmatch => [/ProxyPass .+!$/], + }, + { + :title => 'should accept proxy_pass hash', + :attr => 'proxy_pass', + :value => { 'path' => '/path-a', 'url' => 'http://fake.com/a' }, + :match => [ + /^ ProxyPass \/path-a http:\/\/fake.com\/a$/, + /^ $/, + /^ ProxyPassReverse \/$/, + /^ <\/Location>$/, + + ], + :notmatch => [/ProxyPass .+!$/], + }, + { + :title => 'should accept proxy_pass array of hash', + :attr => 'proxy_pass', + :value => [ + { 'path' => '/path-a/', 'url' => 'http://fake.com/a/' }, + { 'path' => '/path-b', 'url' => 'http://fake.com/b' }, + ], + :match => [ + /^ ProxyPass \/path-a\/ http:\/\/fake.com\/a\/$/, + /^ $/, + /^ ProxyPassReverse \/$/, + /^ <\/Location>$/, + /^ ProxyPass \/path-b http:\/\/fake.com\/b$/, + /^ $/, + /^ ProxyPassReverse \/$/, + /^ <\/Location>$/, + ], + :notmatch => [/ProxyPass .+!$/], + }, + { + :title => 'should enable rack', + :attr => 'rack_base_uris', + :value => ['/rack1','/rack2'], + :match => [ + /^ RackBaseURI \/rack1$/, + /^ RackBaseURI \/rack2$/, + ], + }, + { + :title => 'should accept headers', + :attr => 'headers', + :value => ['add something', 'merge something_else'], + :match => [ + /^ Header add something$/, + /^ Header merge something_else$/, + ], + }, + { + :title => 'should accept request headers', + :attr => 'request_headers', + :value => ['append something', 'unset something_else'], + :match => [ + /^ RequestHeader append something$/, + /^ RequestHeader unset something_else$/, + ], + }, + { + :title => 'should accept rewrite rules', + :attr => 'rewrite_rule', + :value => 'not a real rule', + :match => [/^ RewriteRule not a real rule$/], + }, + { + :title => 'should accept rewrite rules', + :attr => 'rewrites', + :value => [{'rewrite_rule' => ['not a real rule']}], + :match => [/^ RewriteRule not a real rule$/], + }, + { + :title => 'should accept rewrite comment', + :attr => 'rewrites', + :value => [{'comment' => 'rewrite comment', 'rewrite_rule' => ['not a real rule']}], + :match => [/^ #rewrite comment/], + }, + { + :title => 'should accept rewrite conditions', + :attr => 'rewrites', + :value => [{'comment' => 'redirect IE', 'rewrite_cond' => ['%{HTTP_USER_AGENT} ^MSIE'], 'rewrite_rule' => ['^index\.html$ welcome.html'],}], + :match => [ + /^ #redirect IE$/, + /^ RewriteCond %{HTTP_USER_AGENT} \^MSIE$/, + /^ RewriteRule \^index\\\.html\$ welcome.html$/, + ], + }, + { + :title => 'should accept multiple rewrites', + :attr => 'rewrites', + :value => [ + {'rewrite_rule' => ['not a real rule']}, + {'rewrite_rule' => ['not a real rule two']}, + ], + :match => [ + /^ RewriteRule not a real rule$/, + /^ RewriteRule not a real rule two$/, + ], + }, + { + :title => 'should block scm', + :attr => 'block', + :value => 'scm', + :match => [/^ $/], + }, + { + :title => 'should accept a custom fragment', + :attr => 'custom_fragment', + :value => " Some custom fragment line\n That spans multiple lines", + :match => [ + /^ Some custom fragment line$/, + /^ That spans multiple lines$/, + /^<\/VirtualHost>$/, + ], + }, + { + :title => 'should accept an array of alias hashes', + :attr => 'aliases', + :value => [ { 'alias' => '/', 'path' => '/var/www'} ], + :match => [/^ Alias \/ \/var\/www$/], + }, + { + :title => 'should accept an alias hash', + :attr => 'aliases', + :value => { 'alias' => '/', 'path' => '/var/www'}, + :match => [/^ Alias \/ \/var\/www$/], + }, + { + :title => 'should accept multiple aliases', + :attr => 'aliases', + :value => [ + { 'alias' => '/', 'path' => '/var/www'}, + { 'alias' => '/cgi-bin', 'path' => '/var/www/cgi-bin'}, + { 'alias' => '/css', 'path' => '/opt/someapp/css'}, + ], + :match => [ + /^ Alias \/ \/var\/www$/, + /^ Alias \/cgi-bin \/var\/www\/cgi-bin$/, + /^ Alias \/css \/opt\/someapp\/css$/, + ], + }, + { + :title => 'should accept an aliasmatch hash', + :attr => 'aliases', + ## XXX As mentioned above, rspec-puppet drops the $1. Thus, these + # tests don't work. + #:value => { 'aliasmatch' => '^/image/(.*).gif', 'path' => '/files/gifs/$1.gif' }, + #:match => [/^ AliasMatch \^\/image\/\(\.\*\)\.gif \/files\/gifs\/\$1\.gif$/], + }, + { + :title => 'should accept a array of alias and aliasmatch hashes mixed', + :attr => 'aliases', + ## XXX As mentioned above, rspec-puppet drops the $1. Thus, these + # tests don't work. + #:value => [ + # { 'alias' => '/css', 'path' => '/files/css' }, + # { 'aliasmatch' => '^/image/(.*).gif', 'path' => '/files/gifs/$1.gif' }, + # { 'aliasmatch' => '^/image/(.*).jpg', 'path' => '/files/jpgs/$1.jpg' }, + # { 'alias' => '/image', 'path' => '/files/images' }, + #], + #:match => [ + # /^ Alias \/css \/files\/css$/, + # /^ AliasMatch \^\/image\/\(.\*\)\.gif \/files\/gifs\/\$1\.gif$/, + # /^ AliasMatch \^\/image\/\(.\*\)\.jpg \/files\/jpgs\/\$1\.jpg$/, + # /^ Alias \/image \/files\/images$/ + #], + }, + { + :title => 'should accept multiple additional includes', + :attr => 'additional_includes', + :value => [ + '/tmp/proxy_group_a', + '/tmp/proxy_group_b', + '/tmp/proxy_group_c', + ], + :match => [ + /^ Include \/tmp\/proxy_group_a$/, + /^ Include \/tmp\/proxy_group_b$/, + /^ Include \/tmp\/proxy_group_c$/, + ], + }, + { + :title => 'should accept a suPHP_Engine', + :attr => 'suphp_engine', + :value => 'on', + :match => [/^ suPHP_Engine on$/], + }, + { + :title => 'should accept a php_admin_flags', + :attr => 'php_admin_flags', + :value => { 'engine' => 'on' }, + :match => [/^ php_admin_flag engine on$/], + }, + { + :title => 'should accept php_admin_values', + :attr => 'php_admin_values', + :value => { 'open_basedir' => '/srv/web/www.com/:/usr/share/pear/' }, + :match => [/^ php_admin_value open_basedir \/srv\/web\/www.com\/:\/usr\/share\/pear\/$/], + }, + { + :title => 'should accept php_admin_flags in directories', + :attr => 'directories', + :value => { + 'path' => '/srv/www', + 'php_admin_flags' => { 'php_engine' => 'on' } + }, + :match => [/^ php_admin_flag php_engine on$/], + }, + { + :title => 'should accept php_admin_values', + :attr => 'php_admin_values', + :value => { 'open_basedir' => '/srv/web/www.com/:/usr/share/pear/' }, + :match => [/^ php_admin_value open_basedir \/srv\/web\/www.com\/:\/usr\/share\/pear\/$/], + }, + { + :title => 'should accept php_admin_values in directories', + :attr => 'directories', + :value => { + 'path' => '/srv/www', + 'php_admin_values' => { 'open_basedir' => '/srv/web/www.com/:/usr/share/pear/' } + }, + :match => [/^ php_admin_value open_basedir \/srv\/web\/www.com\/:\/usr\/share\/pear\/$/], + }, + { + :title => 'should accept a wsgi script alias', + :attr => 'wsgi_script_aliases', + :value => { '/' => '/var/www/myapp.wsgi'}, + :match => [/^ WSGIScriptAlias \/ \/var\/www\/myapp.wsgi$/], + }, + { + :title => 'should accept multiple wsgi aliases', + :attr => 'wsgi_script_aliases', + :value => { + '/wiki' => '/usr/local/wsgi/scripts/mywiki.wsgi', + '/blog' => '/usr/local/wsgi/scripts/myblog.wsgi', + '/' => '/usr/local/wsgi/scripts/myapp.wsgi', + }, + :match => [ + /^ WSGIScriptAlias \/wiki \/usr\/local\/wsgi\/scripts\/mywiki.wsgi$/, + /^ WSGIScriptAlias \/blog \/usr\/local\/wsgi\/scripts\/myblog.wsgi$/, + /^ WSGIScriptAlias \/ \/usr\/local\/wsgi\/scripts\/myapp.wsgi$/, + ], + }, + { + :title => 'should accept a directory', + :attr => 'directories', + :value => { 'path' => '/opt/app' }, + :notmatch => [' '], + :match => [ + /^ $/, + /^ AllowOverride None$/, + /^ Order allow,deny$/, + /^ Allow from all$/, + /^ <\/Directory>$/, + ], + }, + { + :title => 'should accept directory directives hash', + :attr => 'directories', + :value => { + 'path' => '/opt/app', + 'headers' => 'Set X-Robots-Tag "noindex, noarchive, nosnippet"', + 'allow' => 'from rspec.org', + 'allow_override' => 'Lol', + 'deny' => 'from google.com', + 'options' => '-MultiViews', + 'order' => 'deny,yned', + 'passenger_enabled' => 'onf', + }, + :match => [ + /^ $/, + /^ Header Set X-Robots-Tag "noindex, noarchive, nosnippet"$/, + /^ Allow from rspec.org$/, + /^ AllowOverride Lol$/, + /^ Deny from google.com$/, + /^ Options -MultiViews$/, + /^ Order deny,yned$/, + /^ PassengerEnabled onf$/, + /^ <\/Directory>$/, + ], + }, + { + :title => 'should accept directory directives with arrays and hashes', + :attr => 'directories', + :value => [ + { + 'path' => '/opt/app1', + 'allow' => 'from rspec.org', + 'allow_override' => ['AuthConfig','Indexes'], + 'deny' => 'from google.com', + 'options' => ['-MultiViews','+MultiViews'], + 'order' => ['deny','yned'], + 'passenger_enabled' => 'onf', + }, + { + 'path' => '/opt/app2', + 'addhandlers' => { + 'handler' => 'cgi-script', + 'extensions' => '.cgi', + }, + }, + ], + :match => [ + /^ $/, + /^ Allow from rspec.org$/, + /^ AllowOverride AuthConfig Indexes$/, + /^ Deny from google.com$/, + /^ Options -MultiViews \+MultiViews$/, + /^ Order deny,yned$/, + /^ PassengerEnabled onf$/, + /^ <\/Directory>$/, + /^ $/, + /^ AllowOverride None$/, + /^ Order allow,deny$/, + /^ Allow from all$/, + /^ AddHandler cgi-script .cgi$/, + /^ <\/Directory>$/, + ], + }, + { + :title => 'should accept multiple directories', + :attr => 'directories', + :value => [ + { 'path' => '/opt/app' }, + { 'path' => '/var/www' }, + { 'path' => '/rspec/docroot'} + ], + :match => [ + /^ $/, + /^ $/, + /^ $/, + ], + }, + { + :title => 'should accept location for provider', + :attr => 'directories', + :value => { + 'path' => '/', + 'provider' => 'location', + }, + :notmatch => [' AllowOverride None'], + :match => [ + /^ $/, + /^ Order allow,deny$/, + /^ Allow from all$/, + /^ <\/Location>$/, + ], + }, + { + :title => 'should accept files for provider', + :attr => 'directories', + :value => { + 'path' => 'index.html', + 'provider' => 'files', + }, + :notmatch => [' AllowOverride None'], + :match => [ + /^ $/, + /^ Order allow,deny$/, + /^ Allow from all$/, + /^ <\/Files>$/, + ], + }, + { + :title => 'should contain virtual_docroot', + :attr => 'virtual_docroot', + :value => '/not/default', + :match => [ + /^ VirtualDocumentRoot \/not\/default$/, + ], + }, + { + :title => 'should contain environment variables', + :attr => 'access_log_env_var', + :value => 'admin', + :match => [/CustomLog \/var\/log\/.+_access\.log combined env=admin$/] + }, + + ].each do |param| + describe "when #{param[:attr]} is #{param[:value]}" do + let :params do default_params.merge({ param[:attr].to_sym => param[:value] }) end + + it { should contain_file("25-#{title}.conf").with_mode('0644') } + if param[:match] + it "#{param[:title]}: matches" do + param[:match].each do |match| + should contain_file("25-#{title}.conf").with_content( match ) + end + end + end + if param[:notmatch] + it "#{param[:title]}: notmatches" do + param[:notmatch].each do |notmatch| + should_not contain_file("25-#{title}.conf").with_content( notmatch ) + end + end + end + end + end + end + + context ".conf content with SSL" do + [ + { + :title => 'should accept setting SSLCertificateFile', + :attr => 'ssl_cert', + :value => '/path/to/cert.pem', + :match => [/^ SSLCertificateFile \/path\/to\/cert\.pem$/], + }, + { + :title => 'should accept setting SSLCertificateKeyFile', + :attr => 'ssl_key', + :value => '/path/to/cert.pem', + :match => [/^ SSLCertificateKeyFile \/path\/to\/cert\.pem$/], + }, + { + :title => 'should accept setting SSLCertificateChainFile', + :attr => 'ssl_chain', + :value => '/path/to/cert.pem', + :match => [/^ SSLCertificateChainFile \/path\/to\/cert\.pem$/], + }, + { + :title => 'should accept setting SSLCertificatePath', + :attr => 'ssl_certs_dir', + :value => '/path/to/certs', + :match => [/^ SSLCACertificatePath \/path\/to\/certs$/], + }, + { + :title => 'should accept setting SSLCertificateFile', + :attr => 'ssl_ca', + :value => '/path/to/ca.pem', + :match => [/^ SSLCACertificateFile \/path\/to\/ca\.pem$/], + }, + { + :title => 'should accept setting SSLRevocationPath', + :attr => 'ssl_crl_path', + :value => '/path/to/crl', + :match => [/^ SSLCARevocationPath \/path\/to\/crl$/], + }, + { + :title => 'should accept setting SSLRevocationFile', + :attr => 'ssl_crl', + :value => '/path/to/crl.pem', + :match => [/^ SSLCARevocationFile \/path\/to\/crl\.pem$/], + }, + { + :title => 'should accept setting SSLProxyEngine', + :attr => 'ssl_proxyengine', + :value => true, + :match => [/^ SSLProxyEngine On$/], + }, + { + :title => 'should accept setting SSLProtocol', + :attr => 'ssl_protocol', + :value => 'all -SSLv2', + :match => [/^ SSLProtocol all -SSLv2$/], + }, + { + :title => 'should accept setting SSLCipherSuite', + :attr => 'ssl_cipher', + :value => 'RC4-SHA:HIGH:!ADH:!SSLv2', + :match => [/^ SSLCipherSuite RC4-SHA:HIGH:!ADH:!SSLv2$/], + }, + { + :title => 'should accept setting SSLHonorCipherOrder', + :attr => 'ssl_honorcipherorder', + :value => 'On', + :match => [/^ SSLHonorCipherOrder On$/], + }, + { + :title => 'should accept setting SSLVerifyClient', + :attr => 'ssl_verify_client', + :value => 'optional', + :match => [/^ SSLVerifyClient optional$/], + }, + { + :title => 'should accept setting SSLVerifyDepth', + :attr => 'ssl_verify_depth', + :value => '1', + :match => [/^ SSLVerifyDepth 1$/], + }, + { + :title => 'should accept setting SSLOptions with a string', + :attr => 'ssl_options', + :value => '+ExportCertData', + :match => [/^ SSLOptions \+ExportCertData$/], + }, + { + :title => 'should accept setting SSLOptions with an array', + :attr => 'ssl_options', + :value => ['+StrictRequire','+ExportCertData'], + :match => [/^ SSLOptions \+StrictRequire \+ExportCertData/], + }, + { + :title => 'should accept setting SSLOptions with a string in directories', + :attr => 'directories', + :value => { 'path' => '/srv/www', 'ssl_options' => '+ExportCertData'}, + :match => [/^ SSLOptions \+ExportCertData$/], + }, + { + :title => 'should accept setting SSLOptions with an array in directories', + :attr => 'directories', + :value => { 'path' => '/srv/www', 'ssl_options' => ['-StdEnvVars','+ExportCertData']}, + :match => [/^ SSLOptions -StdEnvVars \+ExportCertData/], + }, + ].each do |param| + describe "when #{param[:attr]} is #{param[:value]} with SSL" do + let :params do + default_params.merge( { + param[:attr].to_sym => param[:value], + :ssl => true, + } ) + end + it { should contain_file("25-#{title}.conf").with_mode('0644') } + if param[:match] + it "#{param[:title]}: matches" do + param[:match].each do |match| + should contain_file("25-#{title}.conf").with_content( match ) + end + end + end + if param[:notmatch] + it "#{param[:title]}: notmatches" do + param[:notmatch].each do |notmatch| + should_not contain_file("25-#{title}.conf").with_content( notmatch ) + end + end + end + end + end + end + + context 'attribute resources' do + describe 'when access_log_file and access_log_pipe are specified' do + let :params do default_params.merge({ + :access_log_file => 'fake.log', + :access_log_pipe => '| /bin/fake', + }) end + it 'should cause a failure' do + expect { subject }.to raise_error(Puppet::Error, /'access_log_file' and 'access_log_pipe' cannot be defined at the same time/) + end + end + describe 'when error_log_file and error_log_pipe are specified' do + let :params do default_params.merge({ + :error_log_file => 'fake.log', + :error_log_pipe => '| /bin/fake', + }) end + it 'should cause a failure' do + expect { subject }.to raise_error(Puppet::Error, /'error_log_file' and 'error_log_pipe' cannot be defined at the same time/) + end + end + describe 'when docroot owner is specified' do + let :params do default_params.merge({ + :docroot_owner => 'testuser', + :docroot_group => 'testgroup', + }) end + it 'should set vhost ownership' do + should contain_file(params[:docroot]).with({ + :ensure => :directory, + :owner => 'testuser', + :group => 'testgroup', + }) + end + end + + describe 'when wsgi_daemon_process and wsgi_daemon_process_options are specified' do + let :params do default_params.merge({ + :wsgi_daemon_process => 'example.org', + :wsgi_daemon_process_options => { 'processes' => '2', 'threads' => '15' }, + }) end + it 'should set wsgi_daemon_process_options' do + should contain_file("25-#{title}.conf").with_content( + /^ WSGIDaemonProcess example.org processes=2 threads=15$/ + ) + end + end + + describe 'when rewrites are specified' do + let :params do default_params.merge({ + :rewrites => [ + { + 'comment' => 'test rewrites', + 'rewrite_cond' => ['%{HTTP_USER_AGENT} ^Lynx/ [OR]', '%{HTTP_USER_AGENT} ^Mozilla/[12]'], + 'rewrite_rule' => ['^index\.html$ welcome.html', '^index\.cgi$ index.php'], + } + ] + }) end + it 'should set RewriteConds and RewriteRules' do + should contain_file("25-#{title}.conf").with_content( + /^ #test rewrites$/ + ) + should contain_file("25-#{title}.conf").with_content( + /^ RewriteCond %\{HTTP_USER_AGENT\} \^Lynx\/ \[OR\]$/ + ) + should contain_file("25-#{title}.conf").with_content( + /^ RewriteCond %\{HTTP_USER_AGENT\} \^Mozilla\/\[12\]$/ + ) + should contain_file("25-#{title}.conf").with_content( + /^ RewriteRule \^index\\.html\$ welcome.html$/ + ) + should contain_file("25-#{title}.conf").with_content( + /^ RewriteRule \^index\\.cgi\$ index.php$/ + ) + end + end + + describe 'when rewrite_rule and rewrite_cond are specified' do + let :params do default_params.merge({ + :rewrite_cond => '%{HTTPS} off', + :rewrite_rule => '(.*) https://%{HTTPS_HOST}%{REQUEST_URI}', + }) end + it 'should set RewriteCond' do + should contain_file("25-#{title}.conf").with_content( + /^ RewriteCond %\{HTTPS\} off$/ + ) + end + end + + describe 'when suphp_engine is on and suphp_configpath is specified' do + let :params do default_params.merge({ + :suphp_engine => 'on', + :suphp_configpath => '/etc/php5/apache2', + }) end + it 'should set suphp_configpath' do + should contain_file("25-#{title}.conf").with_content( + /^ suPHP_ConfigPath \/etc\/php5\/apache2$/ + ) + end + end + + describe 'when suphp_engine is on and suphp_addhandler is specified' do + let :params do default_params.merge({ + :suphp_engine => 'on', + :suphp_addhandler => 'x-httpd-php', + }) end + it 'should set suphp_addhandler' do + should contain_file("25-#{title}.conf").with_content( + /^ suPHP_AddHandler x-httpd-php/ + ) + end + end + + describe 'when suphp_engine is on and suphp { user & group } is specified' do + let :params do default_params.merge({ + :suphp_engine => 'on', + :directories => { 'path' => '/srv/www', + 'suphp' => { 'user' => 'myappuser', 'group' => 'myappgroup' }, + } + }) end + it 'should set suphp_UserGroup' do + should contain_file("25-#{title}.conf").with_content( + /^ suPHP_UserGroup myappuser myappgroup/ + ) + end + end + + describe 'priority/default settings' do + describe 'when neither priority/default is specified' do + let :params do default_params end + it { should contain_file("25-#{title}.conf").with_path( + /25-#{title}.conf/ + ) } + end + describe 'when both priority/default_vhost is specified' do + let :params do + default_params.merge({ + :priority => 15, + :default_vhost => true, + }) + end + it { should contain_file("15-#{title}.conf").with_path( + /15-#{title}.conf/ + ) } + end + describe 'when only priority is specified' do + let :params do + default_params.merge({ :priority => 14, }) + end + it { should contain_file("14-#{title}.conf").with_path( + /14-#{title}.conf/ + ) } + end + describe 'when only default is specified' do + let :params do + default_params.merge({ :default_vhost => true, }) + end + it { should contain_file("10-#{title}.conf").with_path( + /10-#{title}.conf/ + ) } + end + end + + describe 'various ip/port combos' do + describe 'when ip_based is true' do + let :params do default_params.merge({ :ip_based => true }) end + it 'should not specify a NameVirtualHost' do + should contain_apache__listen(params[:port]) + should_not contain_apache__namevirtualhost("*:#{params[:port]}") + end + end + + describe 'when ip_based is default' do + let :params do default_params end + it 'should specify a NameVirtualHost' do + should contain_apache__listen(params[:port]) + should contain_apache__namevirtualhost("*:#{params[:port]}") + end + end + + describe 'when an ip is set' do + let :params do default_params.merge({ :ip => '10.0.0.1' }) end + it 'should specify a NameVirtualHost for the ip' do + should_not contain_apache__listen(params[:port]) + should contain_apache__listen("10.0.0.1:#{params[:port]}") + should contain_apache__namevirtualhost("10.0.0.1:#{params[:port]}") + end + end + + describe 'an ip_based vhost without a port' do + let :params do + { + :docroot => '/fake', + :ip => '10.0.0.1', + :ip_based => true, + } + end + it 'should specify a NameVirtualHost for the ip' do + should_not contain_apache__listen(params[:ip]) + should_not contain_apache__namevirtualhost(params[:ip]) + should contain_file("25-#{title}.conf").with_content %r{} + end + end + end + + describe 'redirect rules' do + describe 'without lockstep arrays' do + let :params do + default_params.merge({ + :redirect_source => [ + '/login', + '/logout', + ], + :redirect_dest => [ + 'http://10.0.0.10/login', + 'http://10.0.0.10/logout', + ], + :redirect_status => [ + 'permanent', + '', + ], + }) + end + + it { should contain_file("25-#{title}.conf").with_content %r{ Redirect permanent /login http://10\.0\.0\.10/login} } + it { should contain_file("25-#{title}.conf").with_content %r{ Redirect /logout http://10\.0\.0\.10/logout} } + end + describe 'redirect match rules' do + let :params do + default_params.merge({ + :redirectmatch_status => [ + '404', + ], + :redirectmatch_regexp => [ + '/\.git(/.*|$)', + ], + }) + end + + it { should contain_file("25-#{title}.conf").with_content %r{ RedirectMatch 404 } } + end + describe 'without a status' do + let :params do + default_params.merge({ + :redirect_source => [ + '/login', + '/logout', + ], + :redirect_dest => [ + 'http://10.0.0.10/login', + 'http://10.0.0.10/logout', + ], + }) + end + + it { should contain_file("25-#{title}.conf").with_content %r{ Redirect /login http://10\.0\.0\.10/login} } + it { should contain_file("25-#{title}.conf").with_content %r{ Redirect /logout http://10\.0\.0\.10/logout} } + end + describe 'with a single status and dest' do + let :params do + default_params.merge({ + :redirect_source => [ + '/login', + '/logout', + ], + :redirect_dest => 'http://10.0.0.10/test', + :redirect_status => 'permanent', + }) + end + + it { should contain_file("25-#{title}.conf").with_content %r{ Redirect permanent /login http://10\.0\.0\.10/test} } + it { should contain_file("25-#{title}.conf").with_content %r{ Redirect permanent /logout http://10\.0\.0\.10/test} } + end + + describe 'with a directoryindex specified' do + let :params do + default_params.merge({ + :directoryindex => 'index.php' + }) + end + it { should contain_file("25-#{title}.conf").with_content %r{DirectoryIndex index.php} } + end + end + end + end +end diff --git a/apache/spec/fixtures/modules/site_apache/templates/fake.conf.erb b/apache/spec/fixtures/modules/site_apache/templates/fake.conf.erb new file mode 100644 index 000000000..019debfe4 --- /dev/null +++ b/apache/spec/fixtures/modules/site_apache/templates/fake.conf.erb @@ -0,0 +1 @@ +Fake template for rspec. diff --git a/apache/spec/spec.opts b/apache/spec/spec.opts new file mode 100644 index 000000000..de653df4b --- /dev/null +++ b/apache/spec/spec.opts @@ -0,0 +1,4 @@ +--format s +--colour +--loadby mtime +--backtrace diff --git a/apache/spec/spec_helper.rb b/apache/spec/spec_helper.rb new file mode 100644 index 000000000..2c6f56649 --- /dev/null +++ b/apache/spec/spec_helper.rb @@ -0,0 +1 @@ +require 'puppetlabs_spec_helper/module_spec_helper' diff --git a/apache/spec/spec_helper_acceptance.rb b/apache/spec/spec_helper_acceptance.rb new file mode 100644 index 000000000..176529fdb --- /dev/null +++ b/apache/spec/spec_helper_acceptance.rb @@ -0,0 +1,38 @@ +require 'beaker-rspec/spec_helper' +require 'beaker-rspec/helpers/serverspec' + +hosts.each do |host| + if host['platform'] =~ /debian/ + on host, 'echo \'export PATH=/var/lib/gems/1.8/bin/:${PATH}\' >> ~/.bashrc' + end + if host.is_pe? + install_pe + else + # Install Puppet + install_package host, 'rubygems' + on host, 'gem install puppet --no-ri --no-rdoc' + on host, "mkdir -p #{host['distmoduledir']}" + end +end + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Readable test descriptions + c.formatter = :documentation + + # Configure all nodes in nodeset + c.before :suite do + # Install module and dependencies + puppet_module_install(:source => proj_root, :module_name => 'apache') + hosts.each do |host| + # Required for mod_passenger tests. + if fact('osfamily') == 'RedHat' + on host, puppet('module','install','stahnma/epel'), { :acceptable_exit_codes => [0,1] } + end + on host, puppet('module','install','puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] } + on host, puppet('module','install','puppetlabs-concat'), { :acceptable_exit_codes => [0,1] } + end + end +end diff --git a/apache/spec/unit/provider/a2mod/gentoo_spec.rb b/apache/spec/unit/provider/a2mod/gentoo_spec.rb new file mode 100644 index 000000000..ddb9dddda --- /dev/null +++ b/apache/spec/unit/provider/a2mod/gentoo_spec.rb @@ -0,0 +1,184 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +provider_class = Puppet::Type.type(:a2mod).provider(:gentoo) + +describe provider_class do + before :each do + provider_class.clear + end + + [:conf_file, :instances, :modules, :initvars, :conf_file, :clear].each do |method| + it "should respond to the class method #{method}" do + provider_class.should respond_to(method) + end + end + + describe "when fetching modules" do + before do + @filetype = mock() + end + + it "should return a sorted array of the defined parameters" do + @filetype.expects(:read).returns(%Q{APACHE2_OPTS="-D FOO -D BAR -D BAZ"\n}) + provider_class.expects(:filetype).returns(@filetype) + + provider_class.modules.should == %w{bar baz foo} + end + + it "should cache the module list" do + @filetype.expects(:read).once.returns(%Q{APACHE2_OPTS="-D FOO -D BAR -D BAZ"\n}) + provider_class.expects(:filetype).once.returns(@filetype) + + 2.times { provider_class.modules.should == %w{bar baz foo} } + end + + it "should normalize parameters" do + @filetype.expects(:read).returns(%Q{APACHE2_OPTS="-D FOO -D BAR -D BAR"\n}) + provider_class.expects(:filetype).returns(@filetype) + + provider_class.modules.should == %w{bar foo} + end + end + + describe "when prefetching" do + it "should match providers to resources" do + provider = mock("ssl_provider", :name => "ssl") + resource = mock("ssl_resource") + resource.expects(:provider=).with(provider) + + provider_class.expects(:instances).returns([provider]) + provider_class.prefetch("ssl" => resource) + end + end + + describe "when flushing" do + before :each do + @filetype = mock() + @filetype.stubs(:backup) + provider_class.expects(:filetype).at_least_once.returns(@filetype) + + @info = mock() + @info.stubs(:[]).with(:name).returns("info") + @info.stubs(:provider=) + + @mpm = mock() + @mpm.stubs(:[]).with(:name).returns("mpm") + @mpm.stubs(:provider=) + + @ssl = mock() + @ssl.stubs(:[]).with(:name).returns("ssl") + @ssl.stubs(:provider=) + end + + it "should add modules whose ensure is present" do + @filetype.expects(:read).at_least_once.returns(%Q{APACHE2_OPTS=""}) + @filetype.expects(:write).with(%Q{APACHE2_OPTS="-D INFO"}) + + @info.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("info" => @info) + + provider_class.flush + end + + it "should remove modules whose ensure is present" do + @filetype.expects(:read).at_least_once.returns(%Q{APACHE2_OPTS="-D INFO"}) + @filetype.expects(:write).with(%Q{APACHE2_OPTS=""}) + + @info.stubs(:should).with(:ensure).returns(:absent) + @info.stubs(:provider=) + provider_class.prefetch("info" => @info) + + provider_class.flush + end + + it "should not modify providers without resources" do + @filetype.expects(:read).at_least_once.returns(%Q{APACHE2_OPTS="-D INFO -D MPM"}) + @filetype.expects(:write).with(%Q{APACHE2_OPTS="-D MPM -D SSL"}) + + @info.stubs(:should).with(:ensure).returns(:absent) + provider_class.prefetch("info" => @info) + + @ssl.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("ssl" => @ssl) + + provider_class.flush + end + + it "should write the modules in sorted order" do + @filetype.expects(:read).at_least_once.returns(%Q{APACHE2_OPTS=""}) + @filetype.expects(:write).with(%Q{APACHE2_OPTS="-D INFO -D MPM -D SSL"}) + + @mpm.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("mpm" => @mpm) + @info.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("info" => @info) + @ssl.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("ssl" => @ssl) + + provider_class.flush + end + + it "should write the records back once" do + @filetype.expects(:read).at_least_once.returns(%Q{APACHE2_OPTS=""}) + @filetype.expects(:write).once.with(%Q{APACHE2_OPTS="-D INFO -D SSL"}) + + @info.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("info" => @info) + + @ssl.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("ssl" => @ssl) + + provider_class.flush + end + + it "should only modify the line containing APACHE2_OPTS" do + @filetype.expects(:read).at_least_once.returns(%Q{# Comment\nAPACHE2_OPTS=""\n# Another comment}) + @filetype.expects(:write).once.with(%Q{# Comment\nAPACHE2_OPTS="-D INFO"\n# Another comment}) + + @info.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("info" => @info) + provider_class.flush + end + + it "should restore any arbitrary arguments" do + @filetype.expects(:read).at_least_once.returns(%Q{APACHE2_OPTS="-Y -D MPM -X"}) + @filetype.expects(:write).once.with(%Q{APACHE2_OPTS="-Y -X -D INFO -D MPM"}) + + @info.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("info" => @info) + provider_class.flush + end + + it "should backup the file once if changes were made" do + @filetype.expects(:read).at_least_once.returns(%Q{APACHE2_OPTS=""}) + @filetype.expects(:write).once.with(%Q{APACHE2_OPTS="-D INFO -D SSL"}) + + @info.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("info" => @info) + + @ssl.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("ssl" => @ssl) + + @filetype.unstub(:backup) + @filetype.expects(:backup) + provider_class.flush + end + + it "should not write the file or run backups if no changes were made" do + @filetype.expects(:read).at_least_once.returns(%Q{APACHE2_OPTS="-X -D INFO -D SSL -Y"}) + @filetype.expects(:write).never + + @info.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("info" => @info) + + @ssl.stubs(:should).with(:ensure).returns(:present) + provider_class.prefetch("ssl" => @ssl) + + @filetype.unstub(:backup) + @filetype.expects(:backup).never + provider_class.flush + end + end +end diff --git a/apache/templates/confd/no-accf.conf.erb b/apache/templates/confd/no-accf.conf.erb new file mode 100644 index 000000000..10e51644c --- /dev/null +++ b/apache/templates/confd/no-accf.conf.erb @@ -0,0 +1,4 @@ + + AcceptFilter http none + AcceptFilter https none + diff --git a/apache/templates/httpd.conf.erb b/apache/templates/httpd.conf.erb new file mode 100644 index 000000000..469836ec3 --- /dev/null +++ b/apache/templates/httpd.conf.erb @@ -0,0 +1,92 @@ +# Security +ServerTokens <%= @server_tokens %> +ServerSignature <%= @server_signature %> +TraceEnable <%= @trace_enable %> + +ServerName "<%= @servername %>" +ServerRoot "<%= @server_root %>" +PidFile <%= @pidfile %> +Timeout <%= @timeout %> +KeepAlive <%= @keepalive %> +MaxKeepAliveRequests 100 +KeepAliveTimeout <%= @keepalive_timeout %> + +User <%= @user %> +Group <%= @group %> + +AccessFileName .htaccess + + Order allow,deny + Deny from all + Satisfy all + + + + Options FollowSymLinks + AllowOverride None + + +DefaultType none +HostnameLookups Off +ErrorLog <%= @logroot %>/<%= @error_log %> +LogLevel <%= @log_level %> +EnableSendfile <%= @sendfile %> + +#Listen 80 + +<% if @apxs_workaround -%> +# Workaround: without this hack apxs would be confused about where to put +# LoadModule directives and fail entire procedure of apache package +# installation/reinstallation. This problem was observed on FreeBSD (apache22). +#LoadModule fake_module libexec/apache22/mod_fake.so +<% end -%> + +Include <%= @mod_load_dir %>/*.load +<% if @mod_load_dir != @confd_dir and @mod_load_dir != @vhost_load_dir -%> +Include <%= @mod_load_dir %>/*.conf +<% end -%> +Include <%= @ports_file %> + +LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined +LogFormat "%h %l %u %t \"%r\" %>s %b" common +LogFormat "%{Referer}i -> %U" referer +LogFormat "%{User-agent}i" agent + +Include <%= @confd_dir %>/*.conf +<% if @vhost_load_dir != @confd_dir -%> +Include <%= @vhost_load_dir %>/*.conf +<% end -%> + +<% if @error_documents -%> +# /usr/share/apache2/error on debian +Alias /error/ "<%= @error_documents_path %>/" + +"> + AllowOverride None + Options IncludesNoExec + AddOutputFilter Includes html + AddHandler type-map var + Order allow,deny + Allow from all + LanguagePriority en cs de es fr it nl sv pt-br ro + ForceLanguagePriority Prefer Fallback + + +ErrorDocument 400 /error/HTTP_BAD_REQUEST.html.var +ErrorDocument 401 /error/HTTP_UNAUTHORIZED.html.var +ErrorDocument 403 /error/HTTP_FORBIDDEN.html.var +ErrorDocument 404 /error/HTTP_NOT_FOUND.html.var +ErrorDocument 405 /error/HTTP_METHOD_NOT_ALLOWED.html.var +ErrorDocument 408 /error/HTTP_REQUEST_TIME_OUT.html.var +ErrorDocument 410 /error/HTTP_GONE.html.var +ErrorDocument 411 /error/HTTP_LENGTH_REQUIRED.html.var +ErrorDocument 412 /error/HTTP_PRECONDITION_FAILED.html.var +ErrorDocument 413 /error/HTTP_REQUEST_ENTITY_TOO_LARGE.html.var +ErrorDocument 414 /error/HTTP_REQUEST_URI_TOO_LARGE.html.var +ErrorDocument 415 /error/HTTP_UNSUPPORTED_MEDIA_TYPE.html.var +ErrorDocument 500 /error/HTTP_INTERNAL_SERVER_ERROR.html.var +ErrorDocument 501 /error/HTTP_NOT_IMPLEMENTED.html.var +ErrorDocument 502 /error/HTTP_BAD_GATEWAY.html.var +ErrorDocument 503 /error/HTTP_SERVICE_UNAVAILABLE.html.var +ErrorDocument 506 /error/HTTP_VARIANT_ALSO_VARIES.html.var +<% end -%> diff --git a/apache/templates/listen.erb b/apache/templates/listen.erb new file mode 100644 index 000000000..8fc871b0a --- /dev/null +++ b/apache/templates/listen.erb @@ -0,0 +1,6 @@ +<%# Listen should always be one of: + - + - : + - [ +-%> +Listen <%= @listen_addr_port %> diff --git a/apache/templates/mod/alias.conf.erb b/apache/templates/mod/alias.conf.erb new file mode 100644 index 000000000..52f16c171 --- /dev/null +++ b/apache/templates/mod/alias.conf.erb @@ -0,0 +1,9 @@ + +Alias /icons/ "<%= @icons_path %>/" +"> + Options Indexes MultiViews + AllowOverride None + Order allow,deny + Allow from all + + diff --git a/apache/templates/mod/authnz_ldap.conf.erb b/apache/templates/mod/authnz_ldap.conf.erb new file mode 100644 index 000000000..565fcf0df --- /dev/null +++ b/apache/templates/mod/authnz_ldap.conf.erb @@ -0,0 +1,5 @@ +<% if @verifyServerCert == true -%> +LDAPVerifyServerCert On +<% else -%> +LDAPVerifyServerCert Off +<% end -%> diff --git a/apache/templates/mod/autoindex.conf.erb b/apache/templates/mod/autoindex.conf.erb new file mode 100644 index 000000000..ef6bbebea --- /dev/null +++ b/apache/templates/mod/autoindex.conf.erb @@ -0,0 +1,56 @@ +IndexOptions FancyIndexing VersionSort HTMLTable NameWidth=* DescriptionWidth=* Charset=UTF-8 +AddIconByEncoding (CMP,/icons/compressed.gif) x-compress x-gzip x-bzip2 + +AddIconByType (TXT,/icons/text.gif) text/* +AddIconByType (IMG,/icons/image2.gif) image/* +AddIconByType (SND,/icons/sound2.gif) audio/* +AddIconByType (VID,/icons/movie.gif) video/* + +AddIcon /icons/binary.gif .bin .exe +AddIcon /icons/binhex.gif .hqx +AddIcon /icons/tar.gif .tar +AddIcon /icons/world2.gif .wrl .wrl.gz .vrml .vrm .iv +AddIcon /icons/compressed.gif .Z .z .tgz .gz .zip +AddIcon /icons/a.gif .ps .ai .eps +AddIcon /icons/layout.gif .html .shtml .htm .pdf +AddIcon /icons/text.gif .txt +AddIcon /icons/c.gif .c +AddIcon /icons/p.gif .pl .py +AddIcon /icons/f.gif .for +AddIcon /icons/dvi.gif .dvi +AddIcon /icons/uuencoded.gif .uu +AddIcon /icons/script.gif .conf .sh .shar .csh .ksh .tcl +AddIcon /icons/tex.gif .tex +AddIcon /icons/bomb.gif /core +AddIcon (SND,/icons/sound2.gif) .ogg +AddIcon (VID,/icons/movie.gif) .ogm + +AddIcon /icons/back.gif .. +AddIcon /icons/hand.right.gif README +AddIcon /icons/folder.gif ^^DIRECTORY^^ +AddIcon /icons/blank.gif ^^BLANKICON^^ + +AddIcon /icons/odf6odt-20x22.png .odt +AddIcon /icons/odf6ods-20x22.png .ods +AddIcon /icons/odf6odp-20x22.png .odp +AddIcon /icons/odf6odg-20x22.png .odg +AddIcon /icons/odf6odc-20x22.png .odc +AddIcon /icons/odf6odf-20x22.png .odf +AddIcon /icons/odf6odb-20x22.png .odb +AddIcon /icons/odf6odi-20x22.png .odi +AddIcon /icons/odf6odm-20x22.png .odm + +AddIcon /icons/odf6ott-20x22.png .ott +AddIcon /icons/odf6ots-20x22.png .ots +AddIcon /icons/odf6otp-20x22.png .otp +AddIcon /icons/odf6otg-20x22.png .otg +AddIcon /icons/odf6otc-20x22.png .otc +AddIcon /icons/odf6otf-20x22.png .otf +AddIcon /icons/odf6oti-20x22.png .oti +AddIcon /icons/odf6oth-20x22.png .oth + +DefaultIcon /icons/unknown.gif +ReadmeName README.html +HeaderName HEADER.html + +IndexIgnore .??* *~ *# HEADER* README* RCS CVS *,v *,t diff --git a/apache/templates/mod/cgid.conf.erb b/apache/templates/mod/cgid.conf.erb new file mode 100644 index 000000000..d771012de --- /dev/null +++ b/apache/templates/mod/cgid.conf.erb @@ -0,0 +1 @@ +ScriptSock <%= @cgisock_path %> diff --git a/apache/templates/mod/dav_fs.conf.erb b/apache/templates/mod/dav_fs.conf.erb new file mode 100644 index 000000000..50edf004e --- /dev/null +++ b/apache/templates/mod/dav_fs.conf.erb @@ -0,0 +1 @@ +DAVLockDB <%= @dav_lock %> diff --git a/apache/templates/mod/deflate.conf.erb b/apache/templates/mod/deflate.conf.erb new file mode 100644 index 000000000..d0997dfeb --- /dev/null +++ b/apache/templates/mod/deflate.conf.erb @@ -0,0 +1,4 @@ +AddOutputFilterByType DEFLATE text/html text/plain text/xml +AddOutputFilterByType DEFLATE text/css +AddOutputFilterByType DEFLATE application/x-javascript application/javascript application/ecmascript +AddOutputFilterByType DEFLATE application/rss+xml diff --git a/apache/templates/mod/dir.conf.erb b/apache/templates/mod/dir.conf.erb new file mode 100644 index 000000000..741f6ae03 --- /dev/null +++ b/apache/templates/mod/dir.conf.erb @@ -0,0 +1 @@ +DirectoryIndex <%= @indexes.join(' ') %> diff --git a/apache/templates/mod/disk_cache.conf.erb b/apache/templates/mod/disk_cache.conf.erb new file mode 100644 index 000000000..0c7e2c4b7 --- /dev/null +++ b/apache/templates/mod/disk_cache.conf.erb @@ -0,0 +1,8 @@ + + + CacheEnable disk / + CacheRoot "<%= @cache_root %>" + CacheDirLevels 2 + CacheDirLength 1 + + diff --git a/apache/templates/mod/event.conf.erb b/apache/templates/mod/event.conf.erb new file mode 100644 index 000000000..40099543d --- /dev/null +++ b/apache/templates/mod/event.conf.erb @@ -0,0 +1,9 @@ + + ServerLimit <%= @serverlimit %> + StartServers <%= @startservers %> + MaxClients <%= @maxclients %> + MinSpareThreads <%= @minsparethreads %> + MaxSpareThreads <%= @maxsparethreads %> + ThreadsPerChild <%= @threadsperchild %> + MaxRequestsPerChild <%= @maxrequestsperchild %> + diff --git a/apache/templates/mod/fastcgi.conf.erb b/apache/templates/mod/fastcgi.conf.erb new file mode 100644 index 000000000..d02b12f77 --- /dev/null +++ b/apache/templates/mod/fastcgi.conf.erb @@ -0,0 +1,6 @@ +# The Fastcgi Apache module configuration file is being +# managed by Puppet and changes will be overwritten. + + AddHandler fastcgi-script .fcgi + FastCgiIpcDir <%= @fastcgi_lib_path %> + diff --git a/apache/templates/mod/info.conf.erb b/apache/templates/mod/info.conf.erb new file mode 100644 index 000000000..01ffe95a9 --- /dev/null +++ b/apache/templates/mod/info.conf.erb @@ -0,0 +1,6 @@ + + SetHandler server-info + Order deny,allow + Deny from all + Allow from <%= Array(@allow_from).join(" ") %> + diff --git a/apache/templates/mod/itk.conf.erb b/apache/templates/mod/itk.conf.erb new file mode 100644 index 000000000..f45f2b35d --- /dev/null +++ b/apache/templates/mod/itk.conf.erb @@ -0,0 +1,8 @@ + + StartServers <%= @startservers %> + MinSpareServers <%= @minspareservers %> + MaxSpareServers <%= @maxspareservers %> + ServerLimit <%= @serverlimit %> + MaxClients <%= @maxclients %> + MaxRequestsPerChild <%= @maxrequestsperchild %> + diff --git a/apache/templates/mod/ldap.conf.erb b/apache/templates/mod/ldap.conf.erb new file mode 100644 index 000000000..14f33ab2b --- /dev/null +++ b/apache/templates/mod/ldap.conf.erb @@ -0,0 +1,7 @@ + + SetHandler ldap-status + Order deny,allow + Deny from all + Allow from 127.0.0.1 ::1 + Satisfy all + diff --git a/apache/templates/mod/mime.conf.erb b/apache/templates/mod/mime.conf.erb new file mode 100644 index 000000000..a69a424a6 --- /dev/null +++ b/apache/templates/mod/mime.conf.erb @@ -0,0 +1,36 @@ +TypesConfig <%= @mime_types_config %> + +AddType application/x-compress .Z +AddType application/x-gzip .gz .tgz +AddType application/x-bzip2 .bz2 + +AddLanguage ca .ca +AddLanguage cs .cz .cs +AddLanguage da .dk +AddLanguage de .de +AddLanguage el .el +AddLanguage en .en +AddLanguage eo .eo +AddLanguage es .es +AddLanguage et .et +AddLanguage fr .fr +AddLanguage he .he +AddLanguage hr .hr +AddLanguage it .it +AddLanguage ja .ja +AddLanguage ko .ko +AddLanguage ltz .ltz +AddLanguage nl .nl +AddLanguage nn .nn +AddLanguage no .no +AddLanguage pl .po +AddLanguage pt .pt +AddLanguage pt-BR .pt-br +AddLanguage ru .ru +AddLanguage sv .sv +AddLanguage zh-CN .zh-cn +AddLanguage zh-TW .zh-tw + +AddHandler type-map var +AddType text/html .shtml +AddOutputFilter INCLUDES .shtml diff --git a/apache/templates/mod/mime_magic.conf.erb b/apache/templates/mod/mime_magic.conf.erb new file mode 100644 index 000000000..4826a765c --- /dev/null +++ b/apache/templates/mod/mime_magic.conf.erb @@ -0,0 +1 @@ +MIMEMagicFile <%= @magic_file %> diff --git a/apache/templates/mod/mpm_event.conf.erb b/apache/templates/mod/mpm_event.conf.erb new file mode 100644 index 000000000..eb6f1ff5f --- /dev/null +++ b/apache/templates/mod/mpm_event.conf.erb @@ -0,0 +1,9 @@ + + StartServers 2 + MinSpareThreads 25 + MaxSpareThreads 75 + ThreadLimit 64 + ThreadsPerChild 25 + MaxClients 150 + MaxRequestsPerChild 0 + diff --git a/apache/templates/mod/negotiation.conf.erb b/apache/templates/mod/negotiation.conf.erb new file mode 100644 index 000000000..50921019b --- /dev/null +++ b/apache/templates/mod/negotiation.conf.erb @@ -0,0 +1,2 @@ +LanguagePriority en ca cs da de el eo es et fr he hr it ja ko ltz nl nn no pl pt pt-BR ru sv zh-CN zh-TW +ForceLanguagePriority Prefer Fallback diff --git a/apache/templates/mod/nss.conf.erb b/apache/templates/mod/nss.conf.erb new file mode 100644 index 000000000..8fd538b76 --- /dev/null +++ b/apache/templates/mod/nss.conf.erb @@ -0,0 +1,228 @@ +# +# This is the Apache server configuration file providing SSL support using. +# the mod_nss plugin. It contains the configuration directives to instruct +# the server how to serve pages over an https connection. +# +# Do NOT simply read the instructions in here without understanding +# what they do. They're here only as hints or reminders. If you are unsure +# consult the online docs. You have been warned. +# + +#LoadModule nss_module modules/libmodnss.so + +# +# When we also provide SSL we have to listen to the +# standard HTTP port (see above) and to the HTTPS port +# +# Note: Configurations that use IPv6 but not IPv4-mapped addresses need two +# Listen directives: "Listen [::]:8443" and "Listen 0.0.0.0:443" +# +Listen 8443 + +## +## SSL Global Context +## +## All SSL configuration in this context applies both to +## the main server and all SSL-enabled virtual hosts. +## + +# +# Some MIME-types for downloading Certificates and CRLs +# +AddType application/x-x509-ca-cert .crt +AddType application/x-pkcs7-crl .crl + +# Pass Phrase Dialog: +# Configure the pass phrase gathering process. +# The filtering dialog program (`builtin' is a internal +# terminal dialog) has to provide the pass phrase on stdout. +<% if @passwd_file -%> +NSSPassPhraseDialog file:<%= @passwd_file %> +<% else -%> +NSSPassPhraseDialog builtin +<% end -%> + +# Pass Phrase Helper: +# This helper program stores the token password pins between +# restarts of Apache. +NSSPassPhraseHelper /usr/sbin/nss_pcache + +# Configure the SSL Session Cache. +# NSSSessionCacheSize is the number of entries in the cache. +# NSSSessionCacheTimeout is the SSL2 session timeout (in seconds). +# NSSSession3CacheTimeout is the SSL3/TLS session timeout (in seconds). +NSSSessionCacheSize 10000 +NSSSessionCacheTimeout 100 +NSSSession3CacheTimeout 86400 + +# +# Pseudo Random Number Generator (PRNG): +# Configure one or more sources to seed the PRNG of the SSL library. +# The seed data should be of good random quality. +# WARNING! On some platforms /dev/random blocks if not enough entropy +# is available. Those platforms usually also provide a non-blocking +# device, /dev/urandom, which may be used instead. +# +# This does not support seeding the RNG with each connection. + +NSSRandomSeed startup builtin +#NSSRandomSeed startup file:/dev/random 512 +#NSSRandomSeed startup file:/dev/urandom 512 + +# +# TLS Negotiation configuration under RFC 5746 +# +# Only renegotiate if the peer's hello bears the TLS renegotiation_info +# extension. Default off. +NSSRenegotiation off + +# Peer must send Signaling Cipher Suite Value (SCSV) or +# Renegotiation Info (RI) extension in ALL handshakes. Default: off +NSSRequireSafeNegotiation off + +## +## SSL Virtual Host Context +## + + + +# General setup for the virtual host +#DocumentRoot "/etc/httpd/htdocs" +#ServerName www.example.com:8443 +#ServerAdmin you@example.com + +# mod_nss can log to separate log files, you can choose to do that if you'd like +# LogLevel is not inherited from httpd.conf. +ErrorLog <%= @error_log %> +TransferLog <%= @transfer_log %> +LogLevel warn + +# SSL Engine Switch: +# Enable/Disable SSL for this virtual host. +NSSEngine on + +# SSL Cipher Suite: +# List the ciphers that the client is permitted to negotiate. +# See the mod_nss documentation for a complete list. + +# SSL 3 ciphers. SSL 2 is disabled by default. +NSSCipherSuite +rsa_rc4_128_md5,+rsa_rc4_128_sha,+rsa_3des_sha,-rsa_des_sha,-rsa_rc4_40_md5,-rsa_rc2_40_md5,-rsa_null_md5,-rsa_null_sha,+fips_3des_sha,-fips_des_sha,-fortezza,-fortezza_rc4_128_sha,-fortezza_null,-rsa_des_56_sha,-rsa_rc4_56_sha,+rsa_aes_128_sha,+rsa_aes_256_sha + +# SSL 3 ciphers + ECC ciphers. SSL 2 is disabled by default. +# +# Comment out the NSSCipherSuite line above and use the one below if you have +# ECC enabled NSS and mod_nss and want to use Elliptical Curve Cryptography +#NSSCipherSuite +rsa_rc4_128_md5,+rsa_rc4_128_sha,+rsa_3des_sha,-rsa_des_sha,-rsa_rc4_40_md5,-rsa_rc2_40_md5,-rsa_null_md5,-rsa_null_sha,+fips_3des_sha,-fips_des_sha,-fortezza,-fortezza_rc4_128_sha,-fortezza_null,-rsa_des_56_sha,-rsa_rc4_56_sha,+rsa_aes_128_sha,+rsa_aes_256_sha,-ecdh_ecdsa_null_sha,+ecdh_ecdsa_rc4_128_sha,+ecdh_ecdsa_3des_sha,+ecdh_ecdsa_aes_128_sha,+ecdh_ecdsa_aes_256_sha,-ecdhe_ecdsa_null_sha,+ecdhe_ecdsa_rc4_128_sha,+ecdhe_ecdsa_3des_sha,+ecdhe_ecdsa_aes_128_sha,+ecdhe_ecdsa_aes_256_sha,-ecdh_rsa_null_sha,+ecdh_rsa_128_sha,+ecdh_rsa_3des_sha,+ecdh_rsa_aes_128_sha,+ecdh_rsa_aes_256_sha,-echde_rsa_null,+ecdhe_rsa_rc4_128_sha,+ecdhe_rsa_3des_sha,+ecdhe_rsa_aes_128_sha,+ecdhe_rsa_aes_256_sha + +# SSL Protocol: +# Cryptographic protocols that provide communication security. +# NSS handles the specified protocols as "ranges", and automatically +# negotiates the use of the strongest protocol for a connection starting +# with the maximum specified protocol and downgrading as necessary to the +# minimum specified protocol that can be used between two processes. +# Since all protocol ranges are completely inclusive, and no protocol in the +# middle of a range may be excluded, the entry "NSSProtocol SSLv3,TLSv1.1" +# is identical to the entry "NSSProtocol SSLv3,TLSv1.0,TLSv1.1". +NSSProtocol SSLv3,TLSv1.0,TLSv1.1 + +# SSL Certificate Nickname: +# The nickname of the RSA server certificate you are going to use. +NSSNickname Server-Cert + +# SSL Certificate Nickname: +# The nickname of the ECC server certificate you are going to use, if you +# have an ECC-enabled version of NSS and mod_nss +#NSSECCNickname Server-Cert-ecc + +# Server Certificate Database: +# The NSS security database directory that holds the certificates and +# keys. The database consists of 3 files: cert8.db, key3.db and secmod.db. +# Provide the directory that these files exist. +NSSCertificateDatabase <%= @httpd_dir -%>/alias + +# Database Prefix: +# In order to be able to store multiple NSS databases in one directory +# they need unique names. This option sets the database prefix used for +# cert8.db and key3.db. +#NSSDBPrefix my-prefix- + +# Client Authentication (Type): +# Client certificate verification type. Types are none, optional and +# require. +#NSSVerifyClient none + +# +# Online Certificate Status Protocol (OCSP). +# Verify that certificates have not been revoked before accepting them. +#NSSOCSP off + +# +# Use a default OCSP responder. If enabled this will be used regardless +# of whether one is included in a client certificate. Note that the +# server certificate is verified during startup. +# +# NSSOCSPDefaultURL defines the service URL of the OCSP responder +# NSSOCSPDefaultName is the nickname of the certificate to trust to +# sign the OCSP responses. +#NSSOCSPDefaultResponder on +#NSSOCSPDefaultURL http://example.com/ocsp/status +#NSSOCSPDefaultName ocsp-nickname + +# Access Control: +# With SSLRequire you can do per-directory access control based +# on arbitrary complex boolean expressions containing server +# variable checks and other lookup directives. The syntax is a +# mixture between C and Perl. See the mod_nss documentation +# for more details. +# +#NSSRequire ( %{SSL_CIPHER} !~ m/^(EXP|NULL)/ \ +# and %{SSL_CLIENT_S_DN_O} eq "Snake Oil, Ltd." \ +# and %{SSL_CLIENT_S_DN_OU} in {"Staff", "CA", "Dev"} \ +# and %{TIME_WDAY} >= 1 and %{TIME_WDAY} <= 5 \ +# and %{TIME_HOUR} >= 8 and %{TIME_HOUR} <= 20 ) \ +# or %{REMOTE_ADDR} =~ m/^192\.76\.162\.[0-9]+$/ +# + +# SSL Engine Options: +# Set various options for the SSL engine. +# o FakeBasicAuth: +# Translate the client X.509 into a Basic Authorisation. This means that +# the standard Auth/DBMAuth methods can be used for access control. The +# user name is the `one line' version of the client's X.509 certificate. +# Note that no password is obtained from the user. Every entry in the user +# file needs this password: `xxj31ZMTZzkVA'. +# o ExportCertData: +# This exports two additional environment variables: SSL_CLIENT_CERT and +# SSL_SERVER_CERT. These contain the PEM-encoded certificates of the +# server (always existing) and the client (only existing when client +# authentication is used). This can be used to import the certificates +# into CGI scripts. +# o StdEnvVars: +# This exports the standard SSL/TLS related `SSL_*' environment variables. +# Per default this exportation is switched off for performance reasons, +# because the extraction step is an expensive operation and is usually +# useless for serving static content. So one usually enables the +# exportation for CGI and SSI requests only. +# o StrictRequire: +# This denies access when "NSSRequireSSL" or "NSSRequire" applied even +# under a "Satisfy any" situation, i.e. when it applies access is denied +# and no other module can change it. +# o OptRenegotiate: +# This enables optimized SSL connection renegotiation handling when SSL +# directives are used in per-directory context. +#NSSOptions +FakeBasicAuth +ExportCertData +CompatEnvVars +StrictRequire + + NSSOptions +StdEnvVars + + + NSSOptions +StdEnvVars + + +# Per-Server Logging: +# The home of a custom SSL log file. Use this when you want a +# compact non-error SSL logfile on a virtual host basis. +#CustomLog /home/rcrit/redhat/apache/logs/ssl_request_log \ +# "%t %h %{SSL_PROTOCOL}x %{SSL_CIPHER}x \"%r\" %b" + + + diff --git a/apache/templates/mod/passenger.conf.erb b/apache/templates/mod/passenger.conf.erb new file mode 100644 index 000000000..9395e2e39 --- /dev/null +++ b/apache/templates/mod/passenger.conf.erb @@ -0,0 +1,34 @@ +# The Passanger Apache module configuration file is being +# managed by Puppet and changes will be overwritten. + + <%- if @passenger_root -%> + PassengerRoot <%= @passenger_root %> + <%- end -%> + <%- if @passenger_ruby -%> + PassengerRuby <%= @passenger_ruby %> + <%- end -%> + <%- if @passenger_high_performance -%> + PassengerHighPerformance <%= @passenger_high_performance %> + <%- end -%> + <%- if @passenger_max_pool_size -%> + PassengerMaxPoolSize <%= @passenger_max_pool_size %> + <%- end -%> + <%- if @passenger_pool_idle_time -%> + PassengerPoolIdleTime <%= @passenger_pool_idle_time %> + <%- end -%> + <%- if @passenger_max_requests -%> + PassengerMaxRequests <%= @passenger_max_requests %> + <%- end -%> + <%- if @passenger_stat_throttle_rate -%> + PassengerStatThrottleRate <%= @passenger_stat_throttle_rate %> + <%- end -%> + <%- if @rack_autodetect -%> + RackAutoDetect <%= @rack_autodetect %> + <%- end -%> + <%- if @rails_autodetect -%> + RailsAutoDetect <%= @rails_autodetect %> + <%- end -%> + <%- if @passenger_use_global_queue -%> + PassengerUseGlobalQueue <%= @passenger_use_global_queue %> + <%- end -%> + diff --git a/apache/templates/mod/peruser.conf.erb b/apache/templates/mod/peruser.conf.erb new file mode 100644 index 000000000..8f44c72b0 --- /dev/null +++ b/apache/templates/mod/peruser.conf.erb @@ -0,0 +1,12 @@ + + MinSpareProcessors <%= @minspareprocessors %> + MinProcessors <%= @minprocessors %> + MaxProcessors <%= @maxprocessors %> + MaxClients <%= @maxclients %> + MaxRequestsPerChild <%= @maxrequestsperchild %> + IdleTimeout <%= @idletimeout %> + ExpireTimeout <%= @expiretimeout %> + KeepAlive <%= @keepalive %> + Include <%= @mod_dir %>/peruser/multiplexers/*.conf + Include <%= @mod_dir %>/peruser/processors/*.conf + diff --git a/apache/templates/mod/php5.conf.erb b/apache/templates/mod/php5.conf.erb new file mode 100644 index 000000000..9eef7628a --- /dev/null +++ b/apache/templates/mod/php5.conf.erb @@ -0,0 +1,30 @@ +# +# PHP is an HTML-embedded scripting language which attempts to make it +# easy for developers to write dynamically generated webpages. +# +# +# LoadModule php5_module modules/libphp5.so +# +# +# # Use of the "ZTS" build with worker is experimental, and no shared +# # modules are supported. +# LoadModule php5_module modules/libphp5-zts.so +# + +# +# Cause the PHP interpreter to handle files with a .php extension. +# +AddHandler php5-script .php +AddType text/html .php + +# +# Add index.php to the list of files that will be served as directory +# indexes. +# +DirectoryIndex index.php + +# +# Uncomment the following line to allow PHP to pretty-print .phps +# files as PHP source code: +# +#AddType application/x-httpd-php-source .phps diff --git a/apache/templates/mod/prefork.conf.erb b/apache/templates/mod/prefork.conf.erb new file mode 100644 index 000000000..aabfdf7b2 --- /dev/null +++ b/apache/templates/mod/prefork.conf.erb @@ -0,0 +1,8 @@ + + StartServers <%= @startservers %> + MinSpareServers <%= @minspareservers %> + MaxSpareServers <%= @maxspareservers %> + ServerLimit <%= @serverlimit %> + MaxClients <%= @maxclients %> + MaxRequestsPerChild <%= @maxrequestsperchild %> + diff --git a/apache/templates/mod/proxy.conf.erb b/apache/templates/mod/proxy.conf.erb new file mode 100644 index 000000000..1f4a4129c --- /dev/null +++ b/apache/templates/mod/proxy.conf.erb @@ -0,0 +1,23 @@ +# +# Proxy Server directives. Uncomment the following lines to +# enable the proxy server: +# + + # Do not enable proxying with ProxyRequests until you have secured your + # server. Open proxy servers are dangerous both to your network and to the + # Internet at large. + ProxyRequests <%= @proxy_requests %> + + <% if @proxy_requests != 'Off' or ( @allow_from and ! @allow_from.empty? ) -%> + + Order deny,allow + Deny from all + Allow from <%= Array(@allow_from).join(" ") %> + + <% end -%> + + # Enable/disable the handling of HTTP/1.1 "Via:" headers. + # ("Full" adds the server version; "Block" removes all outgoing Via: headers) + # Set to one of: Off | On | Full | Block + ProxyVia On + diff --git a/apache/templates/mod/proxy_html.conf.erb b/apache/templates/mod/proxy_html.conf.erb new file mode 100644 index 000000000..7f5898ef7 --- /dev/null +++ b/apache/templates/mod/proxy_html.conf.erb @@ -0,0 +1,24 @@ +<% if @proxy_html_loadfiles -%> +<% Array(@proxy_html_loadfiles).each do |loadfile| -%> +LoadFile <%= loadfile %> +<% end -%> + +<% end -%> +ProxyHTMLLinks a href +ProxyHTMLLinks area href +ProxyHTMLLinks link href +ProxyHTMLLinks img src longdesc usemap +ProxyHTMLLinks object classid codebase data usemap +ProxyHTMLLinks q cite +ProxyHTMLLinks blockquote cite +ProxyHTMLLinks ins cite +ProxyHTMLLinks del cite +ProxyHTMLLinks form action +ProxyHTMLLinks input src usemap +ProxyHTMLLinks head profileProxyHTMLLinks base href +ProxyHTMLLinks script src for + +ProxyHTMLEvents onclick ondblclick onmousedown onmouseup \ + onmouseover onmousemove onmouseout onkeypress \ + onkeydown onkeyup onfocus onblur onload \ + onunload onsubmit onreset onselect onchange diff --git a/apache/templates/mod/reqtimeout.conf.erb b/apache/templates/mod/reqtimeout.conf.erb new file mode 100644 index 000000000..9a18800da --- /dev/null +++ b/apache/templates/mod/reqtimeout.conf.erb @@ -0,0 +1,2 @@ +RequestReadTimeout header=20-40,minrate=500 +RequestReadTimeout body=10,minrate=500 diff --git a/apache/templates/mod/rpaf.conf.erb b/apache/templates/mod/rpaf.conf.erb new file mode 100644 index 000000000..56e2398b5 --- /dev/null +++ b/apache/templates/mod/rpaf.conf.erb @@ -0,0 +1,15 @@ +# Enable reverse proxy add forward +RPAFenable On +# RPAFsethostname will, when enabled, take the incoming X-Host header and +# update the virtual host settings accordingly. This allows to have the same +# hostnames as in the "real" configuration for the forwarding proxy. +<% if @sethostname -%> +RPAFsethostname On +<% else -%> +RPAFsethostname Off +<% end -%> +# Which IPs are forwarding requests to us +RPAFproxy_ips <%= Array(@proxy_ips).join(" ") %> +# Setting RPAFheader allows you to change the header name to parse from the +# default X-Forwarded-For to something of your choice. +RPAFheader <%= @header %> diff --git a/apache/templates/mod/setenvif.conf.erb b/apache/templates/mod/setenvif.conf.erb new file mode 100644 index 000000000..d31c79fe5 --- /dev/null +++ b/apache/templates/mod/setenvif.conf.erb @@ -0,0 +1,34 @@ +# +# The following directives modify normal HTTP response behavior to +# handle known problems with browser implementations. +# +BrowserMatch "Mozilla/2" nokeepalive +BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 +BrowserMatch "RealPlayer 4\.0" force-response-1.0 +BrowserMatch "Java/1\.0" force-response-1.0 +BrowserMatch "JDK/1\.0" force-response-1.0 + +# +# The following directive disables redirects on non-GET requests for +# a directory that does not include the trailing slash. This fixes a +# problem with Microsoft WebFolders which does not appropriately handle +# redirects for folders with DAV methods. +# Same deal with Apple's DAV filesystem and Gnome VFS support for DAV. +# +BrowserMatch "Microsoft Data Access Internet Publishing Provider" redirect-carefully +BrowserMatch "MS FrontPage" redirect-carefully +BrowserMatch "^WebDrive" redirect-carefully +BrowserMatch "^WebDAVFS/1.[0123]" redirect-carefully +BrowserMatch "^gnome-vfs/1.0" redirect-carefully +BrowserMatch "^gvfs/1" redirect-carefully +BrowserMatch "^XML Spy" redirect-carefully +BrowserMatch "^Dreamweaver-WebDAV-SCM1" redirect-carefully +BrowserMatch " Konqueror/4" redirect-carefully + + + BrowserMatch "MSIE [2-6]" \ + nokeepalive ssl-unclean-shutdown \ + downgrade-1.0 force-response-1.0 + # MSIE 7 and newer should be able to use keepalive + BrowserMatch "MSIE [17-9]" ssl-unclean-shutdown + diff --git a/apache/templates/mod/ssl.conf.erb b/apache/templates/mod/ssl.conf.erb new file mode 100644 index 000000000..d42b0ad61 --- /dev/null +++ b/apache/templates/mod/ssl.conf.erb @@ -0,0 +1,24 @@ + + SSLRandomSeed startup builtin + SSLRandomSeed startup file:/dev/urandom 512 + SSLRandomSeed connect builtin + SSLRandomSeed connect file:/dev/urandom 512 + + AddType application/x-x509-ca-cert .crt + AddType application/x-pkcs7-crl .crl + + SSLPassPhraseDialog builtin + SSLSessionCache shmcb:<%= @session_cache %> + SSLSessionCacheTimeout 300 +<% if @ssl_compression -%> + SSLCompression Off +<% end -%> + SSLMutex <%= @ssl_mutex %> + SSLCryptoDevice builtin + SSLHonorCipherOrder On + SSLCipherSuite HIGH:MEDIUM:!aNULL:!MD5 + SSLProtocol all -SSLv2 +<% if @ssl_options -%> + SSLOptions <%= @ssl_options.compact.join(' ') %> +<% end -%> + diff --git a/apache/templates/mod/status.conf.erb b/apache/templates/mod/status.conf.erb new file mode 100644 index 000000000..c00c16a78 --- /dev/null +++ b/apache/templates/mod/status.conf.erb @@ -0,0 +1,12 @@ + + SetHandler server-status + Order deny,allow + Deny from all + Allow from <%= Array(@allow_from).join(" ") %> + +ExtendedStatus <%= @extended_status %> + + + # Show Proxy LoadBalancer status in mod_status + ProxyStatus On + diff --git a/apache/templates/mod/suphp.conf.erb b/apache/templates/mod/suphp.conf.erb new file mode 100644 index 000000000..95fbf97c7 --- /dev/null +++ b/apache/templates/mod/suphp.conf.erb @@ -0,0 +1,19 @@ + + AddType application/x-httpd-suphp .php .php3 .php4 .php5 .phtml + suPHP_AddHandler application/x-httpd-suphp + + + suPHP_Engine on + + + # By default, disable suPHP for debian packaged web applications as files + # are owned by root and cannot be executed by suPHP because of min_uid. + + suPHP_Engine off + + +# # Use a specific php config file (a dir which contains a php.ini file) +# suPHP_ConfigPath /etc/php4/cgi/suphp/ +# # Tells mod_suphp NOT to handle requests with the type . +# suPHP_RemoveHandler + diff --git a/apache/templates/mod/userdir.conf.erb b/apache/templates/mod/userdir.conf.erb new file mode 100644 index 000000000..cfe2e3879 --- /dev/null +++ b/apache/templates/mod/userdir.conf.erb @@ -0,0 +1,19 @@ + +<% if @disable_root -%> + UserDir disabled root +<% end -%> + UserDir <%= @dir %> + + /*/<%= @dir %>> + AllowOverride FileInfo AuthConfig Limit Indexes + Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec + + Order allow,deny + Allow from all + + + Order deny,allow + Deny from all + + + diff --git a/apache/templates/mod/worker.conf.erb b/apache/templates/mod/worker.conf.erb new file mode 100644 index 000000000..f0bba3908 --- /dev/null +++ b/apache/templates/mod/worker.conf.erb @@ -0,0 +1,9 @@ + + ServerLimit <%= @serverlimit %> + StartServers <%= @startservers %> + MaxClients <%= @maxclients %> + MinSpareThreads <%= @minsparethreads %> + MaxSpareThreads <%= @maxsparethreads %> + ThreadsPerChild <%= @threadsperchild %> + MaxRequestsPerChild <%= @maxrequestsperchild %> + diff --git a/apache/templates/mod/wsgi.conf.erb b/apache/templates/mod/wsgi.conf.erb new file mode 100644 index 000000000..ea2a49d1a --- /dev/null +++ b/apache/templates/mod/wsgi.conf.erb @@ -0,0 +1,13 @@ +# The WSGI Apache module configuration file is being +# managed by Puppet an changes will be overwritten. + + <%- if @wsgi_socket_prefix -%> + WSGISocketPrefix <%= @wsgi_socket_prefix %> + <%- end -%> + <%- if @wsgi_python_home -%> + WSGIPythonHome <%= @wsgi_python_home %> + <%- end -%> + <%- if @wsgi_python_path -%> + WSGIPythonPath <%= @wsgi_python_path %> + <%- end -%> + diff --git a/apache/templates/namevirtualhost.erb b/apache/templates/namevirtualhost.erb new file mode 100644 index 000000000..cf767680f --- /dev/null +++ b/apache/templates/namevirtualhost.erb @@ -0,0 +1,8 @@ +<%# NameVirtualHost should always be one of: + - * + - *: + - _default_: + - + - : +-%> +NameVirtualHost <%= @addr_port %> diff --git a/apache/templates/ports_header.erb b/apache/templates/ports_header.erb new file mode 100644 index 000000000..4908db4ad --- /dev/null +++ b/apache/templates/ports_header.erb @@ -0,0 +1,5 @@ +# ************************************ +# Listen & NameVirtualHost resources in module puppetlabs-apache +# Managed by Puppet +# ************************************ + diff --git a/apache/templates/vhost.conf.erb b/apache/templates/vhost.conf.erb new file mode 100644 index 000000000..6245f63fe --- /dev/null +++ b/apache/templates/vhost.conf.erb @@ -0,0 +1,64 @@ +# ************************************ +# Vhost template in module puppetlabs-apache +# Managed by Puppet +# ************************************ + +> + ServerName <%= @servername %> +<% if @serveradmin -%> + ServerAdmin <%= @serveradmin %> +<% end -%> + + ## Vhost docroot +<% if @virtual_docroot -%> + VirtualDocumentRoot <%= @virtual_docroot %> +<% else -%> + DocumentRoot <%= @docroot %> +<% end -%> +<%= scope.function_template(['apache/vhost/_aliases.erb']) -%> + +<%= scope.function_template(['apache/vhost/_itk.erb']) -%> + +<% if @fallbackresource -%> + FallbackResource <%= @fallbackresource %> +<% end -%> + + ## Directories, there should at least be a declaration for <%= @docroot %> +<%= scope.function_template(['apache/vhost/_directories.erb']) -%> + + ## Load additional static includes +<% Array(@additional_includes).each do |include| %> + Include <%= include %> +<% end %> + + ## Logging +<% if @error_log -%> + ErrorLog <%= @error_log_destination %> +<% end -%> +<% if @log_level -%> + LogLevel <%= @log_level %> +<% end -%> + ServerSignature Off +<% if @access_log and @_access_log_env_var -%> + CustomLog <%= @access_log_destination %> <%= @_access_log_format %> <%= @_access_log_env_var %> +<% elsif @access_log -%> + CustomLog <%= @access_log_destination %> <%= @_access_log_format %> +<% end -%> +<%= scope.function_template(['apache/vhost/_block.erb']) -%> +<%= scope.function_template(['apache/vhost/_error_document.erb']) -%> +<%= scope.function_template(['apache/vhost/_proxy.erb']) -%> +<%= scope.function_template(['apache/vhost/_rack.erb']) -%> +<%= scope.function_template(['apache/vhost/_redirect.erb']) -%> +<%= scope.function_template(['apache/vhost/_rewrite.erb']) -%> +<%= scope.function_template(['apache/vhost/_scriptalias.erb']) -%> +<%= scope.function_template(['apache/vhost/_serveralias.erb']) -%> +<%= scope.function_template(['apache/vhost/_setenv.erb']) -%> +<%= scope.function_template(['apache/vhost/_ssl.erb']) -%> +<%= scope.function_template(['apache/vhost/_suphp.erb']) -%> +<%= scope.function_template(['apache/vhost/_php_admin.erb']) -%> +<%= scope.function_template(['apache/vhost/_header.erb']) -%> +<%= scope.function_template(['apache/vhost/_requestheader.erb']) -%> +<%= scope.function_template(['apache/vhost/_wsgi.erb']) -%> +<%= scope.function_template(['apache/vhost/_custom_fragment.erb']) -%> +<%= scope.function_template(['apache/vhost/_fastcgi.erb']) -%> + diff --git a/apache/templates/vhost/_aliases.erb b/apache/templates/vhost/_aliases.erb new file mode 100644 index 000000000..484379bba --- /dev/null +++ b/apache/templates/vhost/_aliases.erb @@ -0,0 +1,12 @@ +<% if @aliases and ! @aliases.empty? -%> + ## Alias declarations for resources outside the DocumentRoot + <%- [@aliases].flatten.compact.each do |alias_statement| -%> + <%- if alias_statement["path"] != '' -%> + <%- if alias_statement["alias"] and alias_statement["alias"] != '' -%> + Alias <%= alias_statement["alias"] %> <%= alias_statement["path"] %> + <%- elsif alias_statement["aliasmatch"] and alias_statement["aliasmatch"] != '' -%> + AliasMatch <%= alias_statement["aliasmatch"] %> <%= alias_statement["path"] %> + <%- end -%> + <%- end -%> + <%- end -%> +<% end -%> diff --git a/apache/templates/vhost/_block.erb b/apache/templates/vhost/_block.erb new file mode 100644 index 000000000..f235f89d2 --- /dev/null +++ b/apache/templates/vhost/_block.erb @@ -0,0 +1,10 @@ +<% if @block and ! @block.empty? -%> + + ## Block access statements +<% if @block.include? 'scm' -%> + # Block access to SCM directories. + + Deny From All + +<% end -%> +<% end -%> diff --git a/apache/templates/vhost/_custom_fragment.erb b/apache/templates/vhost/_custom_fragment.erb new file mode 100644 index 000000000..973964655 --- /dev/null +++ b/apache/templates/vhost/_custom_fragment.erb @@ -0,0 +1,5 @@ +<% if @custom_fragment -%> + + ## Custom fragment +<%= @custom_fragment %> +<% end -%> diff --git a/apache/templates/vhost/_directories.erb b/apache/templates/vhost/_directories.erb new file mode 100644 index 000000000..0d74c450a --- /dev/null +++ b/apache/templates/vhost/_directories.erb @@ -0,0 +1,146 @@ +<% if @_directories and ! @_directories.empty? -%> + <%- [@_directories].flatten.compact.each do |directory| -%> + <%- if directory['path'] and directory['path'] != '' -%> + <%- if directory['provider'] and [ 'directory', 'location', 'files' ].include?(directory['provider']) -%> + <%- provider = directory['provider'].capitalize -%> + <%- else -%> + <%- provider = 'Directory' -%> + <%- end -%> + + <<%= provider + ' ' + directory['path'] %>> + <%- if directory['headers'] -%> + <%- Array(directory['headers']).each do |header| -%> + Header <%= header %> + <%- end -%> + <%- end -%> + <%- if directory['options'] -%> + Options <%= Array(directory['options']).join(' ') %> + <%- end -%> + <%- if provider == 'Directory' -%> + <%- if directory['index_options'] -%> + IndexOptions <%= Array(directory['index_options']).join(' ') %> + <%- end -%> + <%- if directory['index_order_default'] -%> + IndexOrderDefault <%= Array(directory['index_order_default']).join(' ') %> + <%- end -%> + <%- if directory['allow_override'] -%> + AllowOverride <%= Array(directory['allow_override']).join(' ') %> + <%- elsif provider == 'Directory' -%> + AllowOverride None + <%- end -%> + <%- end -%> + <%- if directory['order'] and directory['order'] != '' -%> + Order <%= Array(directory['order']).join(',') %> + <%- else -%> + Order allow,deny + <%- end -%> + <%- if directory['deny'] and directory['deny'] != '' -%> + Deny <%= directory['deny'] %> + <%- end -%> + <%- if directory['allow'] and ! [ false, 'false', '' ].include?(directory['allow']) -%> + Allow <%= directory['allow'] %> + <%- elsif [ 'from all', 'from All' ].include?(directory['deny']) -%> + <%- elsif ! directory['deny'] and [ false, 'false', '' ].include?(directory['allow']) -%> + Deny from all + <%- else -%> + Allow from all + <%- end -%> + <%- if directory['addhandlers'] and ! directory['addhandlers'].empty? -%> + <%- [directory['addhandlers']].flatten.compact.each do |addhandler| -%> + AddHandler <%= addhandler['handler'] %> <%= Array(addhandler['extensions']).join(' ') %> + <%- end -%> + <%- end -%> + <%- if directory['passenger_enabled'] and directory['passenger_enabled'] != '' -%> + PassengerEnabled <%= directory['passenger_enabled'] %> + <%- end -%> + <%- if directory['php_admin_flags'] and ! directory['php_admin_flags'].empty? -%> + <%- directory['php_admin_flags'].each do |flag,value| -%> + <%- value = if value =~ /true|yes|on|1/i then 'on' else 'off' end -%> + php_admin_flag <%= "#{flag} #{value}" %> + <%- end -%> + <%- end -%> + <%- if directory['php_admin_values'] and ! directory['php_admin_values'].empty? -%> + <%- directory['php_admin_values'].each do |key,value| -%> + php_admin_value <%= "#{key} #{value}" %> + <%- end -%> + <%- end -%> + <%- if directory['directoryindex'] and directory['directoryindex'] != '' -%> + DirectoryIndex <%= directory['directoryindex'] %> + <%- end -%> + <%- if directory['error_documents'] and ! directory['error_documents'].empty? -%> + <%- [directory['error_documents']].flatten.compact.each do |error_document| -%> + ErrorDocument <%= error_document['error_code'] %> <%= error_document['document'] %> + <%- end -%> + <%- end -%> + <%- if directory['auth_type'] -%> + AuthType <%= directory['auth_type'] %> + <%- end -%> + <%- if directory['auth_name'] -%> + AuthName "<%= directory['auth_name'] %>" + <%- end -%> + <%- if directory['auth_digest_algorithm'] -%> + AuthDigestAlgorithm <%= directory['auth_digest_algorithm'] %> + <%- end -%> + <%- if directory['auth_digest_domain'] -%> + AuthDigestDomain <%= Array(directory['auth_digest_domain']).join(' ') %> + <%- end -%> + <%- if directory['auth_digest_nonce_lifetime'] -%> + AuthDigestNonceLifetime <%= directory['auth_digest_nonce_lifetime'] %> + <%- end -%> + <%- if directory['auth_digest_provider'] -%> + AuthDigestProvider <%= directory['auth_digest_provider'] %> + <%- end -%> + <%- if directory['auth_digest_qop'] -%> + AuthDigestQop <%= directory['auth_digest_qop'] %> + <%- end -%> + <%- if directory['auth_digest_shmem_size'] -%> + AuthDigestShmemSize <%= directory['auth_digest_shmem_size'] %> + <%- end -%> + <%- if directory['auth_basic_authoritative'] -%> + AuthBasicAuthoritative <%= directory['auth_basic_authoritative'] %> + <%- end -%> + <%- if directory['auth_basic_fake'] -%> + AuthBasicFake <%= directory['auth_basic_fake'] %> + <%- end -%> + <%- if directory['auth_basic_provider'] -%> + AuthBasicProvider <%= directory['auth_basic_provider'] %> + <%- end -%> + <%- if directory['auth_user_file'] -%> + AuthUserFile <%= directory['auth_user_file'] %> + <%- end -%> + <%- if directory['auth_group_file'] -%> + AuthGroupFile <%= directory['auth_group_file'] %> + <%- end -%> + <%- if directory['auth_require'] -%> + Require <%= directory['auth_require'] %> + <%- end -%> + <%- if directory['fallbackresource'] -%> + FallbackResource <%= directory['fallbackresource'] %> + <%- end -%> + <%- if directory['expires_active'] -%> + ExpiresActive <%= directory['expires_active'] %> + <%- end -%> + <%- if directory['expires_default'] -%> + ExpiresDefault <%= directory['expires_default'] %> + <%- end -%> + <%- if directory['expires_by_type'] -%> + <%- Array(directory['expires_by_type']).each do |rule| -%> + ExpiresByType <%= rule %> + <%- end -%> + <%- end -%> + <%- if directory['force_type'] -%> + ForceType <%= directory['force_type'] %> + <%- end -%> + <%- if directory['ssl_options'] -%> + SSLOptions <%= Array(directory['ssl_options']).join(' ') %> + <%- end -%> + <%- if directory['suphp'] and @suphp_engine == 'on' -%> + suPHP_UserGroup <%= directory['suphp']['user'] %> <%= directory['suphp']['group'] %> + <%- end -%> + <%- if directory['custom_fragment'] -%> + <%= directory['custom_fragment'] %> + <%- end -%> + > + <%- end -%> + <%- end -%> +<% end -%> diff --git a/apache/templates/vhost/_error_document.erb b/apache/templates/vhost/_error_document.erb new file mode 100644 index 000000000..654e72c67 --- /dev/null +++ b/apache/templates/vhost/_error_document.erb @@ -0,0 +1,7 @@ +<% if @error_documents and ! @error_documents.empty? -%> + <%- [@error_documents].flatten.compact.each do |error_document| -%> + <%- if error_document["error_code"] != '' and error_document["document"] != '' -%> + ErrorDocument <%= error_document["error_code"] %> <%= error_document["document"] %> + <%- end -%> + <%- end -%> +<% end -%> diff --git a/apache/templates/vhost/_fastcgi.erb b/apache/templates/vhost/_fastcgi.erb new file mode 100644 index 000000000..f0ff72c6b --- /dev/null +++ b/apache/templates/vhost/_fastcgi.erb @@ -0,0 +1,18 @@ +<% if @fastcgi_server -%> + + FastCgiExternalServer <%= @fastcgi_server %> -socket <%= @fastcgi_socket %> +<% end -%> +<% if @fastcgi_dir -%> + + > + Options +ExecCGI + AllowOverride All + SetHandler fastcgi-script + Order allow,deny + Allow from all + AuthBasicAuthoritative Off + + + AllowEncodedSlashes On + ServerSignature Off +<% end -%> diff --git a/apache/templates/vhost/_header.erb b/apache/templates/vhost/_header.erb new file mode 100644 index 000000000..c0f68c825 --- /dev/null +++ b/apache/templates/vhost/_header.erb @@ -0,0 +1,10 @@ +<% if @headers and ! @headers.empty? -%> + + ## Header rules + ## as per http://httpd.apache.org/docs/2.2/mod/mod_headers.html#header + <%- Array(@headers).each do |header_statement| -%> + <%- if header_statement != '' -%> + Header <%= header_statement %> + <%- end -%> + <%- end -%> +<% end -%> diff --git a/apache/templates/vhost/_itk.erb b/apache/templates/vhost/_itk.erb new file mode 100644 index 000000000..7d3bb5166 --- /dev/null +++ b/apache/templates/vhost/_itk.erb @@ -0,0 +1,28 @@ +<% if @itk and ! @itk.empty? -%> + ## ITK statement + + <%- if @itk["user"] and @itk["group"] -%> + AssignUserId <%= @itk["user"] %> <%= @itk["group"] %> + <%- end -%> + <%- if @itk["assignuieridexpr"] -%> + AssignUserIdExpr <%= @itk["assignuieridexpr"] %> + <%- end -%> + <%- if @itk["assignuiergroupexpr"] -%> + AssignGroupIdExpr <%= @itk["assignuiergroupexpr"] %> + <%- end -%> + <%- if @itk["maxclientvhost"] -%> + MaxClientsVHost <%= @itk["maxclientvhost"] %> + <%- end -%> + <%- if @itk["nice"] -%> + NiceValue <%= @itk["nice"] %> + <%- end -%> + <%- if @kernelversion >= '3.5.0' -%> + <%- if @itk["limituidrange"] -%> + LimitUIDRange <%= @itk["limituidrange"] %> + <%- end -%> + <%- if @itk["limitgidrange"] -%> + LimitGIDRange <%= @itk["limitgidrange"] %> + <%- end -%> + <%- end -%> + +<% end -%> diff --git a/apache/templates/vhost/_php_admin.erb b/apache/templates/vhost/_php_admin.erb new file mode 100644 index 000000000..59536cbc9 --- /dev/null +++ b/apache/templates/vhost/_php_admin.erb @@ -0,0 +1,12 @@ +<% if @php_admin_values and not @php_admin_values.empty? -%> +<% @php_admin_values.each do |key,value| -%> + php_admin_value <%= key %> <%= value %> +<% end -%> +<% end -%> +<% if @php_admin_flags and not @php_admin_flags.empty? -%> +<% @php_admin_flags.each do |key,flag| -%> +<%# normalize flag -%> +<% if flag =~ /true|yes|on|1/i then flag = 'on' else flag = 'off' end -%> + php_admin_flag <%= key %> <%= flag %> +<% end -%> +<% end -%> diff --git a/apache/templates/vhost/_proxy.erb b/apache/templates/vhost/_proxy.erb new file mode 100644 index 000000000..a126dbc97 --- /dev/null +++ b/apache/templates/vhost/_proxy.erb @@ -0,0 +1,20 @@ +<% if @proxy_dest or @proxy_pass -%> + + ## Proxy rules + ProxyRequests Off +<%- end -%> +<% [@proxy_pass].flatten.compact.each do |proxy| %> + ProxyPass <%= proxy['path'] %> <%= proxy['url'] %> + > + ProxyPassReverse / + +<% end %> +<% if @proxy_dest -%> +<% Array(@no_proxy_uris).each do |uri| %> + ProxyPass <%= uri %> ! +<% end %> + ProxyPass / <%= @proxy_dest %>/ + + ProxyPassReverse / + +<% end -%> diff --git a/apache/templates/vhost/_rack.erb b/apache/templates/vhost/_rack.erb new file mode 100644 index 000000000..4a5b5f1cd --- /dev/null +++ b/apache/templates/vhost/_rack.erb @@ -0,0 +1,7 @@ +<% if @rack_base_uris -%> + + ## Enable rack +<% Array(@rack_base_uris).each do |uri| -%> + RackBaseURI <%= uri %> +<% end -%> +<% end -%> diff --git a/apache/templates/vhost/_redirect.erb b/apache/templates/vhost/_redirect.erb new file mode 100644 index 000000000..e865bd9af --- /dev/null +++ b/apache/templates/vhost/_redirect.erb @@ -0,0 +1,24 @@ +<% if @redirect_source and @redirect_dest -%> +<% @redirect_dest_a = Array(@redirect_dest) -%> +<% @redirect_source_a = Array(@redirect_source) -%> +<% @redirect_status_a = Array(@redirect_status) -%> + + ## Redirect rules +<% @redirect_source_a.each_with_index do |source, i| -%> +<% @redirect_dest_a[i] ||= @redirect_dest_a[0] -%> +<% @redirect_status_a[i] ||= @redirect_status_a[0] -%> + Redirect <%= "#{@redirect_status_a[i]} " %><%= source %> <%= @redirect_dest_a[i] %> +<% end -%> +<% end -%> + +<%- if @redirectmatch_status and @redirectmatch_regexp -%> +<% @redirectmatch_status_a = Array(@redirectmatch_status) -%> +<% @redirectmatch_regexp_a = Array(@redirectmatch_regexp) -%> + + ## RedirectMatch rules +<% @redirectmatch_status_a.each_with_index do |status, i| -%> +<% @redirectmatch_status_a[i] ||= @redirectmatch_status_a[0] -%> +<% @redirectmatch_regexp_a[i] ||= @redirectmatch_regexp_a[0] -%> + RedirectMatch <%= "#{@redirectmatch_status_a[i]} " %> <%= @redirectmatch_regexp_a[i] %> +<% end -%> +<% end -%> diff --git a/apache/templates/vhost/_requestheader.erb b/apache/templates/vhost/_requestheader.erb new file mode 100644 index 000000000..9f175052b --- /dev/null +++ b/apache/templates/vhost/_requestheader.erb @@ -0,0 +1,10 @@ +<% if @request_headers and ! @request_headers.empty? -%> + + ## Request header rules + ## as per http://httpd.apache.org/docs/2.2/mod/mod_headers.html#requestheader + <%- Array(@request_headers).each do |request_statement| -%> + <%- if request_statement != '' -%> + RequestHeader <%= request_statement %> + <%- end -%> + <%- end -%> +<% end -%> diff --git a/apache/templates/vhost/_rewrite.erb b/apache/templates/vhost/_rewrite.erb new file mode 100644 index 000000000..dbb437c1d --- /dev/null +++ b/apache/templates/vhost/_rewrite.erb @@ -0,0 +1,40 @@ +<% if @rewrites -%> + ## Rewrite rules + RewriteEngine On +<% if @rewrite_base -%> + RewriteBase <%= @rewrite_base %> +<% end -%> + +<% [@rewrites].flatten.compact.each do |rewrite_details| -%> +<% if rewrite_details['comment'] -%> + #<%= rewrite_details['comment'] %> +<% end -%> +<% if rewrite_details['rewrite_cond'] -%> +<%- Array(rewrite_details['rewrite_cond']).each do |commands| -%> +<%- Array(commands).each do |command| -%> + RewriteCond <%= command %> +<%- end -%> +<% end -%> +<% end -%> +<%- Array(rewrite_details['rewrite_rule']).each do |commands| -%> +<%- Array(commands).each do |command| -%> + RewriteRule <%= command %> +<%- end -%> + +<% end -%> +<% end -%> +<%- end -%> +<%# reverse compatibility %> +<% if @rewrite_rule and !@rewrites -%> + ## Rewrite rules + RewriteEngine On +<% if @rewrite_base -%> + RewriteBase <%= @rewrite_base %> +<% end -%> +<% if @rewrite_cond -%> +<% Array(@rewrite_cond).each do |cond| -%> + RewriteCond <%= cond %> +<% end -%> +<% end -%> + RewriteRule <%= @rewrite_rule %> +<%- end -%> diff --git a/apache/templates/vhost/_scriptalias.erb b/apache/templates/vhost/_scriptalias.erb new file mode 100644 index 000000000..cdf3b87e4 --- /dev/null +++ b/apache/templates/vhost/_scriptalias.erb @@ -0,0 +1,24 @@ +<%- if @scriptaliases.is_a?(Array) -%> +<%- aliases = @scriptaliases -%> +<%- elsif @scriptaliases.is_a?(Hash) -%> +<%- aliases = [@scriptaliases] -%> +<%- else -%> +<%- # Nothing to do with any other data type -%> +<%- aliases = [] -%> +<%- end -%> +<%- if @scriptalias or !aliases.empty? -%> + ## Script alias directives +<%# Combine scriptalais and scriptaliases into a single data structure -%> +<%# for backward compatibility and ease of implementation -%> +<%- aliases << { 'alias' => '/cgi-bin/', 'path' => @scriptalias } if @scriptalias -%> +<%- aliases.flatten.compact! -%> +<%- aliases.each do |salias| -%> + <%- if salias["path"] != '' -%> + <%- if salias["alias"] and salias["alias"] != '' -%> + ScriptAlias <%= salias['alias'] %> <%= salias['path'] %> + <%- elsif salias["aliasmatch"] and salias["aliasmatch"] != '' -%> + ScriptAliasMatch <%= salias['aliasmatch'] %> <%= salias['path'] %> + <%- end -%> + <%- end -%> +<%- end -%> +<%- end -%> diff --git a/apache/templates/vhost/_serveralias.erb b/apache/templates/vhost/_serveralias.erb new file mode 100644 index 000000000..278b6ddc5 --- /dev/null +++ b/apache/templates/vhost/_serveralias.erb @@ -0,0 +1,7 @@ +<% if @serveraliases and ! @serveraliases.empty? -%> + + ## Server aliases +<% Array(@serveraliases).each do |serveralias| -%> + ServerAlias <%= serveralias %> +<% end -%> +<% end -%> diff --git a/apache/templates/vhost/_setenv.erb b/apache/templates/vhost/_setenv.erb new file mode 100644 index 000000000..d5f9ea845 --- /dev/null +++ b/apache/templates/vhost/_setenv.erb @@ -0,0 +1,12 @@ +<% if @setenv and ! @setenv.empty? -%> + + ## SetEnv/SetEnvIf for environment variables +<% Array(@setenv).each do |envvar| -%> + SetEnv <%= envvar %> +<% end -%> +<% end -%> +<% if @setenvif and ! @setenvif.empty? -%> +<% Array(@setenvif).each do |envifvar| -%> + SetEnvIf <%= envifvar %> +<% end -%> +<% end -%> diff --git a/apache/templates/vhost/_ssl.erb b/apache/templates/vhost/_ssl.erb new file mode 100644 index 000000000..96027dce7 --- /dev/null +++ b/apache/templates/vhost/_ssl.erb @@ -0,0 +1,41 @@ +<% if @ssl -%> + + ## SSL directives + SSLEngine on + SSLCertificateFile <%= @ssl_cert %> + SSLCertificateKeyFile <%= @ssl_key %> +<% if @ssl_chain -%> + SSLCertificateChainFile <%= @ssl_chain %> +<% end -%> + SSLCACertificatePath <%= @ssl_certs_dir %> +<% if @ssl_ca -%> + SSLCACertificateFile <%= @ssl_ca %> +<% end -%> +<% if @ssl_crl_path -%> + SSLCARevocationPath <%= @ssl_crl_path %> +<% end -%> +<% if @ssl_crl -%> + SSLCARevocationFile <%= @ssl_crl %> +<% end -%> +<% if @ssl_proxyengine -%> + SSLProxyEngine On +<% end -%> +<% if @ssl_protocol -%> + SSLProtocol <%= @ssl_protocol %> +<% end -%> +<% if @ssl_cipher -%> + SSLCipherSuite <%= @ssl_cipher %> +<% end -%> +<% if @ssl_honorcipherorder -%> + SSLHonorCipherOrder <%= @ssl_honorcipherorder %> +<% end -%> +<% if @ssl_verify_client -%> + SSLVerifyClient <%= @ssl_verify_client %> +<% end -%> +<% if @ssl_verify_depth -%> + SSLVerifyDepth <%= @ssl_verify_depth %> +<% end -%> +<% if @ssl_options -%> + SSLOptions <%= Array(@ssl_options).join(' ') %> +<% end -%> +<% end -%> diff --git a/apache/templates/vhost/_suphp.erb b/apache/templates/vhost/_suphp.erb new file mode 100644 index 000000000..37aa299aa --- /dev/null +++ b/apache/templates/vhost/_suphp.erb @@ -0,0 +1,11 @@ +<% if @suphp_engine == 'on' -%> +<% if @suphp_addhandler -%> + suPHP_AddHandler <%= @suphp_addhandler %> +<% end -%> +<% if @suphp_engine -%> + suPHP_Engine <%= @suphp_engine %> +<% end -%> +<% if @suphp_configpath -%> + suPHP_ConfigPath <%= @suphp_configpath %> +<% end -%> +<% end -%> diff --git a/apache/templates/vhost/_wsgi.erb b/apache/templates/vhost/_wsgi.erb new file mode 100644 index 000000000..d9970bddc --- /dev/null +++ b/apache/templates/vhost/_wsgi.erb @@ -0,0 +1,15 @@ +<% if @wsgi_daemon_process and @wsgi_daemon_process_options -%> + WSGIDaemonProcess <%= @wsgi_daemon_process %> <%= @wsgi_daemon_process_options.collect { |k,v| "#{k}=#{v}"}.sort.join(' ') %> +<% elsif @wsgi_daemon_process and !@wsgi_daemon_process_options -%> + WSGIDaemonProcess <%= @wsgi_daemon_process %> +<% end -%> +<% if @wsgi_process_group -%> + WSGIProcessGroup <%= @wsgi_process_group %> +<% end -%> +<% if @wsgi_script_aliases and ! @wsgi_script_aliases.empty? -%> + <%- @wsgi_script_aliases.each do |a, p| -%> + <%- if a != '' and p != ''-%> + WSGIScriptAlias <%= a %> <%= p %> + <%- end -%> + <%- end -%> +<% end -%> diff --git a/apache/tests/apache.pp b/apache/tests/apache.pp new file mode 100644 index 000000000..0d4543564 --- /dev/null +++ b/apache/tests/apache.pp @@ -0,0 +1,6 @@ +include apache +include apache::mod::php +include apache::mod::cgi +include apache::mod::userdir +include apache::mod::disk_cache +include apache::mod::proxy_http diff --git a/apache/tests/dev.pp b/apache/tests/dev.pp new file mode 100644 index 000000000..805ad7e37 --- /dev/null +++ b/apache/tests/dev.pp @@ -0,0 +1 @@ +include apache::dev diff --git a/apache/tests/init.pp b/apache/tests/init.pp new file mode 100644 index 000000000..b3f9f13aa --- /dev/null +++ b/apache/tests/init.pp @@ -0,0 +1 @@ +include apache diff --git a/apache/tests/mod_load_params.pp b/apache/tests/mod_load_params.pp new file mode 100644 index 000000000..0e84c5efb --- /dev/null +++ b/apache/tests/mod_load_params.pp @@ -0,0 +1,11 @@ +# Tests the path and identifier parameters for the apache::mod class + +# Base class for clarity: +class { 'apache': } + + +# Exaple parameter usage: +apache::mod { 'testmod': + path => '/usr/some/path/mod_testmod.so', + id => 'testmod_custom_name', +} diff --git a/apache/tests/mods.pp b/apache/tests/mods.pp new file mode 100644 index 000000000..59362bd9a --- /dev/null +++ b/apache/tests/mods.pp @@ -0,0 +1,9 @@ +## Default mods + +# Base class. Declares default vhost on port 80 and default ssl +# vhost on port 443 listening on all interfaces and serving +# $apache::docroot, and declaring our default set of modules. +class { 'apache': + default_mods => true, +} + diff --git a/apache/tests/mods_custom.pp b/apache/tests/mods_custom.pp new file mode 100644 index 000000000..0ae699c73 --- /dev/null +++ b/apache/tests/mods_custom.pp @@ -0,0 +1,16 @@ +## custom mods + +# Base class. Declares default vhost on port 80 and default ssl +# vhost on port 443 listening on all interfaces and serving +# $apache::docroot, and declaring a custom set of modules. +class { 'apache': + default_mods => [ + 'info', + 'alias', + 'mime', + 'env', + 'setenv', + 'expires', + ], +} + diff --git a/apache/tests/php.pp b/apache/tests/php.pp new file mode 100644 index 000000000..1d926bfb4 --- /dev/null +++ b/apache/tests/php.pp @@ -0,0 +1,4 @@ +class { 'apache': + mpm_module => 'prefork', +} +include apache::mod::php diff --git a/apache/tests/vhost.pp b/apache/tests/vhost.pp new file mode 100644 index 000000000..f0d3f58e4 --- /dev/null +++ b/apache/tests/vhost.pp @@ -0,0 +1,237 @@ +## Default vhosts, and custom vhosts +# NB: Please see the other vhost_*.pp example files for further +# examples. + +# Base class. Declares default vhost on port 80 and default ssl +# vhost on port 443 listening on all interfaces and serving +# $apache::docroot +class { 'apache': } + +# Most basic vhost +apache::vhost { 'first.example.com': + port => '80', + docroot => '/var/www/first', +} + +# Vhost with different docroot owner/group +apache::vhost { 'second.example.com': + port => '80', + docroot => '/var/www/second', + docroot_owner => 'third', + docroot_group => 'third', +} + +# Vhost with serveradmin +apache::vhost { 'third.example.com': + port => '80', + docroot => '/var/www/third', + serveradmin => 'admin@example.com', +} + +# Vhost with ssl (uses default ssl certs) +apache::vhost { 'ssl.example.com': + port => '443', + docroot => '/var/www/ssl', + ssl => true, +} + +# Vhost with ssl and specific ssl certs +apache::vhost { 'fourth.example.com': + port => '443', + docroot => '/var/www/fourth', + ssl => true, + ssl_cert => '/etc/ssl/fourth.example.com.cert', + ssl_key => '/etc/ssl/fourth.example.com.key', +} + +# Vhost with english title and servername parameter +apache::vhost { 'The fifth vhost': + servername => 'fifth.example.com', + port => '80', + docroot => '/var/www/fifth', +} + +# Vhost with server aliases +apache::vhost { 'sixth.example.com': + serveraliases => [ + 'sixth.example.org', + 'sixth.example.net', + ], + port => '80', + docroot => '/var/www/fifth', +} + +# Vhost with alternate options +apache::vhost { 'seventh.example.com': + port => '80', + docroot => '/var/www/seventh', + options => [ + 'Indexes', + 'MultiViews', + ], +} + +# Vhost with AllowOverride for .htaccess +apache::vhost { 'eighth.example.com': + port => '80', + docroot => '/var/www/eighth', + override => 'All', +} + +# Vhost with access and error logs disabled +apache::vhost { 'ninth.example.com': + port => '80', + docroot => '/var/www/ninth', + access_log => false, + error_log => false, +} + +# Vhost with custom access and error logs and logroot +apache::vhost { 'tenth.example.com': + port => '80', + docroot => '/var/www/tenth', + access_log_file => 'tenth_vhost.log', + error_log_file => 'tenth_vhost_error.log', + logroot => '/var/log', +} + +# Vhost with a cgi-bin +apache::vhost { 'eleventh.example.com': + port => '80', + docroot => '/var/www/eleventh', + scriptalias => '/usr/lib/cgi-bin', +} + +# Vhost with a proxypass configuration +apache::vhost { 'twelfth.example.com': + port => '80', + docroot => '/var/www/twelfth', + proxy_dest => 'http://internal.example.com:8080/twelfth', + no_proxy_uris => ['/login','/logout'], +} + +# Vhost to redirect /login and /logout +apache::vhost { 'thirteenth.example.com': + port => '80', + docroot => '/var/www/thirteenth', + redirect_source => [ + '/login', + '/logout', + ], + redirect_dest => [ + 'http://10.0.0.10/login', + 'http://10.0.0.10/logout', + ], +} + +# Vhost to permamently redirect +apache::vhost { 'fourteenth.example.com': + port => '80', + docroot => '/var/www/fourteenth', + redirect_source => '/blog', + redirect_dest => 'http://blog.example.com', + redirect_status => 'permanent', +} + +# Vhost with a rack configuration +apache::vhost { 'fifteenth.example.com': + port => '80', + docroot => '/var/www/fifteenth', + rack_base_uris => ['/rackapp1', '/rackapp2'], +} + +# Vhost to redirect non-ssl to ssl +apache::vhost { 'sixteenth.example.com non-ssl': + servername => 'sixteenth.example.com', + port => '80', + docroot => '/var/www/sixteenth', + rewrites => [ + { + comment => 'redirect non-SSL traffic to SSL site', + rewrite_cond => ['%{HTTPS} off'], + rewrite_rule => ['(.*) https://%{HTTPS_HOST}%{REQUEST_URI}'], + } + ] +} +apache::vhost { 'sixteenth.example.com ssl': + servername => 'sixteenth.example.com', + port => '443', + docroot => '/var/www/sixteenth', + ssl => true, +} + +# Vhost to redirect non-ssl to ssl using old rewrite method +apache::vhost { 'sixteenth.example.com non-ssl old rewrite': + servername => 'sixteenth.example.com', + port => '80', + docroot => '/var/www/sixteenth', + rewrite_cond => '%{HTTPS} off', + rewrite_rule => '(.*) https://%{HTTPS_HOST}%{REQUEST_URI}', +} +apache::vhost { 'sixteenth.example.com ssl old rewrite': + servername => 'sixteenth.example.com', + port => '443', + docroot => '/var/www/sixteenth', + ssl => true, +} + +# Vhost to block repository files +apache::vhost { 'seventeenth.example.com': + port => '80', + docroot => '/var/www/seventeenth', + block => 'scm', +} + +# Vhost with special environment variables +apache::vhost { 'eighteenth.example.com': + port => '80', + docroot => '/var/www/eighteenth', + setenv => ['SPECIAL_PATH /foo/bin','KILROY was_here'], +} + +apache::vhost { 'nineteenth.example.com': + port => '80', + docroot => '/var/www/nineteenth', + setenvif => 'Host "^([^\.]*)\.website\.com$" CLIENT_NAME=$1', +} + +# Vhost with additional include files +apache::vhost { 'twentyieth.example.com': + port => '80', + docroot => '/var/www/twelfth', + additional_includes => ['/tmp/proxy_group_a','/tmp/proxy_group_b'], +} + +# Vhost with alias for subdomain mapped to same named directory +# http://example.com.loc => /var/www/example.com +apache::vhost { 'subdomain.loc': + vhost_name => '*', + port => '80', + virtual_docroot => '/var/www/%-2+', + docroot => '/var/www', + serveraliases => ['*.loc',], +} + +# Vhost with SSLProtocol,SSLCipherSuite, SSLHonorCipherOrder +apache::vhost { 'securedomain.com': + priority => '10', + vhost_name => 'www.securedomain.com', + port => '443', + docroot => '/var/www/secure', + ssl => true, + ssl_cert => '/etc/ssl/securedomain.cert', + ssl_key => '/etc/ssl/securedomain.key', + ssl_chain => '/etc/ssl/securedomain.crt', + ssl_protocol => '-ALL +SSLv3 +TLSv1', + ssl_cipher => 'ALL:!aNULL:!ADH:!eNULL:!LOW:!EXP:RC4+RSA:+HIGH:+MEDIUM', + ssl_honorcipherorder => 'On', + add_listen => false, +} + +# Vhost with access log environment variables writing control +apache::vhost { 'twentyfirst.example.com': + port => '80', + docroot => '/var/www/twentyfirst', + access_log_env_var => 'admin', +} + diff --git a/apache/tests/vhost_directories.pp b/apache/tests/vhost_directories.pp new file mode 100644 index 000000000..945a660f4 --- /dev/null +++ b/apache/tests/vhost_directories.pp @@ -0,0 +1,44 @@ +# Base class. Declares default vhost on port 80 and default ssl +# vhost on port 443 listening on all interfaces and serving +# $apache::docroot +class { 'apache': } + +# Example from README adapted. +apache::vhost { 'readme.example.net': + docroot => '/var/www/readme', + directories => [ + { + 'path' => '/var/www/readme', + 'ServerTokens' => 'prod' , + }, + { + 'path' => '/usr/share/empty', + 'allow' => 'from all', + }, + ], +} + +# location test +apache::vhost { 'location.example.net': + docroot => '/var/www/location', + directories => [ + { + 'path' => '/location', + 'provider' => 'location', + 'ServerTokens' => 'prod' + }, + ], +} + +# files test, curedly disable access to accidental backup files. +apache::vhost { 'files.example.net': + docroot => '/var/www/files', + directories => [ + { + 'path' => '~ (\.swp|\.bak|~)$', + 'provider' => 'files', + 'deny' => 'from all' + }, + ], +} + diff --git a/apache/tests/vhost_ip_based.pp b/apache/tests/vhost_ip_based.pp new file mode 100644 index 000000000..dc0fa4f33 --- /dev/null +++ b/apache/tests/vhost_ip_based.pp @@ -0,0 +1,25 @@ +## IP-based vhosts on any listen port +# IP-based vhosts respond to requests on specific IP addresses. + +# Base class. Turn off the default vhosts; we will be declaring +# all vhosts below. +class { 'apache': + default_vhost => false, +} + +# Listen on port 80 and 81; required because the following vhosts +# are not declared with a port parameter. +apache::listen { '80': } +apache::listen { '81': } + +# IP-based vhosts +apache::vhost { 'first.example.com': + ip => '10.0.0.10', + docroot => '/var/www/first', + ip_based => true, +} +apache::vhost { 'second.example.com': + ip => '10.0.0.11', + docroot => '/var/www/second', + ip_based => true, +} diff --git a/apache/tests/vhost_ssl.pp b/apache/tests/vhost_ssl.pp new file mode 100644 index 000000000..8e7a2b279 --- /dev/null +++ b/apache/tests/vhost_ssl.pp @@ -0,0 +1,23 @@ +## SSL-enabled vhosts +# SSL-enabled vhosts respond only to HTTPS queries. + +# Base class. Turn off the default vhosts; we will be declaring +# all vhosts below. +class { 'apache': + default_vhost => false, +} + +# Non-ssl vhost +apache::vhost { 'first.example.com non-ssl': + servername => 'first.example.com', + port => '80', + docroot => '/var/www/first', +} + +# SSL vhost at the same domain +apache::vhost { 'first.example.com ssl': + servername => 'first.example.com', + port => '443', + docroot => '/var/www/first', + ssl => true, +} diff --git a/apache/tests/vhosts_without_listen.pp b/apache/tests/vhosts_without_listen.pp new file mode 100644 index 000000000..e7d6cc036 --- /dev/null +++ b/apache/tests/vhosts_without_listen.pp @@ -0,0 +1,53 @@ +## Declare ip-based and name-based vhosts +# Mixing Name-based vhost with IP-specific vhosts requires `add_listen => +# 'false'` on the non-IP vhosts + +# Base class. Turn off the default vhosts; we will be declaring +# all vhosts below. +class { 'apache': + default_vhost => false, +} + + +# Add two an IP-based vhost on 10.0.0.10, ssl and non-ssl +apache::vhost { 'The first IP-based vhost, non-ssl': + servername => 'first.example.com', + ip => '10.0.0.10', + port => '80', + ip_based => true, + docroot => '/var/www/first', +} +apache::vhost { 'The first IP-based vhost, ssl': + servername => 'first.example.com', + ip => '10.0.0.10', + port => '443', + ip_based => true, + docroot => '/var/www/first-ssl', + ssl => true, +} + +# Two name-based vhost listening on 10.0.0.20 +apache::vhost { 'second.example.com': + ip => '10.0.0.20', + port => '80', + docroot => '/var/www/second', +} +apache::vhost { 'third.example.com': + ip => '10.0.0.20', + port => '80', + docroot => '/var/www/third', +} + +# Two name-based vhosts without IPs specified, so that they will answer on either 10.0.0.10 or 10.0.0.20 . It is requried to declare +# `add_listen => 'false'` to disable declaring "Listen 80" which will conflict +# with the IP-based preceeding vhosts. +apache::vhost { 'fourth.example.com': + port => '80', + docroot => '/var/www/fourth', + add_listen => false, +} +apache::vhost { 'fifth.example.com': + port => '80', + docroot => '/var/www/fifth', + add_listen => false, +} diff --git a/ceilometer/.fixtures.yml b/ceilometer/.fixtures.yml new file mode 100644 index 000000000..6bec88705 --- /dev/null +++ b/ceilometer/.fixtures.yml @@ -0,0 +1,15 @@ +fixtures: + repositories: + 'inifile': 'git://github.com/puppetlabs/puppetlabs-inifile' + 'keystone': + repo: 'git://github.com/stackforge/puppet-keystone.git' + ref: 'origin/stable/havana' + 'mysql': + repo: 'git://github.com/puppetlabs/puppetlabs-mysql.git' + ref: 'origin/0.x' + 'nova': + repo: 'git://github.com/stackforge/puppet-nova.git' + ref: 'origin/stable/havana' + 'stdlib': 'git://github.com/puppetlabs/puppetlabs-stdlib.git' + symlinks: + 'ceilometer': "#{source_dir}" diff --git a/ceilometer/.gitreview b/ceilometer/.gitreview new file mode 100644 index 000000000..ce604084d --- /dev/null +++ b/ceilometer/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=review.openstack.org +port=29418 +project=stackforge/puppet-ceilometer.git +defaultbranch=stable/havana diff --git a/ceilometer/.travis.yml b/ceilometer/.travis.yml new file mode 100644 index 000000000..63844457d --- /dev/null +++ b/ceilometer/.travis.yml @@ -0,0 +1,28 @@ +language: ruby +bundler_args: --without development +before_script: + - echo $PUPPET_GEM_VERSION | grep '2.6' && git clone git://github.com/puppetlabs/puppetlabs-create_resources.git spec/fixtures/modules/create_resources || true +script: "bundle exec rake spec SPEC_OPTS='--format documentation'" +rvm: + - 1.8.7 + - 1.9.3 + - ruby-head +env: + - PUPPET_GEM_VERSION="~> 2.6" + - PUPPET_GEM_VERSION="~> 2.7" + - PUPPET_GEM_VERSION="~> 3.0" + - PUPPET_GEM_VERSION="~> 3.1" +matrix: + allow_failures: + - rvm: ruby-head + exclude: + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7" + - rvm: ruby-head + env: PUPPET_GEM_VERSION="~> 2.7" + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.6" + - rvm: ruby-head + env: PUPPET_GEM_VERSION="~> 2.6" +notifications: + email: false diff --git a/ceilometer/Gemfile b/ceilometer/Gemfile new file mode 100644 index 000000000..d965fa900 --- /dev/null +++ b/ceilometer/Gemfile @@ -0,0 +1,18 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'puppetlabs_spec_helper', :require => false + gem 'puppet-lint', '~> 0.3.2' + gem 'rake', '10.1.1' + gem 'rspec', '< 2.99' + gem 'json' + gem 'webmock' +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/ceilometer/LICENSE b/ceilometer/LICENSE new file mode 100644 index 000000000..dd5b3a58a --- /dev/null +++ b/ceilometer/LICENSE @@ -0,0 +1,174 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/ceilometer/Modulefile b/ceilometer/Modulefile new file mode 100644 index 000000000..7b6bcf935 --- /dev/null +++ b/ceilometer/Modulefile @@ -0,0 +1,14 @@ +name 'puppetlabs-ceilometer' +version '3.1.1' +source 'https://github.com/stackforge/puppet-ceilometer' +author 'enovance' +license 'Apache License, Version 2.0' +summary 'Install/configure Openstack Ceilometer' +description 'Install/configure Openstack Ceilometer' +project_page 'https://launchpad.net/puppet-ceilometer' + +# Dependencies +dependency 'puppetlabs/keystone', '>=3.0.0 <4.0.0' +dependency 'puppetlabs/stdlib', '>= 3.2.0' +dependency 'puppetlabs/inifile', '>=1.0.0 <2.0.0' +dependency 'puppetlabs/mysql', '>=0.9.0 <1.0.0' diff --git a/ceilometer/README.md b/ceilometer/README.md new file mode 100644 index 000000000..51dfb8057 --- /dev/null +++ b/ceilometer/README.md @@ -0,0 +1,114 @@ +ceilometer +========== + +#### Table of Contents + +1. [Overview - What is the ceilometer module?](#overview) +2. [Module Description - What does the module do?](#module-description) +3. [Setup - The basics of getting started with ceilometer](#setup) +4. [Implementation - An under-the-hood peek at what the module is doing](#implementation) +5. [Limitations - OS compatibility, etc.](#limitations) +6. [Development - Guide for contributing to the module](#development) +7. [Contributors - Those with commits](#contributors) +8. [Release Notes - Notes on the most recent updates to the module](#release-notes) + +Overview +-------- + +The ceilometer module is part of [Stackforge](https://github.com/stackforge), an effort by the +OpenStack infrastructure team to provice continuous integration testing and code review for +OpenStack and OpenStack community projects not part of the core software. The module itself +is used to flexibly configure and manage the metering service for OpenStack. + +Module Description +------------------ + +The ceilometer module is an attempt to make Puppet capable of managing the entirety of ceilometer. +This includes manifests to provision the ceilometer api, agents, and database stores. A +ceilometer_config type is supplied to assist in the manipulation of configuration files. + +Setup +----- + +**What the ceilometer module affects** + +* ceilometer, the metering service for OpenStack + +### Installing ceilometer + + example% puppet module install puppetlabs/ceilometer + +### Beginning with ceilometer + +Implementation +-------------- + +### ceilometer + +ceilometer is a combination of Puppet manifests and Ruby code to deliver configuration and +extra functionality through types and providers. + +Limitations +----------- + +* The ceilometer modules have only been tested on RedHat and Ubuntu family systems. + +Development +----------- + +Developer documentation for the entire puppet-openstack project + +* https://wiki.openstack.org/wiki/Puppet-openstack#Developer_documentation + +Contributors +------------ + +* https://github.com/stackforge/puppet-ceilometer/graphs/contributors + +This is the ceilometer module. + +Release Notes +------------- + +** 3.1.1 ** + +* Removed enforcement of glance_control_exchange. +* Fixed user reference in db.pp. +* Allow db fields configuration without need for dbsync for better replicaset support. +* Fixed alarm package parameters Debian/Ubuntu. + +** 3.1.0 ** + +* Fixed package ceilometer-alarm type error on Debian. +* Remove log_dir from params and make logs configurable in init. +* Removed glance_notifications from notification_topic. +* Don't match commented [DEFAULT] section. + +** 3.0.0 ** + +* Initial release of the puppet-ceilometer module. + + +License +------- + +Apache License 2.0 + + Copyright 2012 eNovance + + 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. + +Contact +------- + +techs@enovance.com diff --git a/ceilometer/Rakefile b/ceilometer/Rakefile new file mode 100644 index 000000000..4c2b2ed07 --- /dev/null +++ b/ceilometer/Rakefile @@ -0,0 +1,6 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' + +PuppetLint.configuration.fail_on_warnings = true +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.send('disable_class_parameter_defaults') diff --git a/ceilometer/examples/site.pp b/ceilometer/examples/site.pp new file mode 100644 index 000000000..32cbe25b2 --- /dev/null +++ b/ceilometer/examples/site.pp @@ -0,0 +1,66 @@ +node default { + Exec { + path => ['/usr/bin', '/bin', '/usr/sbin', '/sbin'] + } + + # First, install a mysql server + class { 'mysql::server': } + # And create the database + class { 'ceilometer::db::mysql': + password => 'ceilometer', + } + + # Add the base ceilometer class & parameters + # This class is required by ceilometer agents & api classes + # The metering_secret parameter is mandatory + class { 'ceilometer': + metering_secret => 'darksecret' + } + + # Configure the ceilometer database + # Only needed if ceilometer::agent::central or ceilometer::api are declared + class { 'ceilometer::db': + } + + # Configure ceilometer database with mongodb + + # class { 'ceilometer::db': + # database_connection => 'mongodb://localhost:27017/ceilometer', + # require => Class['mongodb'], + # } + + # Install the ceilometer-api service + # The keystone_password parameter is mandatory + class { 'ceilometer::api': + keystone_password => 'tralalayouyou' + } + + # Set common auth parameters used by all agents (compute/central) + class { 'ceilometer::agent::auth': + auth_url => 'http://localhost:35357/v2.0', + auth_password => 'tralalerotralala' + } + + # Install compute agent + # default: enable + class { 'ceilometer::agent::compute': + } + + # Install central agent + class { 'ceilometer::agent::central': + } + + # Install alarm notifier + class { 'ceilometer::alarm::notifier': + } + + # Install alarm evaluator + class { 'ceilometer::alarm::evaluator': + } + + # Purge 1 month old meters + class { 'ceilometer::expirer': + time_to_live => '2592000' + } + +} diff --git a/ceilometer/lib/puppet/provider/ceilometer_config/ini_setting.rb b/ceilometer/lib/puppet/provider/ceilometer_config/ini_setting.rb new file mode 100644 index 000000000..df0bde5cc --- /dev/null +++ b/ceilometer/lib/puppet/provider/ceilometer_config/ini_setting.rb @@ -0,0 +1,22 @@ +Puppet::Type.type(:ceilometer_config).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def file_path + '/etc/ceilometer/ceilometer.conf' + end + +end diff --git a/ceilometer/lib/puppet/provider/file_line_after/ruby.rb b/ceilometer/lib/puppet/provider/file_line_after/ruby.rb new file mode 100644 index 000000000..0d7f287c9 --- /dev/null +++ b/ceilometer/lib/puppet/provider/file_line_after/ruby.rb @@ -0,0 +1,83 @@ +Puppet::Type.type(:file_line_after).provide(:ruby) do + def exists? + lines.find do |line| + line.chomp == resource[:line].chomp + end + end + + def create + if resource[:match] + handle_create_with_match + elsif resource[:after] + handle_create_with_after + else + append_line + end + end + + def destroy + local_lines = lines + File.open(resource[:path],'w') do |fh| + fh.write(local_lines.reject{|l| l.chomp == resource[:line] }.join('')) + end + end + + private + def lines + # If this type is ever used with very large files, we should + # write this in a different way, using a temp + # file; for now assuming that this type is only used on + # small-ish config files that can fit into memory without + # too much trouble. + @lines ||= File.readlines(resource[:path]) + end + + def handle_create_with_match() + regex = resource[:match] ? Regexp.new(resource[:match]) : nil + match_count = lines.select { |l| regex.match(l) }.size + if match_count > 1 && resource[:multiple].to_s != 'true' + raise Puppet::Error, "More than one line in file '#{resource[:path]}' matches pattern '#{resource[:match]}'" + end + File.open(resource[:path], 'w') do |fh| + lines.each do |l| + fh.puts(regex.match(l) ? resource[:line] : l) + end + + if (match_count == 0) + fh.puts(resource[:line]) + end + end + end + + def handle_create_with_after + regex = Regexp.new(resource[:after]) + + count = lines.count {|l| l.match(regex)} + + case count + when 1 # find the line to put our line after + File.open(resource[:path], 'w') do |fh| + lines.each do |l| + fh.puts(l) + if regex.match(l) then + fh.puts(resource[:line]) + end + end + end + when 0 # append the line to the end of the file + append_line + else + raise Puppet::Error, "#{count} lines match pattern '#{resource[:after]}' in file '#{resource[:path]}'. One or no line must match the pattern." + end + end + + ## + # append the line to the file. + # + # @api private + def append_line + File.open(resource[:path], 'a') do |fh| + fh.puts resource[:line] + end + end +end diff --git a/ceilometer/lib/puppet/type/ceilometer_config.rb b/ceilometer/lib/puppet/type/ceilometer_config.rb new file mode 100644 index 000000000..830e258d8 --- /dev/null +++ b/ceilometer/lib/puppet/type/ceilometer_config.rb @@ -0,0 +1,19 @@ +Puppet::Type.newtype(:ceilometer_config) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from ceilometer.conf' + newvalues(/\S+\/\S+/) + end + + newproperty(:value) do + desc 'The value of the setting to be defined.' + munge do |value| + value = value.to_s.strip + value.capitalize! if value =~ /^(true|false)$/i + value + end + end + +end diff --git a/ceilometer/lib/puppet/type/file_line_after.rb b/ceilometer/lib/puppet/type/file_line_after.rb new file mode 100644 index 000000000..d71d7c298 --- /dev/null +++ b/ceilometer/lib/puppet/type/file_line_after.rb @@ -0,0 +1,79 @@ +Puppet::Type.newtype(:file_line_after) do + + desc <<-EOT + Ensures that a given line is contained within a file. The implementation + matches the full line, including whitespace at the beginning and end. If + the line is not contained in the given file, Puppet will add the line to + ensure the desired state. Multiple resources may be declared to manage + multiple lines in the same file. + + Example: + + file_line_after { 'sudo_rule': + path => '/etc/sudoers', + line => '%sudo ALL=(ALL) ALL', + } + file_line_after { 'sudo_rule_nopw': + path => '/etc/sudoers', + line => '%sudonopw ALL=(ALL) NOPASSWD: ALL', + } + + In this example, Puppet will ensure both of the specified lines are + contained in the file /etc/sudoers. + + EOT + + ensurable do + defaultvalues + defaultto :present + end + + newparam(:name, :namevar => true) do + desc 'An arbitrary name used as the identity of the resource.' + end + + newparam(:match) do + desc 'An optional regular expression to run against existing lines in the file;\n' + + 'if a match is found, we replace that line rather than adding a new line.' + end + + newparam(:multiple) do + desc 'An optional value to determine if match can change multiple lines.' + newvalues(true, false) + end + + newparam(:after) do + desc 'An optional value used to specify the line after which we will add any new lines. (Existing lines are added in place)' + end + + newparam(:line) do + desc 'The line to be appended to the file located by the path parameter.' + end + + newparam(:path) do + desc 'The file Puppet will ensure contains the line specified by the line parameter.' + validate do |value| + unless (Puppet.features.posix? and value =~ /^\//) or (Puppet.features.microsoft_windows? and (value =~ /^.:\// or value =~ /^\/\/[^\/]+\/[^\/]+/)) + raise(Puppet::Error, "File paths must be fully qualified, not '#{value}'") + end + end + end + + # Autorequire the file resource if it's being managed + autorequire(:file) do + self[:path] + end + + validate do + unless self[:line] and self[:path] + raise(Puppet::Error, "Both line and path are required attributes") + end + + if (self[:match]) + unless Regexp.new(self[:match]).match(self[:line]) + raise(Puppet::Error, "When providing a 'match' parameter, the value must be a regex that matches against the value of your 'line' parameter") + end + end + + end +end diff --git a/ceilometer/manifests/agent/auth.pp b/ceilometer/manifests/agent/auth.pp new file mode 100644 index 000000000..9eec3d330 --- /dev/null +++ b/ceilometer/manifests/agent/auth.pp @@ -0,0 +1,62 @@ +# The ceilometer::agent::auth class helps configure common +# auth settings for the agents. +# +# == Parameters +# [*auth_url*] +# the keystone public endpoint +# Optional. Defaults to 'http://localhost:5000/v2.0' +# +# [*auth_region*] +# the keystone region of this node +# Optional. Defaults to 'RegionOne' +# +# [*auth_user*] +# the keystone user for ceilometer services +# Optional. Defaults to 'ceilometer' +# +# [*auth_password*] +# the keystone password for ceilometer services +# Required. +# +# [*auth_tenant_name*] +# the keystone tenant name for ceilometer services +# Optional. Defaults to 'services' +# +# [*auth_tenant_id*] +# the keystone tenant id for ceilometer services. +# Optional. Defaults to empty. +# +# [*auth_cacert*] +# Certificate chain for SSL validation. Optional; Defaults to 'None' +# +class ceilometer::agent::auth ( + $auth_password, + $auth_url = 'http://localhost:5000/v2.0', + $auth_region = 'RegionOne', + $auth_user = 'ceilometer', + $auth_tenant_name = 'services', + $auth_tenant_id = '', + $auth_cacert = undef, +) { + + if ! $auth_cacert { + ceilometer_config { 'service_credentials/os_cacert': ensure => absent } + } else { + ceilometer_config { 'service_credentials/os_cacert': value => $auth_cacert } + } + + ceilometer_config { + 'service_credentials/os_auth_url' : value => $auth_url; + 'service_credentials/os_auth_region' : value => $auth_region; + 'service_credentials/os_username' : value => $auth_user; + 'service_credentials/os_password' : value => $auth_password; + 'service_credentials/os_tenant_name' : value => $auth_tenant_name; + } + + if ($auth_tenant_id != '') { + ceilometer_config { + 'service_credentials/os_tenant_id' : value => $auth_tenant_id; + } + } + +} diff --git a/ceilometer/manifests/agent/central.pp b/ceilometer/manifests/agent/central.pp new file mode 100644 index 000000000..0a0785613 --- /dev/null +++ b/ceilometer/manifests/agent/central.pp @@ -0,0 +1,36 @@ +# Installs/configures the ceilometer central agent +# +# == Parameters +# [*enabled*] +# Should the service be enabled. Optional. Defauls to true +# +class ceilometer::agent::central ( + $enabled = true, +) { + + include ceilometer::params + + Ceilometer_config<||> ~> Service['ceilometer-agent-central'] + + Package['ceilometer-agent-central'] -> Service['ceilometer-agent-central'] + package { 'ceilometer-agent-central': + ensure => installed, + name => $::ceilometer::params::agent_central_package_name, + } + + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + Package['ceilometer-common'] -> Service['ceilometer-agent-central'] + service { 'ceilometer-agent-central': + ensure => $service_ensure, + name => $::ceilometer::params::agent_central_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + } + +} diff --git a/ceilometer/manifests/agent/compute.pp b/ceilometer/manifests/agent/compute.pp new file mode 100644 index 000000000..c07d717af --- /dev/null +++ b/ceilometer/manifests/agent/compute.pp @@ -0,0 +1,77 @@ +# The ceilometer::agent::compute class installs the ceilometer compute agent +# Include this class on all nova compute nodes +# +# == Parameters +# [*enabled*] +# should the service be started or not +# Optional. Defaults to true +# +class ceilometer::agent::compute ( + $enabled = true, +) inherits ceilometer { + + include ceilometer::params + + Ceilometer_config<||> ~> Service['ceilometer-agent-compute'] + + Package['ceilometer-agent-compute'] -> Service['ceilometer-agent-compute'] + package { 'ceilometer-agent-compute': + ensure => installed, + name => $::ceilometer::params::agent_compute_package_name, + } + + if $::ceilometer::params::libvirt_group { + User['ceilometer'] { + groups => ['nova', $::ceilometer::params::libvirt_group] + } + } else { + User['ceilometer'] { + groups => ['nova'] + } + } + + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + Package['ceilometer-common'] -> Service['ceilometer-agent-compute'] + service { 'ceilometer-agent-compute': + ensure => $service_ensure, + name => $::ceilometer::params::agent_compute_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + } + + nova_config { + 'DEFAULT/instance_usage_audit' : value => 'True'; + 'DEFAULT/instance_usage_audit_period' : value => 'hour'; + } + + #NOTE(dprince): This is using a custom (inline) file_line provider + # until this lands upstream: + # https://github.com/puppetlabs/puppetlabs-stdlib/pull/174 + Nova_config<| |> { + before +> File_line_after[ + 'nova-notification-driver-common', + 'nova-notification-driver-ceilometer' + ], + } + + file_line_after { + 'nova-notification-driver-common': + line => + 'notification_driver=nova.openstack.common.notifier.rpc_notifier', + path => '/etc/nova/nova.conf', + after => '^\s*\[DEFAULT\]', + notify => Service['nova-compute']; + 'nova-notification-driver-ceilometer': + line => 'notification_driver=ceilometer.compute.nova_notifier', + path => '/etc/nova/nova.conf', + after => '^\s*\[DEFAULT\]', + notify => Service['nova-compute']; + } + +} diff --git a/ceilometer/manifests/alarm/evaluator.pp b/ceilometer/manifests/alarm/evaluator.pp new file mode 100644 index 000000000..9c36198c8 --- /dev/null +++ b/ceilometer/manifests/alarm/evaluator.pp @@ -0,0 +1,64 @@ +# Installs the ceilometer alarm evaluator service +# +# == Params +# [*enabled*] +# should the service be enabled +# [*evaluation_interval*] +# define the time interval for the alarm evaluator +# [*evaluation_service*] +# define which service use for the evaluator +# [*partition_rpc_topic*] +# define which topic the alarm evaluator should access +# [*record_history*] +# Record alarm change events +# +class ceilometer::alarm::evaluator ( + $enabled = true, + $evaluation_interval = 60, + $evaluation_service = 'ceilometer.alarm.service.SingletonAlarmService', + $partition_rpc_topic = 'alarm_partition_coordination', + $record_history = true, +) { + + include ceilometer::params + + validate_re($evaluation_interval,'^(\d+)$') + + Ceilometer_config<||> ~> Service['ceilometer-alarm-evaluator'] + + Package[$::ceilometer::params::alarm_package_name] -> Service['ceilometer-alarm-evaluator'] + Package[$::ceilometer::params::alarm_package_name] -> Package<| title == 'ceilometer-alarm' |> + + # Workaround for Ubuntu havana, where alarm_package_name = common_package_name + if ( $::ceilometer::params::common_package_name in $::ceilometer::params::alarm_package_name) + { + if ! defined(Package[$::ceilometer::params::common_package_name]) { + package{$::ceilometer::params::common_package_name : } + } + } else { + ensure_packages($::ceilometer::params::alarm_package_name) + } + + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + Package['ceilometer-common'] -> Service['ceilometer-alarm-evaluator'] + + service { 'ceilometer-alarm-evaluator': + ensure => $service_ensure, + name => $::ceilometer::params::alarm_evaluator_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true + } + + ceilometer_config { + 'alarm/evaluation_interval' : value => $evaluation_interval; + 'alarm/evaluation_service' : value => $evaluation_service; + 'alarm/partition_rpc_topic' : value => $partition_rpc_topic; + 'alarm/record_history' : value => $record_history; + } +} diff --git a/ceilometer/manifests/alarm/notifier.pp b/ceilometer/manifests/alarm/notifier.pp new file mode 100644 index 000000000..10d4c68b7 --- /dev/null +++ b/ceilometer/manifests/alarm/notifier.pp @@ -0,0 +1,74 @@ +# Installs the ceilometer alarm notifier service +# +# == Params +# [*enabled*] +# should the service be enabled +# [*notifier_rpc_topic*] +# define on which topic the notifier will have +# access +# [*rest_notifier_certificate_key*] +# define the certificate key for the rest service +# [*rest_notifier_certificate_file*] +# define the certificate file for the rest service +# [*rest_notifier_ssl_verify*] +# should the ssl verify parameter be enabled +# +class ceilometer::alarm::notifier ( + $enabled = true, + $notifier_rpc_topic = undef, + $rest_notifier_certificate_key = undef, + $rest_notifier_certificate_file = undef, + $rest_notifier_ssl_verify = true, +) { + + include ceilometer::params + + validate_bool($rest_notifier_ssl_verify) + + Ceilometer_config<||> ~> Service['ceilometer-alarm-notifier'] + + Package[$::ceilometer::params::alarm_package_name] -> Service['ceilometer-alarm-notifier'] + Package[$::ceilometer::params::alarm_package_name] -> Package<| title == 'ceilometer-alarm' |> + # Workaround for Ubuntu havana, where alarm_package_name = common_package_name + if ( $::ceilometer::params::common_package_name in $::ceilometer::params::alarm_package_name ) { + if ! defined(Package[$::ceilometer::params::common_package_name]) { + package{$::ceilometer::params::common_package_name : } + } + } else { + ensure_packages($::ceilometer::params::alarm_package_name) + } + + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + Package['ceilometer-common'] -> Service['ceilometer-alarm-notifier'] + + service { 'ceilometer-alarm-notifier': + ensure => $service_ensure, + name => $::ceilometer::params::alarm_notifier_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true + } + + if $notifier_rpc_topic != undef { + ceilometer_config { + 'alarm/notifier_rpc_topic' : value => $notifier_rpc_topic; + } + } + if $rest_notifier_certificate_key != undef { + ceilometer_config { + 'alarm/rest_notifier_certificate_key' :value => $rest_notifier_certificate_key; + 'alarm/rest_notifier_ssl_verify' :value => $rest_notifier_ssl_verify; + } + } + if $rest_notifier_certificate_file != undef { + ceilometer_config { + 'alarm/rest_notifier_certificate_file' :value => $rest_notifier_certificate_file; + } + } + +} diff --git a/ceilometer/manifests/api.pp b/ceilometer/manifests/api.pp new file mode 100644 index 000000000..92406a86b --- /dev/null +++ b/ceilometer/manifests/api.pp @@ -0,0 +1,115 @@ +# Installs & configure the ceilometer api service +# +# == Parameters +# [*enabled*] +# should the service be enabled. Optional. Defaults to true +# +# [*keystone_host*] +# keystone's admin endpoint IP/Host. Optional. Defaults to 127.0.0.1 +# +# [*keystone_port*] +# keystone's admin endpoint port. Optional. Defaults to 35357 +# +# [*keystone_auth_admin_prefix*] +# 'path' to the keystone admin endpoint. Optional. Defaults to false (empty) +# Define to a path starting with a '/' and without trailing '/'. +# Eg.: '/keystone/admin' to match keystone::wsgi::apache default. +# +# [*keystone_protocol*] http/https +# Optional. Defaults to https +# +# [*keytone_user*] user to authenticate with +# Optional. Defaults to ceilometer +# +# [*keystone_tenant*] tenant to authenticate with +# Optional. Defaults to services +# +# [*keystone_password*] password to authenticate with +# Mandatory. +# +# [*host*] +# (optional) The ceilometer api bind address +# Defaults to 0.0.0.0 +# +# [*port*] +# (optional) The ceilometer api port +# Defaults to 8777 +# + +class ceilometer::api ( + $enabled = true, + $keystone_host = '127.0.0.1', + $keystone_port = '35357', + $keystone_auth_admin_prefix = false, + $keystone_protocol = 'http', + $keystone_user = 'ceilometer', + $keystone_tenant = 'services', + $keystone_password = false, + $keystone_auth_uri = false, + $host = '0.0.0.0', + $port = '8777' +) { + + include ceilometer::params + + validate_string($keystone_password) + + Ceilometer_config<||> ~> Service['ceilometer-api'] + + Package['ceilometer-api'] -> Ceilometer_config<||> + Package['ceilometer-api'] -> Service['ceilometer-api'] + package { 'ceilometer-api': + ensure => installed, + name => $::ceilometer::params::api_package_name, + } + + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + Package['ceilometer-common'] -> Service['ceilometer-api'] + service { 'ceilometer-api': + ensure => $service_ensure, + name => $::ceilometer::params::api_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + require => Class['ceilometer::db'], + subscribe => Exec['ceilometer-dbsync'] + } + + ceilometer_config { + 'keystone_authtoken/auth_host' : value => $keystone_host; + 'keystone_authtoken/auth_port' : value => $keystone_port; + 'keystone_authtoken/auth_protocol' : value => $keystone_protocol; + 'keystone_authtoken/admin_tenant_name' : value => $keystone_tenant; + 'keystone_authtoken/admin_user' : value => $keystone_user; + 'keystone_authtoken/admin_password' : value => $keystone_password; + 'api/host' : value => $host; + 'api/port' : value => $port; + } + + if $keystone_auth_admin_prefix { + validate_re($keystone_auth_admin_prefix, '^(/.+[^/])?$') + ceilometer_config { + 'keystone_authtoken/auth_admin_prefix': value => $keystone_auth_admin_prefix; + } + } else { + ceilometer_config { + 'keystone_authtoken/auth_admin_prefix': ensure => absent; + } + } + + if $keystone_auth_uri { + ceilometer_config { + 'keystone_authtoken/auth_uri': value => $keystone_auth_uri; + } + } else { + ceilometer_config { + 'keystone_authtoken/auth_uri': value => "${keystone_protocol}://${keystone_host}:5000/"; + } + } + +} diff --git a/ceilometer/manifests/client.pp b/ceilometer/manifests/client.pp new file mode 100644 index 000000000..faa791a18 --- /dev/null +++ b/ceilometer/manifests/client.pp @@ -0,0 +1,20 @@ +# +# Installs the ceilometer python library. +# +# == parameters +# [*ensure*] +# ensure state for pachage. +# +class ceilometer::client ( + $ensure = 'present' +) { + + include ceilometer::params + + package { 'python-ceilometerclient': + ensure => $ensure, + name => $::ceilometer::params::client_package_name, + } + +} + diff --git a/ceilometer/manifests/collector.pp b/ceilometer/manifests/collector.pp new file mode 100644 index 000000000..8ba53219b --- /dev/null +++ b/ceilometer/manifests/collector.pp @@ -0,0 +1,37 @@ +# Installs the ceilometer collector service +# +# == Params +# [*enabled*] +# should the service be enabled +# +class ceilometer::collector ( + $enabled = true, +) { + + include ceilometer::params + + Ceilometer_config<||> ~> Service['ceilometer-collector'] + + Package['ceilometer-collector'] -> Service['ceilometer-collector'] + package { 'ceilometer-collector': + ensure => installed, + name => $::ceilometer::params::collector_package_name, + } + + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + Package['ceilometer-common'] -> Service['ceilometer-collector'] + service { 'ceilometer-collector': + ensure => $service_ensure, + name => $::ceilometer::params::collector_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + require => Class['ceilometer::db'], + subscribe => Exec['ceilometer-dbsync'] + } +} diff --git a/ceilometer/manifests/db.pp b/ceilometer/manifests/db.pp new file mode 100644 index 000000000..15822d8ce --- /dev/null +++ b/ceilometer/manifests/db.pp @@ -0,0 +1,71 @@ +# Configures the ceilometer database +# This class will install the required libraries depending on the driver +# specified in the connection_string parameter +# +# == Parameters +# [*database_connection*] +# the connection string. format: [driver]://[user]:[password]@[host]/[database] +# +# [*sync_db*] +# enable dbsync. +# +class ceilometer::db ( + $database_connection = 'mysql://ceilometer:ceilometer@localhost/ceilometer', + $sync_db = true +) { + + include ceilometer::params + + Package<| title == 'ceilometer-common' |> -> Class['ceilometer::db'] + + validate_re($database_connection, + '(sqlite|mysql|postgresql|mongodb):\/\/(\S+:\S+@\S+\/\S+)?') + + case $database_connection { + /^mysql:\/\//: { + $backend_package = false + include mysql::python + } + /^postgres:\/\//: { + $backend_package = $::ceilometer::params::psycopg_package_name + } + /^mongodb:\/\//: { + $backend_package = $::ceilometer::params::pymongo_package_name + } + /^sqlite:\/\//: { + $backend_package = $::ceilometer::params::sqlite_package_name + } + default: { + fail('Unsupported backend configured') + } + } + + if $sync_db { + $command = $::ceilometer::params::dbsync_command + } else { + $command = '/bin/true' + } + + if $backend_package and !defined(Package[$backend_package]) { + package {'ceilometer-backend-package': + ensure => present, + name => $backend_package, + } + } + + ceilometer_config { + 'database/connection': value => $database_connection; + } + + Ceilometer_config['database/connection'] ~> Exec['ceilometer-dbsync'] + + exec { 'ceilometer-dbsync': + command => $command, + path => '/usr/bin', + user => $::ceilometer::params::user, + refreshonly => true, + logoutput => on_failure, + subscribe => Ceilometer_config['database/connection'] + } + +} diff --git a/ceilometer/manifests/db/mysql.pp b/ceilometer/manifests/db/mysql.pp new file mode 100644 index 000000000..3b045a9b6 --- /dev/null +++ b/ceilometer/manifests/db/mysql.pp @@ -0,0 +1,63 @@ +# The ceilometer::db::mysql class creates a MySQL database for ceilometer. +# It must be used on the MySQL server +# +# == Parameters +# +# [*password*] +# password to connect to the database. Mandatory. +# +# [*dbname*] +# name of the database. Optional. Defaults to ceilometer. +# +# [*user*] +# user to connect to the database. Optional. Defaults to ceilometer. +# +# [*host*] +# the default source host user is allowed to connect from. +# Optional. Defaults to 'localhost' +# +# [*allowed_hosts*] +# other hosts the user is allowd to connect from. +# Optional. Defaults to undef. +# +# [*charset*] +# the database charset. Optional. Defaults to 'latin1' +# +class ceilometer::db::mysql( + $password = false, + $dbname = 'ceilometer', + $user = 'ceilometer', + $host = 'localhost', + $allowed_hosts = undef, + $charset = 'latin1', +) { + + validate_string($password) + + Class['mysql::server'] -> Class['ceilometer::db::mysql'] + Class['ceilometer::db::mysql'] -> Exec<| title == 'ceilometer-dbsync' |> + Mysql::Db[$dbname] ~> Exec<| title == 'ceilometer-dbsync' |> + + mysql::db { $dbname: + user => $user, + password => $password, + host => $host, + charset => $charset, + require => Class['mysql::config'], + } + + # Check allowed_hosts to avoid duplicate resource declarations + if is_array($allowed_hosts) and delete($allowed_hosts,$host) != [] { + $real_allowed_hosts = delete($allowed_hosts,$host) + } elsif is_string($allowed_hosts) and ($allowed_hosts != $host) { + $real_allowed_hosts = $allowed_hosts + } + + if $real_allowed_hosts { + ceilometer::db::mysql::host_access { $real_allowed_hosts: + user => $user, + password => $password, + database => $dbname, + } + } +} diff --git a/ceilometer/manifests/db/mysql/host_access.pp b/ceilometer/manifests/db/mysql/host_access.pp new file mode 100644 index 000000000..e9a802d77 --- /dev/null +++ b/ceilometer/manifests/db/mysql/host_access.pp @@ -0,0 +1,30 @@ +# Allow a user to access the ceilometer database +# +# == Namevar +# The host to allow +# +# == Parameters +# [*user*] +# username to allow +# +# [*password*] +# user password +# +# [*database*] +# the database name +# +define ceilometer::db::mysql::host_access ($user, $password, $database) { + + database_user { "${user}@${name}": + password_hash => mysql_password($password), + provider => 'mysql', + require => Database[$database], + } + + database_grant { "${user}@${name}/${database}": + # TODO figure out which privileges to grant. + privileges => 'all', + provider => 'mysql', + require => Database_user["${user}@${name}"] + } +} diff --git a/ceilometer/manifests/expirer.pp b/ceilometer/manifests/expirer.pp new file mode 100644 index 000000000..597b09cb4 --- /dev/null +++ b/ceilometer/manifests/expirer.pp @@ -0,0 +1,74 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# == Class: ceilometer::expirer +# +# Setups Ceilometer Expirer service to enable TTL feature. +# +# === Parameters +# +# [*time_to_live*] +# (optional) Number of seconds that samples are kept in the database. +# Should be a valid integer +# Defaults to '-1' to disable TTL and keep forever the datas. +# +# [*minute*] +# (optional) Defaults to '1'. +# +# [*hour*] +# (optional) Defaults to '0'. +# +# [*monthday*] +# (optional) Defaults to '*'. +# +# [*month*] +# (optional) Defaults to '*'. +# +# [*weekday*] +# (optional) Defaults to '*'. +# + +class ceilometer::expirer ( + $time_to_live = '-1', + $minute = 1, + $hour = 0, + $monthday = '*', + $month = '*', + $weekday = '*', +) { + + include ceilometer::params + + Package<| title == 'ceilometer-common' |> -> Class['ceilometer::expirer'] + + ceilometer_config { + 'database/time_to_live': value => $time_to_live; + } + + cron { 'ceilometer-expirer': + command => $ceilometer::params::expirer_command, + environment => 'PATH=/bin:/usr/bin:/usr/sbin', + user => 'ceilometer', + minute => $minute, + hour => $hour, + monthday => $monthday, + month => $month, + weekday => $weekday + } + + +} diff --git a/ceilometer/manifests/init.pp b/ceilometer/manifests/init.pp new file mode 100644 index 000000000..32bb423b8 --- /dev/null +++ b/ceilometer/manifests/init.pp @@ -0,0 +1,211 @@ +# Class ceilometer +# +# ceilometer base package & configuration +# +# == parameters +# [*metering_secret*] +# secret key for signing messages. Mandatory. +# [*package_ensure*] +# ensure state for package. Optional. Defaults to 'present' +# [*debug*] +# should the daemons log debug messages. Optional. Defaults to 'False' +# [*log_dir*] +# (optional) directory to which ceilometer logs are sent. +# If set to boolean false, it will not log to any directory. +# Defaults to '/var/log/ceilometer' +# [*verbose*] +# should the daemons log verbose messages. Optional. Defaults to 'False' +# [*use_syslog*] +# (optional) Use syslog for logging +# Defaults to false +# [*log_facility*] +# (optional) Syslog facility to receive log lines. +# Defaults to 'LOG_USER' +# [*rpc_backend*] +# (optional) what rpc/queuing service to use +# Defaults to impl_kombu (rabbitmq) +# [*rabbit_host*] +# ip or hostname of the rabbit server. Optional. Defaults to '127.0.0.1' +# [*rabbit_port*] +# port of the rabbit server. Optional. Defaults to 5672. +# [*rabbit_hosts*] +# array of host:port (used with HA queues). Optional. Defaults to undef. +# If defined, will remove rabbit_host & rabbit_port parameters from config +# [*rabbit_userid*] +# user to connect to the rabbit server. Optional. Defaults to 'guest' +# [*rabbit_password*] +# password to connect to the rabbit_server. Optional. Defaults to empty. +# [*rabbit_virtual_host*] +# virtualhost to use. Optional. Defaults to '/' +# +# [*qpid_hostname*] +# [*qpid_port*] +# [*qpid_username*] +# [*qpid_password*] +# [*qpid_heartbeat*] +# [*qpid_protocol*] +# [*qpid_tcp_nodelay*] +# [*qpid_reconnect*] +# [*qpid_reconnect_timeout*] +# [*qpid_reconnect_limit*] +# [*qpid_reconnect_interval*] +# [*qpid_reconnect_interval_min*] +# [*qpid_reconnect_interval_max*] +# (optional) various QPID options +# + +class ceilometer( + $metering_secret = false, + $package_ensure = 'present', + $debug = false, + $log_dir = '/var/log/ceilometer', + $verbose = false, + $use_syslog = false, + $log_facility = 'LOG_USER', + $rpc_backend = 'ceilometer.openstack.common.rpc.impl_kombu', + $rabbit_host = '127.0.0.1', + $rabbit_port = 5672, + $rabbit_hosts = undef, + $rabbit_userid = 'guest', + $rabbit_password = '', + $rabbit_virtual_host = '/', + $qpid_hostname = 'localhost', + $qpid_port = 5672, + $qpid_username = 'guest', + $qpid_password = 'guest', + $qpid_heartbeat = 60, + $qpid_protocol = 'tcp', + $qpid_tcp_nodelay = true, + $qpid_reconnect = true, + $qpid_reconnect_timeout = 0, + $qpid_reconnect_limit = 0, + $qpid_reconnect_interval_min = 0, + $qpid_reconnect_interval_max = 0, + $qpid_reconnect_interval = 0 +) { + + validate_string($metering_secret) + + + include ceilometer::params + + File { + require => Package['ceilometer-common'], + } + + group { 'ceilometer': + name => 'ceilometer', + require => Package['ceilometer-common'], + } + + user { 'ceilometer': + name => 'ceilometer', + gid => 'ceilometer', + system => true, + require => Package['ceilometer-common'], + } + + file { '/etc/ceilometer/': + ensure => directory, + owner => 'ceilometer', + group => 'ceilometer', + mode => '0750', + } + + file { '/etc/ceilometer/ceilometer.conf': + owner => 'ceilometer', + group => 'ceilometer', + mode => '0640', + } + + if ! defined(Package['ceilometer-common']) { + package { 'ceilometer-common': + ensure => $package_ensure, + name => $::ceilometer::params::common_package_name, + } + } + + Package['ceilometer-common'] -> Ceilometer_config<||> + + if $rpc_backend == 'ceilometer.openstack.common.rpc.impl_kombu' { + + if $rabbit_hosts { + ceilometer_config { 'DEFAULT/rabbit_host': ensure => absent } + ceilometer_config { 'DEFAULT/rabbit_port': ensure => absent } + ceilometer_config { 'DEFAULT/rabbit_hosts': + value => join($rabbit_hosts, ',') + } + } else { + ceilometer_config { 'DEFAULT/rabbit_host': value => $rabbit_host } + ceilometer_config { 'DEFAULT/rabbit_port': value => $rabbit_port } + ceilometer_config { 'DEFAULT/rabbit_hosts': + value => "${rabbit_host}:${rabbit_port}" + } + } + + if size($rabbit_hosts) > 1 { + ceilometer_config { 'DEFAULT/rabbit_ha_queues': value => true } + } else { + ceilometer_config { 'DEFAULT/rabbit_ha_queues': value => false } + } + + ceilometer_config { + 'DEFAULT/rabbit_userid' : value => $rabbit_userid; + 'DEFAULT/rabbit_password' : value => $rabbit_password; + 'DEFAULT/rabbit_virtual_host' : value => $rabbit_virtual_host; + } + } + + if $rpc_backend == 'ceilometer.openstack.common.rpc.impl_qpid' { + + ceilometer_config { + 'DEFAULT/qpid_hostname' : value => $qpid_hostname; + 'DEFAULT/qpid_port' : value => $qpid_port; + 'DEFAULT/qpid_username' : value => $qpid_username; + 'DEFAULT/qpid_password' : value => $qpid_password; + 'DEFAULT/qpid_heartbeat' : value => $qpid_heartbeat; + 'DEFAULT/qpid_protocol' : value => $qpid_protocol; + 'DEFAULT/qpid_tcp_nodelay' : value => $qpid_tcp_nodelay; + 'DEFAULT/qpid_reconnect' : value => $qpid_reconnect; + 'DEFAULT/qpid_reconnect_timeout' : value => $qpid_reconnect_timeout; + 'DEFAULT/qpid_reconnect_limit' : value => $qpid_reconnect_limit; + 'DEFAULT/qpid_reconnect_interval_min': value => $qpid_reconnect_interval_min; + 'DEFAULT/qpid_reconnect_interval_max': value => $qpid_reconnect_interval_max; + 'DEFAULT/qpid_reconnect_interval' : value => $qpid_reconnect_interval; + } + + } + + # Once we got here, we can act as an honey badger on the rpc used. + ceilometer_config { + 'DEFAULT/rpc_backend' : value => $rpc_backend; + 'DEFAULT/metering_secret' : value => $metering_secret; + 'DEFAULT/debug' : value => $debug; + 'DEFAULT/verbose' : value => $verbose; + 'DEFAULT/notification_topics' : value => 'notifications'; + } + + # Log configuration + if $log_dir { + ceilometer_config { + 'DEFAULT/log_dir' : value => $log_dir; + } + } else { + ceilometer_config { + 'DEFAULT/log_dir' : ensure => absent; + } + } + + # Syslog configuration + if $use_syslog { + ceilometer_config { + 'DEFAULT/use_syslog': value => true; + 'DEFAULT/syslog_log_facility': value => $log_facility; + } + } else { + ceilometer_config { + 'DEFAULT/use_syslog': value => false; + } + } + +} diff --git a/ceilometer/manifests/keystone/auth.pp b/ceilometer/manifests/keystone/auth.pp new file mode 100644 index 000000000..811bcfbdf --- /dev/null +++ b/ceilometer/manifests/keystone/auth.pp @@ -0,0 +1,143 @@ +# == Class: ceilometer::keystone::auth +# +# Configures Ceilometer user, service and endpoint in Keystone. +# +# === Parameters +# +# [*password*] +# Password for Ceilometer user. Required. +# +# [*email*] +# Email for Ceilometer user. Optional. Defaults to 'ceilometer@localhost'. +# +# [*auth_name*] +# Username for Ceilometer service. Optional. Defaults to 'ceilometer'. +# +# [*configure_endpoint*] +# Should Ceilometer endpoint be configured? Optional. Defaults to 'true'. +# +# [*service_type*] +# Type of service. Optional. Defaults to 'metering'. +# +# [*public_address*] +# Public address for endpoint. Optional. Defaults to '127.0.0.1'. +# +# [*admin_address*] +# Admin address for endpoint. Optional. Defaults to '127.0.0.1'. +# +# [*internal_address*] +# Internal address for endpoint. Optional. Defaults to '127.0.0.1'. +# +# [*port*] +# Default port for enpoints. Optional. Defaults to '8777'. +# +# [*region*] +# Region for endpoint. Optional. Defaults to 'RegionOne'. +# +# [*tenant*] +# Tenant for Ceilometer user. Optional. Defaults to 'services'. +# +# [*public_protocol*] +# Protocol for public endpoint. Optional. Defaults to 'http'. +# +# [*admin_protocol*] +# Protocol for admin endpoint. Optional. Defaults to 'http'. +# +# [*internal_protocol*] +# Protocol for public endpoint. Optional. Defaults to 'http'. +# +# [*public_url*] +# The endpoint's public url. +# Optional. Defaults to $public_protocol://$public_address:$port +# This url should *not* contain any API version and should have +# no trailing '/' +# Setting this variable overrides other $public_* parameters. +# +# [*admin_url*] +# The endpoint's admin url. +# Optional. Defaults to $admin_protocol://$admin_address:$port +# This url should *not* contain any API version and should have +# no trailing '/' +# Setting this variable overrides other $admin_* parameters. +# +# [*internal_url*] +# The endpoint's internal url. +# Optional. Defaults to $internal_protocol://$internal_address:$port +# This url should *not* contain any API version and should have +# no trailing '/' +# Setting this variable overrides other $internal_* parameters. +# +class ceilometer::keystone::auth ( + $password = false, + $email = 'ceilometer@localhost', + $auth_name = 'ceilometer', + $service_type = 'metering', + $public_address = '127.0.0.1', + $admin_address = '127.0.0.1', + $internal_address = '127.0.0.1', + $port = '8777', + $region = 'RegionOne', + $tenant = 'services', + $public_protocol = 'http', + $admin_protocol = 'http', + $internal_protocol = 'http', + $configure_endpoint = true, + $public_url = undef, + $admin_url = undef, + $internal_url = undef, +) { + + validate_string($password) + + if $public_url { + $public_url_real = $public_url + } else { + $public_url_real = "${public_protocol}://${public_address}:${port}" + } + + if $admin_url { + $admin_url_real = $admin_url + } else { + $admin_url_real = "${admin_protocol}://${admin_address}:${port}" + } + + if $internal_url { + $internal_url_real = $internal_url + } else { + $internal_url_real = "${internal_protocol}://${internal_address}:${port}" + } + + Keystone_user_role["${auth_name}@${tenant}"] ~> + Service <| name == 'ceilometer-api' |> + + keystone_user { $auth_name: + ensure => present, + password => $password, + email => $email, + tenant => $tenant, + } + if !defined(Keystone_role['ResellerAdmin']) { + keystone_role { 'ResellerAdmin': + ensure => present, + } + } + keystone_user_role { "${auth_name}@${tenant}": + ensure => present, + roles => ['admin', 'ResellerAdmin'], + require => Keystone_role['ResellerAdmin'], + } + keystone_service { $auth_name: + ensure => present, + type => $service_type, + description => 'Openstack Metering Service', + } + if $configure_endpoint { + keystone_endpoint { "${region}/${auth_name}": + ensure => present, + public_url => $public_url_real, + admin_url => $admin_url_real, + internal_url => $internal_url_real, + } + } +} + diff --git a/ceilometer/manifests/params.pp b/ceilometer/manifests/params.pp new file mode 100644 index 000000000..4f64ac3a0 --- /dev/null +++ b/ceilometer/manifests/params.pp @@ -0,0 +1,75 @@ +# Parameters for puppet-ceilometer +# +class ceilometer::params { + + $dbsync_command = 'ceilometer-dbsync --config-file=/etc/ceilometer/ceilometer.conf' + $expirer_command = 'ceilometer-expirer' + $user = 'ceilometer' + + case $::osfamily { + 'RedHat': { + # package names + $agent_central_package_name = 'openstack-ceilometer-central' + $agent_compute_package_name = 'openstack-ceilometer-compute' + $api_package_name = 'openstack-ceilometer-api' + $collector_package_name = 'openstack-ceilometer-collector' + $alarm_package_name = ['openstack-ceilometer-alarm'] + $common_package_name = 'openstack-ceilometer-common' + $client_package_name = 'python-ceilometerclient' + # service names + $agent_central_service_name = 'openstack-ceilometer-central' + $agent_compute_service_name = 'openstack-ceilometer-compute' + $api_service_name = 'openstack-ceilometer-api' + $collector_service_name = 'openstack-ceilometer-collector' + $alarm_notifier_service_name = 'openstack-ceilometer-alarm-notifier' + $alarm_evaluator_service_name = 'openstack-ceilometer-alarm-evaluator' + $pymongo_package_name = 'python-pymongo' + $psycopg_package_name = 'python-psycopg2' + # db packages + if $::operatingsystem == 'Fedora' and $::operatingsystemrelease >= 18 { + # fallback to stdlib version, not provided on fedora + $sqlite_package_name = undef + } else { + $sqlite_package_name = 'python-sqlite2' + } + + } + 'Debian': { + # package names + $agent_central_package_name = 'ceilometer-agent-central' + $agent_compute_package_name = 'ceilometer-agent-compute' + $api_package_name = 'ceilometer-api' + $collector_package_name = 'ceilometer-collector' + $common_package_name = 'ceilometer-common' + $client_package_name = 'python-ceilometerclient' + # service names + $agent_central_service_name = 'ceilometer-agent-central' + $agent_compute_service_name = 'ceilometer-agent-compute' + $api_service_name = 'ceilometer-api' + $collector_service_name = 'ceilometer-collector' + $alarm_notifier_service_name = 'ceilometer-alarm-notifier' + $alarm_evaluator_service_name = 'ceilometer-alarm-evaluator' + # db packages + $psycopg_package_name = 'python-psycopg2' + $pymongo_package_name = 'python-pymongo' + $sqlite_package_name = 'python-pysqlite2' + + # Operating system specific + case $::operatingsystem { + 'Ubuntu': { + $libvirt_group = 'libvirtd' + $alarm_package_name = ['ceilometer-common'] + } + default: { + $libvirt_group = 'libvirt' + $alarm_package_name = ['ceilometer-alarm-notifier','ceilometer-alarm-evaluator'] + } + } + } + default: { + fail("Unsupported osfamily: ${::osfamily} operatingsystem: \ +${::operatingsystem}, module ${module_name} only support osfamily \ +RedHat and Debian") + } + } +} diff --git a/ceilometer/spec/classes/ceilometer_agent_auth_spec.rb b/ceilometer/spec/classes/ceilometer_agent_auth_spec.rb new file mode 100644 index 000000000..ec3b5823b --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_agent_auth_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'ceilometer::agent::auth' do + + let :pre_condition do + "class { 'ceilometer': metering_secret => 's3cr3t' }" + end + + let :params do + { :auth_url => 'http://localhost:5000/v2.0', + :auth_region => 'RegionOne', + :auth_user => 'ceilometer', + :auth_password => 'password', + :auth_tenant_name => 'services', + :enabled => true, + } + end + + shared_examples_for 'ceilometer-agent-auth' do + + it 'configures authentication' do + should contain_ceilometer_config('service_credentials/os_auth_url').with_value('http://localhost:5000/v2.0') + should contain_ceilometer_config('service_credentials/os_auth_region').with_value('RegionOne') + should contain_ceilometer_config('service_credentials/os_username').with_value('ceilometer') + should contain_ceilometer_config('service_credentials/os_password').with_value('password') + should contain_ceilometer_config('service_credentials/os_tenant_name').with_value('services') + should contain_ceilometer_config('service_credentials/os_cacert').with(:ensure => 'absent') + end + + context 'when overriding parameters' do + before do + params.merge!(:auth_cacert => '/tmp/dummy.pem') + end + it { should contain_ceilometer_config('service_credentials/os_cacert').with_value(params[:auth_cacert]) } + end + + end + +end diff --git a/ceilometer/spec/classes/ceilometer_agent_central_spec.rb b/ceilometer/spec/classes/ceilometer_agent_central_spec.rb new file mode 100644 index 000000000..5fab4c924 --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_agent_central_spec.rb @@ -0,0 +1,68 @@ +require 'spec_helper' + +describe 'ceilometer::agent::central' do + + let :pre_condition do + "class { 'ceilometer': metering_secret => 's3cr3t' }" + end + + let :params do + { :enabled => true } + end + + shared_examples_for 'ceilometer-agent-central' do + + it { should contain_class('ceilometer::params') } + + it 'installs ceilometer-agent-central package' do + should contain_package('ceilometer-agent-central').with( + :ensure => 'installed', + :name => platform_params[:agent_package_name], + :before => 'Service[ceilometer-agent-central]' + ) + end + + it 'ensures ceilometer-common is installed before the service' do + should contain_package('ceilometer-common').with( + :before => /Service\[ceilometer-agent-central\]/ + ) + end + + it 'configures ceilometer-agent-central service' do + should contain_service('ceilometer-agent-central').with( + :ensure => 'running', + :name => platform_params[:agent_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true + ) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :agent_package_name => 'ceilometer-agent-central', + :agent_service_name => 'ceilometer-agent-central' } + end + + it_configures 'ceilometer-agent-central' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :agent_package_name => 'openstack-ceilometer-central', + :agent_service_name => 'openstack-ceilometer-central' } + end + + it_configures 'ceilometer-agent-central' + end +end diff --git a/ceilometer/spec/classes/ceilometer_agent_compute_spec.rb b/ceilometer/spec/classes/ceilometer_agent_compute_spec.rb new file mode 100644 index 000000000..3d5ed9aab --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_agent_compute_spec.rb @@ -0,0 +1,111 @@ +require 'spec_helper' + +describe 'ceilometer::agent::compute' do + + let :pre_condition do + "include nova\n" + + "include nova::compute\n" + + "class { 'ceilometer': metering_secret => 's3cr3t' }" + end + + let :params do + { :enabled => true } + end + + shared_examples_for 'ceilometer-agent-compute' do + + it { should contain_class('ceilometer::params') } + + it 'installs ceilometer-agent-compute package' do + should contain_package('ceilometer-agent-compute').with( + :ensure => 'installed', + :name => platform_params[:agent_package_name], + :before => 'Service[ceilometer-agent-compute]' + ) + end + + it 'adds ceilometer user to nova group and, if required, to libvirt group' do + if platform_params[:libvirt_group] + should contain_user('ceilometer').with_groups(['nova', "#{platform_params[:libvirt_group]}"]) + else + should contain_user('ceilometer').with_groups('nova') + end + end + + it 'ensures ceilometer-common is installed before the service' do + should contain_package('ceilometer-common').with( + :before => /Service\[ceilometer-agent-compute\]/ + ) + end + + it 'configures ceilometer-agent-compute service' do + should contain_service('ceilometer-agent-compute').with( + :ensure => 'running', + :name => platform_params[:agent_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true + ) + end + + it 'configures instance usage audit in nova' do + should contain_nova_config('DEFAULT/instance_usage_audit').with_value('True') + should contain_nova_config('DEFAULT/instance_usage_audit_period').with_value('hour') + end + + it 'configures nova notification driver' do + should contain_file_line_after('nova-notification-driver-common').with( + :line => 'notification_driver=nova.openstack.common.notifier.rpc_notifier', + :path => '/etc/nova/nova.conf', + :notify => 'Service[nova-compute]' + ) + should contain_file_line_after('nova-notification-driver-ceilometer').with( + :line => 'notification_driver=ceilometer.compute.nova_notifier', + :path => '/etc/nova/nova.conf', + :notify => 'Service[nova-compute]' + ) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :agent_package_name => 'ceilometer-agent-compute', + :agent_service_name => 'ceilometer-agent-compute' } + end + + context 'on Ubuntu operating systems' do + before do + facts.merge!( :operatingsystem => 'Ubuntu' ) + platform_params.merge!( :libvirt_group => 'libvirtd' ) + end + + it_configures 'ceilometer-agent-compute' + end + + context 'on other operating systems' do + before do + platform_params.merge!( :libvirt_group => 'libvirt' ) + end + + it_configures 'ceilometer-agent-compute' + end + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :agent_package_name => 'openstack-ceilometer-compute', + :agent_service_name => 'openstack-ceilometer-compute' } + end + + it_configures 'ceilometer-agent-compute' + end +end diff --git a/ceilometer/spec/classes/ceilometer_alarm_evaluator_spec.rb b/ceilometer/spec/classes/ceilometer_alarm_evaluator_spec.rb new file mode 100644 index 000000000..8f4380ad7 --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_alarm_evaluator_spec.rb @@ -0,0 +1,116 @@ +require 'spec_helper' + +describe 'ceilometer::alarm::evaluator' do + + let :pre_condition do + "class { 'ceilometer': metering_secret => 's3cr3t' }" + end + + let :params do + { :evaluation_interval => 60, + :evaluation_service => 'ceilometer.alarm.service.SingletonAlarmService', + :partition_rpc_topic => 'alarm_partition_coordination', + :record_history => true, + :enabled => true, + } + end + + shared_examples_for 'ceilometer-alarm-evaluator' do + it { should contain_class('ceilometer::params') } + + it 'installs ceilometer-alarm package' do + should contain_package(platform_params[:alarm_evaluator_package_name]).with( + :ensure => 'present', + :name => platform_params[:alarm_evaluator_package_name] + ) + end + + it 'ensures ceilometer-common is installed before the service' do + should contain_package('ceilometer-common').with( + :before => /Service\[ceilometer-alarm-evaluator\]/ + ) + end + + it 'configures ceilometer-alarm-evaluator service' do + should contain_service('ceilometer-alarm-evaluator').with( + :ensure => 'running', + :name => platform_params[:alarm_evaluator_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true + ) + end + + + it 'configures alarm evaluator' do + should contain_ceilometer_config('alarm/evaluation_interval').with_value( params[:evaluation_interval] ) + should contain_ceilometer_config('alarm/evaluation_service').with_value( params[:evaluation_service] ) + should contain_ceilometer_config('alarm/partition_rpc_topic').with_value( params[:partition_rpc_topic] ) + should contain_ceilometer_config('alarm/record_history').with_value( params[:record_history] ) + end + + context 'when overriding parameters' do + before do + params.merge!(:evaluation_interval => 80, + :partition_rpc_topic => 'alarm_partition_coordination', + :record_history => false, + :evaluation_service => 'ceilometer.alarm.service.SingletonTestAlarmService') + end + it { should contain_ceilometer_config('alarm/evaluation_interval').with_value(params[:evaluation_interval]) } + it { should contain_ceilometer_config('alarm/evaluation_service').with_value(params[:evaluation_service]) } + it { should contain_ceilometer_config('alarm/record_history').with_value(params[:record_history]) } + it { should contain_ceilometer_config('alarm/partition_rpc_topic').with_value(params[:partition_rpc_topic]) } + end + + context 'when override the evaluation interval with a non numeric value' do + before do + params.merge!(:evaluation_interval => 'NaN') + end + + it { expect { should contain_ceilometer_config('alarm/evaluation_interval') }.to\ + raise_error(Puppet::Error, /validate_re\(\): .* does not match/) } + end + end + + context 'on Debian operating system' do + let :facts do + { :osfamily => 'Debian', + :operatingsystem => 'Debian' } + end + + let :platform_params do + { :alarm_evaluator_package_name => 'ceilometer-common', + :alarm_evaluator_service_name => 'ceilometer-alarm-evaluator' } + end + + it_configures 'ceilometer-alarm-evaluator' + end + + context 'on Ubuntu operating system' do + let :facts do + { :osfamily => 'Debian', + :operatingsystem => 'Ubuntu' } + end + + let :platform_params do + { :alarm_evaluator_package_name => 'ceilometer-common', + :alarm_evaluator_service_name => 'ceilometer-alarm-evaluator' } + end + + it_configures 'ceilometer-alarm-evaluator' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :alarm_evaluator_package_name => 'openstack-ceilometer-alarm', + :alarm_evaluator_service_name => 'openstack-ceilometer-alarm-evaluator' } + end + + it_configures 'ceilometer-alarm-evaluator' + end + +end diff --git a/ceilometer/spec/classes/ceilometer_alarm_notifier_spec.rb b/ceilometer/spec/classes/ceilometer_alarm_notifier_spec.rb new file mode 100644 index 000000000..3722abf0d --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_alarm_notifier_spec.rb @@ -0,0 +1,109 @@ +require 'spec_helper' + +describe 'ceilometer::alarm::notifier' do + + let :pre_condition do + "class { 'ceilometer': metering_secret => 's3cr3t' }" + end + + let :params do + { + #:notifier_rpc_topic => 'UNSET', + #:rest_notifier_certificate_key => 'UNSET', + #:rest_notifier_certificate_file => 'UNSET', + #:rest_notifier_ssl_verify => true, + :enabled => true, + } + end + + shared_examples_for 'ceilometer-alarm-notifier' do + it { should contain_class('ceilometer::params') } + + it 'installs ceilometer-alarm package' do + should contain_package(platform_params[:alarm_notifier_package_name]).with( + :ensure => 'present', + :name => platform_params[:alarm_notifier_package_name] + ) + end + + it 'ensures ceilometer-common is installed before the service' do + should contain_package('ceilometer-common').with( + :before => /Service\[ceilometer-alarm-notifier\]/ + ) + end + + it 'configures ceilometer-alarm-notifier service' do + should contain_service('ceilometer-alarm-notifier').with( + :ensure => 'running', + :name => platform_params[:alarm_notifier_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true + ) + end + + + it 'configures alarm notifier' do + should_not contain_ceilometer_config('alarm/notifier_rpc_topic') + should_not contain_ceilometer_config('alarm/rest_notifier_certificate_key') + should_not contain_ceilometer_config('alarm/rest_notifier_certificate_file') + should_not contain_ceilometer_config('alarm/rest_notifier_ssl_verify') + end + + context 'when overriding parameters' do + before do + params.merge!(:notifier_rpc_topic => 'alarm_notifier', + :rest_notifier_certificate_key => '0xdeadbeef', + :rest_notifier_certificate_file => '/var/file', + :rest_notifier_ssl_verify => true) + end + it { should contain_ceilometer_config('alarm/notifier_rpc_topic').with_value(params[:notifier_rpc_topic]) } + it { should contain_ceilometer_config('alarm/rest_notifier_certificate_key').with_value(params[:rest_notifier_certificate_key]) } + it { should contain_ceilometer_config('alarm/rest_notifier_certificate_file').with_value(params[:rest_notifier_certificate_file]) } + it { should contain_ceilometer_config('alarm/rest_notifier_ssl_verify').with_value(params[:rest_notifier_ssl_verify]) } + end + + end + + context 'on Debian operating system' do + let :facts do + { :osfamily => 'Debian', + :operatingsystem => 'Debian' } + end + + let :platform_params do + { :alarm_notifier_package_name => 'ceilometer-common', + :alarm_notifier_service_name => 'ceilometer-alarm-notifier' } + end + + it_configures 'ceilometer-alarm-notifier' + end + + context 'on Ubuntu operating system' do + let :facts do + { :osfamily => 'Debian', + :operatingsystem => 'Ubuntu' } + end + + let :platform_params do + { :alarm_notifier_package_name => 'ceilometer-common', + :alarm_notifier_service_name => 'ceilometer-alarm-notifier' } + end + + it_configures 'ceilometer-alarm-notifier' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :alarm_notifier_package_name => 'openstack-ceilometer-alarm', + :alarm_notifier_service_name => 'openstack-ceilometer-alarm-notifier' } + end + + it_configures 'ceilometer-alarm-notifier' + end + +end diff --git a/ceilometer/spec/classes/ceilometer_api_spec.rb b/ceilometer/spec/classes/ceilometer_api_spec.rb new file mode 100644 index 000000000..546f92f91 --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_api_spec.rb @@ -0,0 +1,127 @@ +require 'spec_helper' + +describe 'ceilometer::api' do + + let :pre_condition do + "class { 'ceilometer': metering_secret => 's3cr3t' }" + end + + let :params do + { :enabled => true, + :keystone_host => '127.0.0.1', + :keystone_port => '35357', + :keystone_protocol => 'http', + :keystone_user => 'ceilometer', + :keystone_password => 'ceilometer-passw0rd', + :keystone_tenant => 'services', + :host => '0.0.0.0', + :port => '8777' + } + end + + shared_examples_for 'ceilometer-api' do + + context 'without required parameter keystone_password' do + before { params.delete(:keystone_password) } + it { expect { should raise_error(Puppet::Error) } } + end + + it { should contain_class('ceilometer::params') } + + it 'installs ceilometer-api package' do + should contain_package('ceilometer-api').with( + :ensure => 'installed', + :name => platform_params[:api_package_name] + ) + end + + it 'configures ceilometer-api service' do + should contain_service('ceilometer-api').with( + :ensure => 'running', + :name => platform_params[:api_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true, + :require => 'Class[Ceilometer::Db]', + :subscribe => 'Exec[ceilometer-dbsync]' + ) + end + + it 'configures keystone authentication middleware' do + should contain_ceilometer_config('keystone_authtoken/auth_host').with_value( params[:keystone_host] ) + should contain_ceilometer_config('keystone_authtoken/auth_port').with_value( params[:keystone_port] ) + should contain_ceilometer_config('keystone_authtoken/auth_protocol').with_value( params[:keystone_protocol] ) + should contain_ceilometer_config('keystone_authtoken/admin_tenant_name').with_value( params[:keystone_tenant] ) + should contain_ceilometer_config('keystone_authtoken/admin_user').with_value( params[:keystone_user] ) + should contain_ceilometer_config('keystone_authtoken/admin_password').with_value( params[:keystone_password] ) + should contain_ceilometer_config('keystone_authtoken/auth_admin_prefix').with_ensure('absent') + should contain_ceilometer_config('keystone_authtoken/auth_uri').with_value( params[:keystone_protocol] + "://" + params[:keystone_host] + ":5000/" ) + should contain_ceilometer_config('api/host').with_value( params[:host] ) + should contain_ceilometer_config('api/port').with_value( params[:port] ) + end + + context 'when specifying keystone_auth_admin_prefix' do + describe 'with a correct value' do + before { params['keystone_auth_admin_prefix'] = '/keystone/admin' } + it { should contain_ceilometer_config('keystone_authtoken/auth_admin_prefix').with_value('/keystone/admin') } + end + + [ + '/keystone/', + 'keystone/', + 'keystone', + '/keystone/admin/', + 'keystone/admin/', + 'keystone/admin' + ].each do |auth_admin_prefix| + describe "with an incorrect value #{auth_admin_prefix}" do + before { params['keystone_auth_admin_prefix'] = auth_admin_prefix } + + it { expect { should contain_ceilomete_config('keystone_authtoken/auth_admin_prefix') }.to \ + raise_error(Puppet::Error, /validate_re\(\): "#{auth_admin_prefix}" does not match/) } + end + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :api_package_name => 'ceilometer-api', + :api_service_name => 'ceilometer-api' } + end + + it_configures 'ceilometer-api' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :api_package_name => 'openstack-ceilometer-api', + :api_service_name => 'openstack-ceilometer-api' } + end + + it_configures 'ceilometer-api' + end + + describe 'with custom auth_uri' do + let :facts do + { :osfamily => 'RedHat' } + end + before do + params.merge!({ + :keystone_auth_uri => 'https://foo.bar:1234/', + }) + end + it 'should configure custom auth_uri correctly' do + should contain_ceilometer_config('keystone_authtoken/auth_uri').with_value( 'https://foo.bar:1234/' ) + end + end + +end diff --git a/ceilometer/spec/classes/ceilometer_client_spec.rb b/ceilometer/spec/classes/ceilometer_client_spec.rb new file mode 100644 index 000000000..777c931e9 --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_client_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe 'ceilometer::client' do + + shared_examples_for 'ceilometer client' do + + it { should contain_class('ceilometer::params') } + + it 'installs ceilometer client package' do + should contain_package('python-ceilometerclient').with( + :ensure => 'present', + :name => platform_params[:client_package_name] + ) + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :client_package_name => 'python-ceilometerclient' } + end + + it_configures 'ceilometer client' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :client_package_name => 'python-ceilometerclient' } + end + + it_configures 'ceilometer client' + end +end diff --git a/ceilometer/spec/classes/ceilometer_collector_spec.rb b/ceilometer/spec/classes/ceilometer_collector_spec.rb new file mode 100644 index 000000000..c89227c3d --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_collector_spec.rb @@ -0,0 +1,58 @@ +require 'spec_helper' + +describe 'ceilometer::collector' do + + let :pre_condition do + "class { 'ceilometer': metering_secret => 's3cr3t' }" + end + + shared_examples_for 'ceilometer-collector' do + + it { should contain_class('ceilometer::params') } + + it 'installs ceilometer-collector package' do + should contain_package('ceilometer-collector').with( + :ensure => 'installed', + :name => platform_params[:collector_package_name] + ) + end + + it 'configures ceilometer-collector service' do + should contain_service('ceilometer-collector').with( + :ensure => 'running', + :name => platform_params[:collector_service_name], + :enable => true, + :hasstatus => true, + :hasrestart => true, + :require => 'Class[Ceilometer::Db]', + :subscribe => 'Exec[ceilometer-dbsync]' + ) + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :collector_package_name => 'ceilometer-collector', + :collector_service_name => 'ceilometer-collector' } + end + + it_configures 'ceilometer-collector' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :collector_package_name => 'openstack-ceilometer-collector', + :collector_service_name => 'openstack-ceilometer-collector' } + end + + it_configures 'ceilometer-collector' + end +end diff --git a/ceilometer/spec/classes/ceilometer_db_mysql_spec.rb b/ceilometer/spec/classes/ceilometer_db_mysql_spec.rb new file mode 100644 index 000000000..04241986b --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_db_mysql_spec.rb @@ -0,0 +1,110 @@ +require 'spec_helper' + +describe 'ceilometer::db::mysql' do + + let :pre_condition do + 'include mysql::server' + end + + let :params do + { :password => 's3cr3t', + :dbname => 'ceilometer', + :user => 'ceilometer', + :host => 'localhost', + :charset => 'latin1' + } + end + + shared_examples_for 'ceilometer mysql database' do + + context 'when omiting the required parameter password' do + before { params.delete(:password) } + it { expect { should raise_error(Puppet::Error) } } + end + + it 'creates a mysql database' do + should contain_mysql__db( params[:dbname] ).with( + :user => params[:user], + :password => params[:password], + :host => params[:host], + :charset => params[:charset], + :require => 'Class[Mysql::Config]' + ) + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'ceilometer mysql database' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'ceilometer mysql database' + end + + describe "overriding allowed_hosts param to array" do + let :facts do + { :osfamily => "Debian" } + end + let :params do + { + :password => 'ceilometerpass', + :allowed_hosts => ['localhost','%'] + } + end + + it {should_not contain_ceilometer__db__mysql__host_access("localhost").with( + :user => 'ceilometer', + :password => 'ceilometerpass', + :database => 'ceilometer' + )} + it {should contain_ceilometer__db__mysql__host_access("%").with( + :user => 'ceilometer', + :password => 'ceilometerpass', + :database => 'ceilometer' + )} + end + + describe "overriding allowed_hosts param to string" do + let :facts do + { :osfamily => 'RedHat' } + end + let :params do + { + :password => 'ceilometerpass2', + :allowed_hosts => '192.168.1.1' + } + end + + it {should contain_ceilometer__db__mysql__host_access("192.168.1.1").with( + :user => 'ceilometer', + :password => 'ceilometerpass2', + :database => 'ceilometer' + )} + end + + describe "overriding allowed_hosts param equals to host param " do + let :facts do + { :osfamily => 'RedHat' } + end + let :params do + { + :password => 'ceilometerpass2', + :allowed_hosts => 'localhost' + } + end + + it {should_not contain_ceilometer__db__mysql__host_access("localhost").with( + :user => 'ceilometer', + :password => 'ceilometerpass2', + :database => 'ceilometer' + )} + end +end diff --git a/ceilometer/spec/classes/ceilometer_db_spec.rb b/ceilometer/spec/classes/ceilometer_db_spec.rb new file mode 100644 index 000000000..0dc88dcd3 --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_db_spec.rb @@ -0,0 +1,168 @@ +require 'spec_helper' + +describe 'ceilometer::db' do + + # debian has "python-pymongo" + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :params do + { :database_connection => 'mongodb://localhost:1234/ceilometer', + :sync_db => true } + end + + it { should contain_class('ceilometer::params') } + + it 'installs python-mongodb package' do + should contain_package('ceilometer-backend-package').with( + :ensure => 'present', + :name => 'python-pymongo') + should contain_ceilometer_config('database/connection').with_value('mongodb://localhost:1234/ceilometer') + end + + it 'runs ceilometer-dbsync' do + should contain_exec('ceilometer-dbsync').with( + :command => 'ceilometer-dbsync --config-file=/etc/ceilometer/ceilometer.conf', + :path => '/usr/bin', + :refreshonly => 'true', + :user => 'ceilometer', + :logoutput => 'on_failure' + ) + end + end + + # Fedora > 18 has python-pymongo too + context 'on Redhat platforms' do + let :facts do + { :osfamily => 'Redhat', + :operatingsystem => 'Fedora', + :operatingsystemrelease => 18 + } + end + + let :params do + { :database_connection => 'mongodb://localhost:1234/ceilometer', + :sync_db => false } + end + + it { should contain_class('ceilometer::params') } + + it 'installs pymongo package' do + should contain_package('ceilometer-backend-package').with( + :ensure => 'present', + :name => 'python-pymongo') + should contain_ceilometer_config('database/connection').with_value('mongodb://localhost:1234/ceilometer') + end + + it 'runs ceilometer-dbsync' do + should contain_exec('ceilometer-dbsync').with( + :command => '/bin/true', + :path => '/usr/bin', + :refreshonly => 'true', + :user => 'ceilometer', + :logoutput => 'on_failure' + ) + end + end + + # RHEL has python-pymongo too + context 'on Redhat platforms' do + let :facts do + { :osfamily => 'Redhat', + :operatingsystem => 'CentOS', + :operatingsystemrelease => 6.4 + } + end + + let :params do + { :database_connection => 'mongodb://localhost:1234/ceilometer', + :sync_db => true } + end + + it { should contain_class('ceilometer::params') } + + it 'installs pymongo package' do + should contain_package('ceilometer-backend-package').with( + :ensure => 'present', + :name => 'python-pymongo') + end + + it 'runs ceilometer-dbsync' do + should contain_exec('ceilometer-dbsync').with( + :command => 'ceilometer-dbsync --config-file=/etc/ceilometer/ceilometer.conf', + :path => '/usr/bin', + :refreshonly => 'true', + :user => 'ceilometer', + :logoutput => 'on_failure' + ) + end + end + + # RHEL has python-sqlite2 + context 'on Redhat platforms' do + let :facts do + { :osfamily => 'Redhat', + :operatingsystem => 'CentOS', + :operatingsystemrelease => 6.4 + } + end + + let :params do + { :database_connection => 'sqlite:///var/lib/ceilometer.db', + :sync_db => false } + end + + it { should contain_class('ceilometer::params') } + + it 'installs pymongo package' do + should contain_package('ceilometer-backend-package').with( + :ensure => 'present', + :name => 'python-sqlite2') + should contain_ceilometer_config('database/connection').with_value('sqlite:///var/lib/ceilometer.db') + end + + it 'runs ceilometer-dbsync' do + should contain_exec('ceilometer-dbsync').with( + :command => '/bin/true', + :path => '/usr/bin', + :refreshonly => 'true', + :user => 'ceilometer', + :logoutput => 'on_failure' + ) + end + end + + # debian has "python-pysqlite2" + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :params do + { :database_connection => 'sqlite:///var/lib/ceilometer.db', + :sync_db => true } + end + + it { should contain_class('ceilometer::params') } + + it 'installs python-mongodb package' do + should contain_package('ceilometer-backend-package').with( + :ensure => 'present', + :name => 'python-pysqlite2') + end + + it 'runs ceilometer-dbsync' do + should contain_exec('ceilometer-dbsync').with( + :command => 'ceilometer-dbsync --config-file=/etc/ceilometer/ceilometer.conf', + :path => '/usr/bin', + :refreshonly => 'true', + :user => 'ceilometer', + :logoutput => 'on_failure' + ) + end + end + +end + diff --git a/ceilometer/spec/classes/ceilometer_expirer_spec.rb b/ceilometer/spec/classes/ceilometer_expirer_spec.rb new file mode 100644 index 000000000..08d62fd38 --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_expirer_spec.rb @@ -0,0 +1,87 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# Unit tests for ceilometer::expirer +# + +require 'spec_helper' + +describe 'ceilometer::expirer' do + + let :pre_condition do + "class { 'ceilometer': metering_secret => 's3cr3t' }" + end + + let :params do + { :time_to_live => '-1' } + end + + shared_examples_for 'ceilometer-expirer' do + + it { should contain_class('ceilometer::params') } + + it 'installs ceilometer common package' do + should contain_package('ceilometer-common').with( + :ensure => 'present', + :name => platform_params[:common_package_name] + ) + end + + it 'configures a cron' do + should contain_cron('ceilometer-expirer').with( + :command => 'ceilometer-expirer', + :environment => 'PATH=/bin:/usr/bin:/usr/sbin', + :user => 'ceilometer', + :minute => 1, + :hour => 0, + :monthday => '*', + :month => '*', + :weekday => '*' + ) + end + + it 'configures database section in ceilometer.conf' do + should contain_ceilometer_config('database/time_to_live').with_value( params[:time_to_live] ) + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :common_package_name => 'ceilometer-common' } + end + + it_configures 'ceilometer-expirer' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :common_package_name => 'openstack-ceilometer-common' } + end + + it_configures 'ceilometer-expirer' + end + +end diff --git a/ceilometer/spec/classes/ceilometer_init_spec.rb b/ceilometer/spec/classes/ceilometer_init_spec.rb new file mode 100644 index 000000000..977a5a16f --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_init_spec.rb @@ -0,0 +1,253 @@ +require 'spec_helper' + +describe 'ceilometer' do + + let :params do + { + :metering_secret => 'metering-s3cr3t', + :package_ensure => 'present', + :debug => 'False', + :log_dir => '/var/log/ceilometer', + :verbose => 'False', + } + end + + let :rabbit_params do + { + :rabbit_host => '127.0.0.1', + :rabbit_port => 5672, + :rabbit_userid => 'guest', + :rabbit_password => '', + :rabbit_virtual_host => '/', + } + end + + let :qpid_params do + { + :rpc_backend => "ceilometer.openstack.common.rpc.impl_qpid", + :qpid_hostname => 'localhost', + :qpid_port => 5672, + :qpid_username => 'guest', + :qpid_password => 'guest', + } + end + + shared_examples_for 'ceilometer' do + + context 'with rabbit_host parameter' do + before { params.merge!( rabbit_params ) } + it_configures 'a ceilometer base installation' + it_configures 'rabbit without HA support (with backward compatibility)' + end + + context 'with rabbit_hosts parameter' do + context 'with one server' do + before { params.merge!( rabbit_params ).merge!( :rabbit_hosts => ['127.0.0.1:5672'] ) } + it_configures 'a ceilometer base installation' + it_configures 'rabbit without HA support (without backward compatibility)' + end + + context 'with multiple servers' do + before { params.merge!( rabbit_params ).merge!( :rabbit_hosts => ['rabbit1:5672', 'rabbit2:5672'] ) } + it_configures 'a ceilometer base installation' + it_configures 'rabbit with HA support' + end + end + + context 'with qpid' do + before {params.merge!( qpid_params ) } + it_configures 'a ceilometer base installation' + it_configures 'qpid support' + end + + end + + shared_examples_for 'a ceilometer base installation' do + + it { should contain_class('ceilometer::params') } + + it 'configures ceilometer group' do + should contain_group('ceilometer').with( + :name => 'ceilometer', + :require => 'Package[ceilometer-common]' + ) + end + + it 'configures ceilometer user' do + should contain_user('ceilometer').with( + :name => 'ceilometer', + :gid => 'ceilometer', + :system => true, + :require => 'Package[ceilometer-common]' + ) + end + + it 'configures ceilometer configuration folder' do + should contain_file('/etc/ceilometer/').with( + :ensure => 'directory', + :owner => 'ceilometer', + :group => 'ceilometer', + :mode => '0750', + :require => 'Package[ceilometer-common]' + ) + end + + it 'configures ceilometer configuration file' do + should contain_file('/etc/ceilometer/ceilometer.conf').with( + :owner => 'ceilometer', + :group => 'ceilometer', + :mode => '0640', + :require => 'Package[ceilometer-common]' + ) + end + + it 'installs ceilometer common package' do + should contain_package('ceilometer-common').with( + :ensure => 'present', + :name => platform_params[:common_package_name] + ) + end + + it 'configures required metering_secret' do + should contain_ceilometer_config('DEFAULT/metering_secret').with_value('metering-s3cr3t') + end + + context 'without the required metering_secret' do + before { params.delete(:metering_secret) } + it { expect { should raise_error(Puppet::Error) } } + end + + it 'configures debug and verbosity' do + should contain_ceilometer_config('DEFAULT/debug').with_value( params[:debug] ) + should contain_ceilometer_config('DEFAULT/verbose').with_value( params[:verbose] ) + end + + it 'configures logging directory by default' do + should contain_ceilometer_config('DEFAULT/log_dir').with_value( params[:log_dir] ) + end + + context 'with logging directory disabled' do + before { params.merge!( :log_dir => false) } + + it { should contain_ceilometer_config('DEFAULT/log_dir').with_ensure('absent') } + end + + it 'configures syslog to be disabled by default' do + should contain_ceilometer_config('DEFAULT/use_syslog').with_value('false') + end + + context 'with syslog enabled' do + before { params.merge!( :use_syslog => 'true' ) } + + it { should contain_ceilometer_config('DEFAULT/use_syslog').with_value('true') } + it { should contain_ceilometer_config('DEFAULT/syslog_log_facility').with_value('LOG_USER') } + end + + context 'with syslog enabled and custom settings' do + before { params.merge!( + :use_syslog => 'true', + :log_facility => 'LOG_LOCAL0' + ) } + + it { should contain_ceilometer_config('DEFAULT/use_syslog').with_value('true') } + it { should contain_ceilometer_config('DEFAULT/syslog_log_facility').with_value('LOG_LOCAL0') } + end + + it 'configures notification_topics' do + should contain_ceilometer_config('DEFAULT/notification_topics').with_value('notifications') + end + end + + shared_examples_for 'rabbit without HA support (with backward compatibility)' do + + it 'configures rabbit' do + should contain_ceilometer_config('DEFAULT/rabbit_userid').with_value( params[:rabbit_userid] ) + should contain_ceilometer_config('DEFAULT/rabbit_password').with_value( params[:rabbit_password] ) + should contain_ceilometer_config('DEFAULT/rabbit_virtual_host').with_value( params[:rabbit_virtual_host] ) + end + + it { should contain_ceilometer_config('DEFAULT/rabbit_host').with_value( params[:rabbit_host] ) } + it { should contain_ceilometer_config('DEFAULT/rabbit_port').with_value( params[:rabbit_port] ) } + it { should contain_ceilometer_config('DEFAULT/rabbit_hosts').with_value( "#{params[:rabbit_host]}:#{params[:rabbit_port]}" ) } + it { should contain_ceilometer_config('DEFAULT/rabbit_ha_queues').with_value('false') } + end + + shared_examples_for 'rabbit without HA support (without backward compatibility)' do + + it 'configures rabbit' do + should contain_ceilometer_config('DEFAULT/rabbit_userid').with_value( params[:rabbit_userid] ) + should contain_ceilometer_config('DEFAULT/rabbit_password').with_value( params[:rabbit_password] ) + should contain_ceilometer_config('DEFAULT/rabbit_virtual_host').with_value( params[:rabbit_virtual_host] ) + end + + it { should contain_ceilometer_config('DEFAULT/rabbit_host').with_ensure('absent') } + it { should contain_ceilometer_config('DEFAULT/rabbit_port').with_ensure('absent') } + it { should contain_ceilometer_config('DEFAULT/rabbit_hosts').with_value( params[:rabbit_hosts].join(',') ) } + it { should contain_ceilometer_config('DEFAULT/rabbit_ha_queues').with_value('false') } + end + + shared_examples_for 'rabbit with HA support' do + + it 'configures rabbit' do + should contain_ceilometer_config('DEFAULT/rabbit_userid').with_value( params[:rabbit_userid] ) + should contain_ceilometer_config('DEFAULT/rabbit_password').with_value( params[:rabbit_password] ) + should contain_ceilometer_config('DEFAULT/rabbit_virtual_host').with_value( params[:rabbit_virtual_host] ) + end + + it { should contain_ceilometer_config('DEFAULT/rabbit_host').with_ensure('absent') } + it { should contain_ceilometer_config('DEFAULT/rabbit_port').with_ensure('absent') } + it { should contain_ceilometer_config('DEFAULT/rabbit_hosts').with_value( params[:rabbit_hosts].join(',') ) } + it { should contain_ceilometer_config('DEFAULT/rabbit_ha_queues').with_value('true') } + end + + shared_examples_for 'qpid support' do + context("with default parameters") do + it { should contain_ceilometer_config('DEFAULT/qpid_reconnect').with_value(true) } + it { should contain_ceilometer_config('DEFAULT/qpid_reconnect_timeout').with_value('0') } + it { should contain_ceilometer_config('DEFAULT/qpid_reconnect_limit').with_value('0') } + it { should contain_ceilometer_config('DEFAULT/qpid_reconnect_interval_min').with_value('0') } + it { should contain_ceilometer_config('DEFAULT/qpid_reconnect_interval_max').with_value('0') } + it { should contain_ceilometer_config('DEFAULT/qpid_reconnect_interval').with_value('0') } + it { should contain_ceilometer_config('DEFAULT/qpid_heartbeat').with_value('60') } + it { should contain_ceilometer_config('DEFAULT/qpid_protocol').with_value('tcp') } + it { should contain_ceilometer_config('DEFAULT/qpid_tcp_nodelay').with_value(true) } + end + + context("with mandatory parameters set") do + it { should contain_ceilometer_config('DEFAULT/rpc_backend').with_value('ceilometer.openstack.common.rpc.impl_qpid') } + it { should contain_ceilometer_config('DEFAULT/qpid_hostname').with_value( params[:qpid_hostname] ) } + it { should contain_ceilometer_config('DEFAULT/qpid_port').with_value( params[:qpid_port] ) } + it { should contain_ceilometer_config('DEFAULT/qpid_username').with_value( params[:qpid_username]) } + it { should contain_ceilometer_config('DEFAULT/qpid_password').with_value(params[:qpid_password]) } + end + + context("failing if the rpc_backend is not present") do + before { params.delete( :rpc_backend) } + it { expect { should raise_error(Puppet::Error) } } + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :common_package_name => 'ceilometer-common' } + end + + it_configures 'ceilometer' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :common_package_name => 'openstack-ceilometer-common' } + end + + it_configures 'ceilometer' + end +end diff --git a/ceilometer/spec/classes/ceilometer_keystone_auth_spec.rb b/ceilometer/spec/classes/ceilometer_keystone_auth_spec.rb new file mode 100644 index 000000000..15287fc26 --- /dev/null +++ b/ceilometer/spec/classes/ceilometer_keystone_auth_spec.rb @@ -0,0 +1,166 @@ +require 'spec_helper' + +describe 'ceilometer::keystone::auth' do + + let :default_params do + { + :email => 'ceilometer@localhost', + :auth_name => 'ceilometer', + :configure_endpoint => true, + :service_type => 'metering', + :public_address => '127.0.0.1', + :admin_address => '127.0.0.1', + :internal_address => '127.0.0.1', + :port => '8777', + :region => 'RegionOne', + :tenant => 'services', + :public_protocol => 'http', + :admin_protocol => 'http', + :internal_protocol => 'http' + } + end + + shared_examples_for 'ceilometer keystone auth' do + + context 'without the required password parameter' do + it { expect { should raise_error(Puppet::Error) } } + end + + let :params do + { :password => 'ceil0met3r-passZord' } + end + + context 'with the required parameters' do + it 'configures ceilometer user' do + should contain_keystone_user( default_params[:auth_name] ).with( + :ensure => 'present', + :password => params[:password], + :email => default_params[:email], + :tenant => default_params[:tenant] + ) + end + + it 'configures ceilometer user roles' do + should contain_keystone_user_role("#{default_params[:auth_name]}@#{default_params[:tenant]}").with( + :ensure => 'present', + :roles => ['admin','ResellerAdmin'] + ) + end + + it 'configures ceilometer service' do + should contain_keystone_service( default_params[:auth_name] ).with( + :ensure => 'present', + :type => default_params[:service_type], + :description => 'Openstack Metering Service' + ) + end + + it 'configure ceilometer endpoints' do + should contain_keystone_endpoint("#{default_params[:region]}/#{default_params[:auth_name]}").with( + :ensure => 'present', + :public_url => "#{default_params[:public_protocol]}://#{default_params[:public_address]}:#{default_params[:port]}", + :admin_url => "#{default_params[:admin_protocol]}://#{default_params[:admin_address]}:#{default_params[:port]}", + :internal_url => "#{default_params[:internal_protocol]}://#{default_params[:internal_address]}:#{default_params[:port]}" + ) + end + end + + context 'with overriden parameters' do + before do + params.merge!({ + :email => 'mighty-ceilometer@remotehost', + :auth_name => 'mighty-ceilometer', + :service_type => 'cloud-measuring', + :public_address => '10.0.0.1', + :admin_address => '10.0.0.2', + :internal_address => '10.0.0.3', + :port => '65001', + :region => 'RegionFortyTwo', + :tenant => 'mighty-services', + :public_protocol => 'https', + :admin_protocol => 'ftp', + :internal_protocol => 'gopher' + }) + end + + it 'configures ceilometer user' do + should contain_keystone_user( params[:auth_name] ).with( + :ensure => 'present', + :password => params[:password], + :email => params[:email], + :tenant => params[:tenant] + ) + end + + it 'configures ceilometer user roles' do + should contain_keystone_user_role("#{params[:auth_name]}@#{params[:tenant]}").with( + :ensure => 'present', + :roles => ['admin','ResellerAdmin'] + ) + end + + it 'configures ceilometer service' do + should contain_keystone_service( params[:auth_name] ).with( + :ensure => 'present', + :type => params[:service_type], + :description => 'Openstack Metering Service' + ) + end + + it 'configure ceilometer endpoints' do + should contain_keystone_endpoint("#{params[:region]}/#{params[:auth_name]}").with( + :ensure => 'present', + :public_url => "#{params[:public_protocol]}://#{params[:public_address]}:#{params[:port]}", + :admin_url => "#{params[:admin_protocol]}://#{params[:admin_address]}:#{params[:port]}", + :internal_url => "#{params[:internal_protocol]}://#{params[:internal_address]}:#{params[:port]}" + ) + end + + context 'with overriden full uri' do + before do + params.merge!({ + :public_url => 'https://public.host:443/ceilometer_pub', + :admin_url => 'https://admin.host/ceilometer_adm', + :internal_url => 'http://internal.host:80/ceilometer_int', + }) + end + it 'configure ceilometer endpoints' do + should contain_keystone_endpoint("#{params[:region]}/#{params[:auth_name]}").with( + :ensure => 'present', + :public_url => params[:public_url], + :admin_url => params[:admin_url], + :internal_url => params[:internal_url] + ) + end + end + + context 'with configure_endpoint = false' do + before do + params.delete!(:configure_endpoint) + it 'does not configure ceilometer endpoints' do + should_not contain_keystone_endpoint("#{params[:region]}/#{params[:auth_name]}") + end + end + end + end + + + end + + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'ceilometer keystone auth' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'ceilometer keystone auth' + end +end diff --git a/ceilometer/spec/spec_helper.rb b/ceilometer/spec/spec_helper.rb new file mode 100644 index 000000000..e17ce17d6 --- /dev/null +++ b/ceilometer/spec/spec_helper.rb @@ -0,0 +1,5 @@ +require 'puppetlabs_spec_helper/module_spec_helper' + +RSpec.configure do |c| + c.alias_it_should_behave_like_to :it_configures, 'configures' +end diff --git a/certmonger/LICENSE b/certmonger/LICENSE new file mode 100644 index 000000000..3fa0da30c --- /dev/null +++ b/certmonger/LICENSE @@ -0,0 +1,13 @@ +Copyright 2013 Red Hat, Inc. + +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. diff --git a/certmonger/Modulefile b/certmonger/Modulefile new file mode 100644 index 000000000..2bc1a0099 --- /dev/null +++ b/certmonger/Modulefile @@ -0,0 +1,10 @@ +name 'rcritten/certmonger' +version '1.0.2' +source 'git://github.com/rcritten/puppet-certmonger.git' +author 'Rob Crittenden ' +license 'Apache' +summary 'Certmonger Puppet Module' +description 'This module tracks certificates using certmonger.' +project_page 'https://github.com/rcritten/puppet-certmonger' + +dependency 'puppetlabs/stdlib', '>= 0.0.1' diff --git a/certmonger/README.md b/certmonger/README.md new file mode 100644 index 000000000..edcb46d38 --- /dev/null +++ b/certmonger/README.md @@ -0,0 +1,34 @@ +# certmonger puppet module + +very simple puppet module to request IPA certs via certmonger. + +This requires that the machine already be enrolled in an IPA server + +When using an NSS database this has a side-effect of creating a file +in the enrolled subdirectory of the NSS database named after the principal. +This is an indicator that the certificate has already been requested. + +Be aware of SELinux too. You can't just put NSS databases in any directory +you want. The certmonger status will probably be in NEED_KEY_PAIR in the +case of a AVC. + +certmonger uses the host principal on the machine to communicate with IPA. +By default, host principals do not have permission to add new services. +This means you'll probably need to pre-create the services, otherwise +you'll get a status like this: + +Request ID '20130823131914': + status: CA_REJECTED + ca-error: Server denied our request, giving up: 2100 (RPC failed at server. Insufficient access: You need to be a member of the serviceadmin role to add services). + +When generating an OpenSSL certificate we need to let certmonger create the +key and cert files. If puppet does it first then certmonger will think that +the user provided a key to use (a blank file) and things fail. The current +workaround is to call getcert -f /path/to/cert to make sure the key exists. + +TESTING + +Between OpenSSL invocations you'll want to clean up. This is what I do: + +getcert stop-tracking -i `getcert list -f /tmp/test.crt |grep ID |cut -d\' -f2` +rm -f /tmp/test.key /tmp/test.crt diff --git a/certmonger/lib/facter/ipa_client_configured.rb b/certmonger/lib/facter/ipa_client_configured.rb new file mode 100644 index 000000000..cc50290c2 --- /dev/null +++ b/certmonger/lib/facter/ipa_client_configured.rb @@ -0,0 +1,9 @@ +Facter.add("ipa_client_configured") do + setcode do + if File.exist? "/etc/ipa/default.conf" + "true" + else + "false" + end + end +end diff --git a/certmonger/manifests/request_ipa_cert.pp b/certmonger/manifests/request_ipa_cert.pp new file mode 100644 index 000000000..d3747ce07 --- /dev/null +++ b/certmonger/manifests/request_ipa_cert.pp @@ -0,0 +1,164 @@ +# Request a new certificate from IPA using certmonger +# +# Parameters: +# $dbname - required for nss, unused for openssl. The directory +# to store the db +# $seclib, - required - select nss or openssl +# $principal - required - the IPA principal to associate the +# certificate with +# $nickname - required for nss, unused for openssl. The NSS +# certificate nickname +# $cert - required for openssl, unused for nss. The full file +# path to store the certificate in. +# $key - required for openssl, unused for nss. The full file +# path to store the key in +# $basedir - The base directory for $dbname, defaults +# to '/etc/pki'. Not used with openssl. +# $owner_id - owner of OpenSSL cert and key files +# $group_id - group of OpenSSL cert and key files +# $hostname - hostname in the subject of the certificate. +# defaults to current fqdn. +# +# Actions: +# Submits a certificate request to an IPA server for a new certificate. +# +# Requires: +# The NSS db must already exist. It can be created using the nssdb +# module. +# +# Sample Usage: +# +# NSS: +# certmonger::request_ipa_cert {'test': +# seclib => "nss", +# nickname => "broker", +# principal => "qpid/${fqdn}"} +# +# OpenSSL: +# certmonger::request_ipa_cert {'test': +# seclib => "openssl", +# principal => "qpid/${fqdn}", +# key => "/etc/pki/test2/test2.key", +# cert => "/etc/pki/test2/test2.crt", +# owner_id => 'qpidd', +# group_id => 'qpidd'} + +define certmonger::request_ipa_cert ( + $dbname = $title, + $seclib, + $principal, + $nickname = undef, + $cert = undef, + $key = undef, + $basedir = '/etc/pki', + $owner_id = undef, + $group_id = undef, + $hostname = undef +) { + include certmonger::server + + if "$ipa_client_configured" == 'true' { + + $principal_no_slash = regsubst($principal, '\/', '_') + + if $hostname == undef { + $subject = '' + } else { + $subject = "-N cn=${hostname}" + } + + if $seclib == 'nss' { + $options = "-d ${basedir}/${dbname} -n ${nickname} -p ${basedir}/${dbname}/password.conf" + + file {"${basedir}/${dbname}/requested": + ensure => directory, + mode => 0600, + owner => 0, + group => 0, + } + + # Semaphore file to determine if we've already requested a certificate. + file {"${basedir}/${dbname}/requested/${principal_no_slash}": + ensure => file, + mode => 0600, + owner => $owner_id, + group => $group_id, + require => [ + Exec["get_cert_nss_${title}"] + ], + } + exec {"get_cert_nss_${title}": + command => "/usr/bin/ipa-getcert request ${options} -K ${principal} ${subject}", + creates => "${basedir}/${dbname}/requested/${principal_no_slash}", + require => [ + Package['certmonger'], + File["${basedir}/${dbname}/password.conf"], + ], + } + } + elsif $seclib == 'openssl' { + + $options = "-k ${key} -f ${cert}" + + # NOTE: Order is extremely important here. If the key file exists + # (content doesn't matter) then certmonger will attempt to use that + # as the key. You could end up in a NEWLY_ADDED_NEED_KEYINFO_READ_PIN + # state if the key file doesn't actually contain a key. + + file {"${cert}": + ensure => file, + mode => 0444, + owner => $owner_id, + group => $group_id, + } + file {"${key}": + ensure => file, + mode => 0440, + owner => $owner_id, + group => $group_id, + } + exec {"get_cert_openssl_${title}": + command => "/usr/bin/ipa-getcert request ${options} -K ${principal} ${subject}", + creates => [ + "${key}", + "${cert}", + ], + require => [ + Package['certmonger'], + ], + before => [ + File["${key}"], + File["${cert}"], + ], + notify => Exec["wait_for_certmonger_${title}"], + } + + # We need certmonger to finish creating the key before we + # can proceed. Use onlyif as a way to execute multiple + # commands without restorting to shipping a shell script. + # This will call getcert to check the status of our cert + # 5 times. This doesn't short circuit though, so all 5 will + # always run, causing a 5-second delay. + exec {"wait_for_certmonger_${title}": + command => "true", + onlyif => [ + "sleep 1 && getcert list -f ${cert}", + "sleep 1 && getcert list -f ${cert}", + "sleep 1 && getcert list -f ${cert}", + "sleep 1 && getcert list -f ${cert}", + "sleep 1 && getcert list -f ${cert}", + ], + path => "/usr/bin:/bin", + before => [ + File["${key}"], + File["${cert}"], + ], + refreshonly => true, + } + } else { + fail("Unrecognized security library: ${seclib}") + } + } else { + fail("ipa not configured") + } +} diff --git a/certmonger/manifests/server.pp b/certmonger/manifests/server.pp new file mode 100644 index 000000000..0755f1b5e --- /dev/null +++ b/certmonger/manifests/server.pp @@ -0,0 +1,11 @@ +class certmonger::server { + + package { 'certmonger': ensure => present } + + service { 'certmonger': + name => 'certmonger', + ensure => running, + enable => true, + require => Package['certmonger'], + } +} diff --git a/certmonger/tests/nss_ipa.pp b/certmonger/tests/nss_ipa.pp new file mode 100644 index 000000000..1b8de5f93 --- /dev/null +++ b/certmonger/tests/nss_ipa.pp @@ -0,0 +1,6 @@ +certmonger::request_ipa_cert {'test': + seclib => "nss", + nickname => "Server-Cert", + principal => "nss_test/${fqdn}", + basedir => '/tmp/nssdb', +} diff --git a/certmonger/tests/openssl_ipa.pp b/certmonger/tests/openssl_ipa.pp new file mode 100644 index 000000000..80e843d21 --- /dev/null +++ b/certmonger/tests/openssl_ipa.pp @@ -0,0 +1,8 @@ +certmonger::request_ipa_cert {'test': + seclib => "openssl", + principal => "openssl_test/${fqdn}", + key => "/tmp/test.key", + cert => "/tmp/test.crt", + owner_id => 'rcrit', + group_id => 'rcrit' +} diff --git a/cinder/.fixtures.yml b/cinder/.fixtures.yml new file mode 100644 index 000000000..f24a907e6 --- /dev/null +++ b/cinder/.fixtures.yml @@ -0,0 +1,21 @@ +fixtures: + repositories: + 'apt': 'git://github.com/puppetlabs/puppetlabs-apt.git' + 'inifile': 'git://github.com/puppetlabs/puppetlabs-inifile' + 'keystone': + repo: 'git://github.com/stackforge/puppet-keystone.git' + ref: 'origin/stable/havana' + 'mysql': + repo: 'git://github.com/puppetlabs/puppetlabs-mysql.git' + ref: 'origin/0.x' + 'postgresql': + repo: 'git://github.com/puppetlabs/puppet-postgresql.git' + ref: '2.5.0' + 'qpid': 'git://github.com/dprince/puppet-qpid.git' + 'rabbitmq': + repo: 'git://github.com/puppetlabs/puppetlabs-rabbitmq' + ref: 'origin/2.x' + 'stdlib': 'git://github.com/puppetlabs/puppetlabs-stdlib.git' + 'sysctl': 'git://github.com/duritong/puppet-sysctl.git' + symlinks: + 'cinder': "#{source_dir}" diff --git a/cinder/.gitignore b/cinder/.gitignore new file mode 100644 index 000000000..1fc755c8f --- /dev/null +++ b/cinder/.gitignore @@ -0,0 +1,5 @@ +Gemfile.lock +spec/fixtures/modules/* +spec/fixtures/manifests/site.pp +*.swp +pkg diff --git a/cinder/.gitreview b/cinder/.gitreview new file mode 100644 index 000000000..9963e93dc --- /dev/null +++ b/cinder/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=review.openstack.org +port=29418 +project=stackforge/puppet-cinder.git +defaultbranch=stable/havana diff --git a/cinder/Gemfile b/cinder/Gemfile new file mode 100644 index 000000000..d965fa900 --- /dev/null +++ b/cinder/Gemfile @@ -0,0 +1,18 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'puppetlabs_spec_helper', :require => false + gem 'puppet-lint', '~> 0.3.2' + gem 'rake', '10.1.1' + gem 'rspec', '< 2.99' + gem 'json' + gem 'webmock' +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/cinder/LICENSE b/cinder/LICENSE new file mode 100644 index 000000000..8d968b6cb --- /dev/null +++ b/cinder/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. + 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. diff --git a/cinder/Modulefile b/cinder/Modulefile new file mode 100644 index 000000000..79f83cfb2 --- /dev/null +++ b/cinder/Modulefile @@ -0,0 +1,15 @@ +name 'puppetlabs-cinder' +version '3.1.1' +source 'https://github.com/stackforge/puppet-cinder' +author 'Puppet Labs' +license 'Apache License 2.0' +summary 'Puppet Labs Cinder Module' +description 'Puppet module to install and configure the Openstack Cinder block storage service' +project_page 'https://launchpad.net/puppet-openstack' + +dependency 'puppetlabs/keystone', '>=3.0.0 <4.0.0' +dependency 'puppetlabs/inifile', '>=1.0.0 <2.0.0' +dependency 'puppetlabs/mysql', '>=0.9.0 <1.0.0' +dependency 'puppetlabs/stdlib', '>=3.2.0' +dependency 'puppetlabs/rabbitmq', '>=2.0.2 <3.0.0' +dependency 'dprince/qpid', '>=1.0.0 <2.0.0' diff --git a/cinder/README.md b/cinder/README.md new file mode 100644 index 000000000..4c1a6a3a2 --- /dev/null +++ b/cinder/README.md @@ -0,0 +1,235 @@ +cinder +======= + +#### Table of Contents + +1. [Overview - What is the cinder module?](#overview) +2. [Module Description - What does the module do?](#module-description) +3. [Setup - The basics of getting started with cinder](#setup) +4. [Implementation - An under-the-hood peek at what the module is doing](#implementation) +5. [Limitations - OS compatibility, etc.](#limitations) +6. [Development - Guide for contributing to the module](#development) +7. [Contributors - Those with commits](#contributors) +8. [Release Notes - Notes on the most recent updates to the module](#release-notes) + +Overview +-------- + +The cinder module is a part of [Stackforge](https://github.com/stackfoge), an effort by the Openstack infrastructure team to provide continuous integration testing and code review for Openstack and Openstack community projects not part of the core software. The module its self is used to flexibly configure and manage the block storage service for Openstack. + +Module Description +------------------ + +The cinder module is a thorough attempt to make Puppet capable of managing the entirety of cinder. This includes manifests to provision such things as keystone endpoints, RPC configurations specific to cinder, and database connections. Types are shipped as part of the cinder module to assist in manipulation of configuration files. + +This module is tested in combination with other modules needed to build and leverage an entire Openstack software stack. These modules can be found, all pulled together in the [openstack module](https://github.com/stackfoge/puppet-openstack). + +Setup +----- + +**What the cinder module affects** + +* cinder, the block storage service for Openstack. + +### Installing cinder + + puppet module install puppetlabs/cinder + +### Beginning with cinder + +To utilize the cinder module's functionality you will need to declare multiple resources. The following is a modified excerpt from the [openstack module](https://github.com/stackfoge/puppet-openstack). This is not an exhaustive list of all the components needed, we recommend you consult and understand the [openstack module](https://github.com/stackforge/puppet-openstack) and the [core openstack](http://docs.openstack.org) documentation. + +**Define a cinder control node** + +```puppet +class { 'cinder': + sql_connection => 'mysql://cinder:secret_block_password@openstack-controller.example.com/cinder', + rabbit_password => 'secret_rpc_password_for_blocks', + rabbit_host => 'openstack-controller.example.com', + verbose => true, +} + +class { 'cinder::api': + keystone_password => $keystone_password, + keystone_enabled => $keystone_enabled, + keystone_user => $keystone_user, + keystone_auth_host => $keystone_auth_host, + keystone_auth_port => $keystone_auth_port, + keystone_auth_protocol => $keystone_auth_protocol, + service_port => $keystone_service_port, + package_ensure => $cinder_api_package_ensure, + bind_host => $cinder_bind_host, + enabled => $cinder_api_enabled, +} + +class { 'cinder::scheduler': + scheduler_driver => 'cinder.scheduler.simple.SimpleScheduler', +} +``` + +**Define a cinder storage node** + +```puppet +class { 'cinder': + sql_connection => 'mysql://cinder:secret_block_password@openstack-controller.example.com/cinder', + rabbit_password => 'secret_rpc_password_for_blocks', + rabbit_host => 'openstack-controller.example.com', + verbose => true, +} + +class { 'cinder::volume': } + +class { 'cinder::volume::iscsi': + iscsi_ip_address => '10.0.0.2', +} +``` + +**Define a cinder storage node with multiple backends ** + +```puppet +class { 'cinder': + sql_connection => 'mysql://cinder:secret_block_password@openstack-controller.example.com/cinder', + rabbit_password => 'secret_rpc_password_for_blocks', + rabbit_host => 'openstack-controller.example.com', + verbose => true, +} + +class { 'cinder::volume': } + +cinder::backend::iscsi {'iscsi1': + iscsi_ip_address => '10.0.0.2', +} + +cinder::backend::iscsi {'iscsi2': + iscsi_ip_address => '10.0.0.3', +} + +cinder::backend::iscsi {'iscsi3': + iscsi_ip_address => '10.0.0.4', + volume_backend_name => 'iscsi', +} + +cinder::backend::iscsi {'iscsi4': + iscsi_ip_address => '10.0.0.5', + volume_backend_name => 'iscsi', +} + +cinder::backend::rbd {'rbd-images': + rbd_pool => 'images', + rbd_user => 'images', +} + +# Cinder::Type requires keystone credentials +Cinder::Type { + os_password => 'admin', + os_tenant_name => 'admin', + os_username => 'admin', + os_auth_url => 'http://127.0.0.1:5000/v2.0/', +} + +cinder::type {'iscsi': + set_key => 'volume_backend_name', + set_value => ['iscsi1', 'iscsi2', 'iscsi'] +} + +cinder::type {'rbd': + set_key => 'volume_backend_name', + set_value => 'rbd-images', +} + +class { 'cinder::backends': + enabled_backends => ['iscsi1', 'iscsi2', 'rbd-images'] +} +``` + +Note: that the name passed to any backend resource must be unique accross all backends otherwise a duplicate resource will be defined. + +** Using type and type_set ** + +Cinder allows for the usage of type to set extended information that can be used for various reasons. We have resource provider for ``type`` and ``type_set`` Since types are rarely defined with out also setting attributes with it, the resource for ``type`` can also call ``type_set`` if you pass ``set_key`` and ``set_value`` + + +Implementation +-------------- + +### cinder + +cinder is a combination of Puppet manifest and ruby code to delivery configuration and extra functionality through types and providers. + +Limitations +------------ + +* Setup of storage nodes is limited to Linux and LVM, i.e. Puppet won't configure a Nexenta appliacne but nova can be configured to use the Nexenta driver with Class['cinder::volume::nexenta']. + +Development +----------- + +Developer documentation for the entire puppet-openstack project. + +* https://wiki.openstack.org/wiki/Puppet-openstack#Developer_documentation + +Contributors +------------ + +* https://github.com/stackforge/puppet-cinder/graphs/contributors + +Release Notes +------------- + +**3.1.1** + +* Fixed resource duplication bug. + +**3.1.0** + +* Added default_volume_type as a Cinder API parameter. +* Added parameter for endpoint procols. +* Deprecated glance_api_version. +* Added support for VMDK. +* Added support for Cinder multi backend. +* Added support for https authentication endpoints. +* Replaced pip with native package manager (VMDK). + +**3.0.0** + +* Major release for OpenStack Havana. +* Added support for Solid Fire. +* Added support for ceilometer. +* Added Ceph backend support. +* Added Glance support. +* Improved Cinder backup support. +* Fixed bug for cinder-volume requirement. +* Fixed default Swift URL for backup service. + +**2.2.0** + +* Added support for rate limiting via api-paste.ini +* Added support to configure control_exchange. +* Added parameter check to enable or disable db_sync. +* Added syslog support. +* Added default auth_uri setting for auth token. +* Set package defaults to present. +* Fixed a bug to create empty init script when necessary. +* Various lint fixes. + +**2.1.0** + +* Added configuration of Cinder quotas. +* Added support for NetApp direct driver backend. +* Added support for ceph backend. +* Added support for SQL idle timeout. +* Added support for RabbitMQ clustering with single IP. +* Fixed allowed_hosts/database connection bug. +* Fixed lvm2 setup failure for Ubuntu. +* Removed unnecessary mysql::server dependency. +* Pinned RabbitMQ and database module versions. +* Various lint and bug fixes. + +**2.0.0** + +* Upstream is now part of stackfoge. +* Nexenta, NFS, and SAN support added as cinder volume drivers. +* Postgres support added. +* The Apache Qpid and the RabbitMQ message brokers available as RPC backends. +* Configurability of scheduler_driver. +* Various cleanups and bug fixes. diff --git a/cinder/Rakefile b/cinder/Rakefile new file mode 100644 index 000000000..4c2b2ed07 --- /dev/null +++ b/cinder/Rakefile @@ -0,0 +1,6 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' + +PuppetLint.configuration.fail_on_warnings = true +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.send('disable_class_parameter_defaults') diff --git a/cinder/lib/puppet/provider/cinder_api_paste_ini/ini_setting.rb b/cinder/lib/puppet/provider/cinder_api_paste_ini/ini_setting.rb new file mode 100644 index 000000000..26c3b528d --- /dev/null +++ b/cinder/lib/puppet/provider/cinder_api_paste_ini/ini_setting.rb @@ -0,0 +1,27 @@ +Puppet::Type.type(:cinder_api_paste_ini).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def self.file_path + '/etc/cinder/api-paste.ini' + end + + # added for backwards compatibility with older versions of inifile + def file_path + self.class.file_path + end + +end diff --git a/cinder/lib/puppet/provider/cinder_config/ini_setting.rb b/cinder/lib/puppet/provider/cinder_config/ini_setting.rb new file mode 100644 index 000000000..6dcd95597 --- /dev/null +++ b/cinder/lib/puppet/provider/cinder_config/ini_setting.rb @@ -0,0 +1,27 @@ +Puppet::Type.type(:cinder_config).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def self.file_path + '/etc/cinder/cinder.conf' + end + + # added for backwards compatibility with older versions of inifile + def file_path + self.class.file_path + end + +end diff --git a/cinder/lib/puppet/type/cinder_api_paste_ini.rb b/cinder/lib/puppet/type/cinder_api_paste_ini.rb new file mode 100644 index 000000000..d895b4a3c --- /dev/null +++ b/cinder/lib/puppet/type/cinder_api_paste_ini.rb @@ -0,0 +1,42 @@ +Puppet::Type.newtype(:cinder_api_paste_ini) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from /etc/cinder/api-paste.ini' + newvalues(/\S+\/\S+/) + end + + newproperty(:value) do + desc 'The value of the setting to be defined.' + munge do |value| + value = value.to_s.strip + value.capitalize! if value =~ /^(true|false)$/i + value + end + + def is_to_s( currentvalue ) + if resource.secret? + return '[old secret redacted]' + else + return currentvalue + end + end + + def should_to_s( newvalue ) + if resource.secret? + return '[new secret redacted]' + else + return newvalue + end + end + end + + newparam(:secret, :boolean => true) do + desc 'Whether to hide the value from Puppet logs. Defaults to `false`.' + + newvalues(:true, :false) + + defaultto false + end +end diff --git a/cinder/lib/puppet/type/cinder_config.rb b/cinder/lib/puppet/type/cinder_config.rb new file mode 100644 index 000000000..62d38256b --- /dev/null +++ b/cinder/lib/puppet/type/cinder_config.rb @@ -0,0 +1,42 @@ +Puppet::Type.newtype(:cinder_config) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from /etc/cinder/cinder.conf' + newvalues(/\S+\/\S+/) + end + + newproperty(:value) do + desc 'The value of the setting to be defined.' + munge do |value| + value = value.to_s.strip + value.capitalize! if value =~ /^(true|false)$/i + value + end + + def is_to_s( currentvalue ) + if resource.secret? + return '[old secret redacted]' + else + return currentvalue + end + end + + def should_to_s( newvalue ) + if resource.secret? + return '[new secret redacted]' + else + return newvalue + end + end + end + + newparam(:secret, :boolean => true) do + desc 'Whether to hide the value from Puppet logs. Defaults to `false`.' + + newvalues(:true, :false) + + defaultto false + end +end diff --git a/cinder/manifests/api.pp b/cinder/manifests/api.pp new file mode 100644 index 000000000..35356b172 --- /dev/null +++ b/cinder/manifests/api.pp @@ -0,0 +1,182 @@ +# == Class: cinder::api +# +# Setup and configure the cinder API endpoint +# +# === Parameters +# +# [*keystone_password*] +# The password to use for authentication (keystone) +# +# [*keystone_enabled*] +# (optional) Use keystone for authentification +# Defaults to true +# +# [*keystone_tenant*] +# (optional) The tenant of the auth user +# Defaults to services +# +# [*keystone_user*] +# (optional) The name of the auth user +# Defaults to cinder +# +# [*keystone_auth_host*] +# (optional) The keystone host +# Defaults to localhost +# +# [*keystone_auth_port*] +# (optional) The keystone auth port +# Defaults to 35357 +# +# [*keystone_auth_protocol*] +# (optional) The protocol used to access the auth host +# Defaults to http. +# +# [*keystone_auth_admin_prefix*] +# (optional) The admin_prefix used to admin endpoint of the auth host +# This allow admin auth URIs like http://auth_host:35357/keystone. +# (where '/keystone' is the admin prefix) +# Defaults to false for empty. If defined, should be a string with a +# leading '/' and no trailing '/'. +# +# [*service_port*] +# (optional) The cinder api port +# Defaults to 5000 +# +# [*package_ensure*] +# (optional) The state of the package +# Defaults to present +# +# [*bind_host*] +# (optional) The cinder api bind address +# Defaults to 0.0.0.0 +# +# [*enabled*] +# (optional) The state of the service +# Defaults to true +# +# [*ratelimits*] +# (optional) The state of the service +# Defaults to undef. If undefined the default ratelimiting values are used. +# +# [*ratelimits_factory*] +# (optional) Factory to use for ratelimiting +# Defaults to 'cinder.api.v1.limits:RateLimitingMiddleware.factory' +# +# [*default_volume_type*] +# (optional) Specify the default volume type +# Defaults to undef. +# +class cinder::api ( + $keystone_password, + $keystone_enabled = true, + $keystone_tenant = 'services', + $keystone_user = 'cinder', + $keystone_auth_host = 'localhost', + $keystone_auth_port = '35357', + $keystone_auth_protocol = 'http', + $keystone_auth_admin_prefix = false, + $keystone_auth_uri = false, + $service_port = '5000', + $package_ensure = 'present', + $bind_host = '0.0.0.0', + $enabled = true, + $ratelimits = undef, + $ratelimits_factory = + 'cinder.api.v1.limits:RateLimitingMiddleware.factory', + $default_volume_type = undef, +) { + + include cinder::params + + Cinder_config<||> ~> Service['cinder-api'] + Cinder_api_paste_ini<||> ~> Service['cinder-api'] + + if $::cinder::params::api_package { + Package['cinder-api'] -> Cinder_config<||> + Package['cinder-api'] -> Cinder_api_paste_ini<||> + Package['cinder-api'] -> Service['cinder-api'] + package { 'cinder-api': + ensure => $package_ensure, + name => $::cinder::params::api_package, + } + } + + if $enabled { + + Cinder_config<||> ~> Exec['cinder-manage db_sync'] + + exec { 'cinder-manage db_sync': + command => $::cinder::params::db_sync_command, + path => '/usr/bin', + user => 'cinder', + refreshonly => true, + logoutput => 'on_failure', + require => Package['cinder'], + } + $ensure = 'running' + } else { + $ensure = 'stopped' + } + + service { 'cinder-api': + ensure => $ensure, + name => $::cinder::params::api_service, + enable => $enabled, + hasstatus => true, + require => Package['cinder'], + } + + cinder_config { + 'DEFAULT/osapi_volume_listen': value => $bind_host + } + + if $keystone_auth_uri { + cinder_api_paste_ini { 'filter:authtoken/auth_uri': value => $keystone_auth_uri; } + } else { + cinder_api_paste_ini { 'filter:authtoken/auth_uri': value => "${keystone_auth_protocol}://${keystone_auth_host}:${service_port}/"; } + } + + if $keystone_enabled { + cinder_config { + 'DEFAULT/auth_strategy': value => 'keystone' ; + } + cinder_api_paste_ini { + 'filter:authtoken/service_protocol': value => $keystone_auth_protocol; + 'filter:authtoken/service_host': value => $keystone_auth_host; + 'filter:authtoken/service_port': value => $service_port; + 'filter:authtoken/auth_protocol': value => $keystone_auth_protocol; + 'filter:authtoken/auth_host': value => $keystone_auth_host; + 'filter:authtoken/auth_port': value => $keystone_auth_port; + 'filter:authtoken/admin_tenant_name': value => $keystone_tenant; + 'filter:authtoken/admin_user': value => $keystone_user; + 'filter:authtoken/admin_password': value => $keystone_password, secret => true; + } + + if ($ratelimits != undef) { + cinder_api_paste_ini { + 'filter:ratelimit/paste.filter_factory': value => $ratelimits_factory; + 'filter:ratelimit/limits': value => $ratelimits; + } + } + + if $keystone_auth_admin_prefix { + validate_re($keystone_auth_admin_prefix, '^(/.+[^/])?$') + cinder_api_paste_ini { + 'filter:authtoken/auth_admin_prefix': value => $keystone_auth_admin_prefix; + } + } else { + cinder_api_paste_ini { + 'filter:authtoken/auth_admin_prefix': ensure => absent; + } + } + } + if $default_volume_type { + cinder_config { + 'DEFAULT/default_volume_type': value => $default_volume_type; + } + } else { + cinder_config { + 'DEFAULT/default_volume_type': ensure => absent; + } + } +} diff --git a/cinder/manifests/backend/iscsi.pp b/cinder/manifests/backend/iscsi.pp new file mode 100644 index 000000000..4ed7bd22f --- /dev/null +++ b/cinder/manifests/backend/iscsi.pp @@ -0,0 +1,56 @@ +# +# Define: cinder::backend::iscsi +# Parameters: +# +# [*volume_backend_name*] +# (optional) Allows for the volume_backend_name to be separate of $name. +# Defaults to: $name +# +# +define cinder::backend::iscsi ( + $iscsi_ip_address, + $volume_backend_name = $name, + $volume_group = 'cinder-volumes', + $iscsi_helper = 'tgtadm' +) { + + include cinder::params + + cinder_config { + "${name}/volume_backend_name": value => $volume_backend_name; + "${name}/iscsi_ip_address": value => $iscsi_ip_address; + "${name}/iscsi_helper": value => $iscsi_helper; + "${name}/volume_group": value => $volume_group; + } + + case $iscsi_helper { + 'tgtadm': { + package { 'tgt': + ensure => present, + name => $::cinder::params::tgt_package_name, + } + + if($::osfamily == 'RedHat') { + file_line { 'cinder include': + path => '/etc/tgt/targets.conf', + line => 'include /etc/cinder/volumes/*', + match => '#?include /', + require => Package['tgt'], + notify => Service['tgtd'], + } + } + + service { 'tgtd': + ensure => running, + name => $::cinder::params::tgt_service_name, + enable => true, + require => Class['cinder::volume'], + } + } + + default: { + fail("Unsupported iscsi helper: ${iscsi_helper}.") + } + } + +} diff --git a/cinder/manifests/backend/netapp.pp b/cinder/manifests/backend/netapp.pp new file mode 100644 index 000000000..759e17cd3 --- /dev/null +++ b/cinder/manifests/backend/netapp.pp @@ -0,0 +1,157 @@ +# == define: cinder::backend::netapp +# +# Configures Cinder to use the NetApp unified volume driver +# Compatible for multiple backends +# +# === Parameters +# +# [*netapp_login*] +# (required) Administrative user account name used to access the storage +# system. +# +# [*netapp_password*] +# (required) Password for the administrative user account specified in the +# netapp_login parameter. +# +# [*netapp_server_hostname*] +# (required) The hostname (or IP address) for the storage system. +# +# [*netapp_server_port*] +# (optional) The TCP port to use for communication with ONTAPI on the +# storage system. Traditionally, port 80 is used for HTTP and port 443 is +# used for HTTPS; however, this value should be changed if an alternate +# port has been configured on the storage system. +# Defaults to 80 +# +# [*netapp_size_multiplier*] +# (optional) The quantity to be multiplied by the requested volume size to +# ensure enough space is available on the virtual storage server (Vserver) to +# fulfill the volume creation request. +# Defaults to 1.2 +# +# [*netapp_storage_family*] +# (optional) The storage family type used on the storage system; valid values +# are ontap_7mode for using Data ONTAP operating in 7-Mode or ontap_cluster +# for using clustered Data ONTAP. +# Defaults to ontap_cluster +# +# [*netapp_storage_protocol*] +# (optional) The storage protocol to be used on the data path with the storage +# system; valid values are iscsi or nfs. +# Defaults to nfs +# +# [*netapp_transport_type*] +# (optional) The transport protocol used when communicating with ONTAPI on the +# storage system. Valid values are http or https. +# Defaults to http +# +# [*netapp_vfiler*] +# (optional) The vFiler unit on which provisioning of block storage volumes +# will be done. This parameter is only used by the driver when connecting to +# an instance with a storage family of Data ONTAP operating in 7-Mode and the +# storage protocol selected is iSCSI. Only use this parameter when utilizing +# the MultiStore feature on the NetApp storage system. +# Defaults to '' +# +# [*netapp_volume_list*] +# (optional) This parameter is only utilized when the storage protocol is +# configured to use iSCSI. This parameter is used to restrict provisioning to +# the specified controller volumes. Specify the value of this parameter to be +# a comma separated list of NetApp controller volume names to be used for +# provisioning. +# Defaults to '' +# +# [*netapp_vserver*] +# (optional) This parameter specifies the virtual storage server (Vserver) +# name on the storage cluster on which provisioning of block storage volumes +# should occur. If using the NFS storage protocol, this parameter is mandatory +# for storage service catalog support (utilized by Cinder volume type +# extra_specs support). If this parameter is specified, the exports belonging +# to the Vserver will only be used for provisioning in the future. Block +# storage volumes on exports not belonging to the Vserver specified by +# this parameter will continue to function normally. +# Defaults to '' +# +# [*expiry_thres_minutes*] +# (optional) This parameter specifies the threshold for last access time for +# images in the NFS image cache. When a cache cleaning cycle begins, images +# in the cache that have not been accessed in the last M minutes, where M is +# the value of this parameter, will be deleted from the cache to create free +# space on the NFS share. +# Defaults to 720 +# +# [*thres_avl_size_perc_start*] +# (optional) If the percentage of available space for an NFS share has +# dropped below the value specified by this parameter, the NFS image cache +# will be cleaned. +# Defaults to 20 +# +# [*thres_avl_size_perc_stop*] +# (optional) When the percentage of available space on an NFS share has +# reached the percentage specified by this parameter, the driver will stop +# clearing files from the NFS image cache that have not been accessed in the +# last M minutes, where M is the value of the expiry_thres_minutes parameter. +# Defaults to 60 +# +# [*nfs_shares_config*] +# (optional) File with the list of available NFS shares +# Defaults to '' +# +# === Examples +# +# cinder::backend::netapp { 'myBackend': +# netapp_login => 'clusterAdmin', +# netapp_password => 'password', +# netapp_server_hostname => 'netapp.mycorp.com', +# netapp_server_port => '443', +# netapp_transport_type => 'https', +# netapp_vserver => 'openstack-vserver', +# } +# +# === Authors +# +# Bob Callaway +# +# === Copyright +# +# Copyright 2014 NetApp, Inc. +# +define cinder::backend::netapp ( + $netapp_login, + $netapp_password, + $netapp_server_hostname, + $volume_backend_name = $name, + $netapp_server_port = '80', + $netapp_size_multiplier = '1.2', + $netapp_storage_family = 'ontap_cluster', + $netapp_storage_protocol = 'nfs', + $netapp_transport_type = 'http', + $netapp_vfiler = '', + $netapp_volume_list = '', + $netapp_vserver = '', + $expiry_thres_minutes = '720', + $thres_avl_size_perc_start = '20', + $thres_avl_size_perc_stop = '60', + $nfs_shares_config = '', +) { + + cinder_config { + "${volume_backend_name}/volume_backend_name": value => $volume_backend_name; + "${volume_backend_name}/volume_driver": value => 'cinder.volume.drivers.netapp.common.NetAppDriver'; + "${volume_backend_name}/netapp_login": value => $netapp_login; + "${volume_backend_name}/netapp_password": value => $netapp_password, secret => true; + "${volume_backend_name}/netapp_server_hostname": value => $netapp_server_hostname; + "${volume_backend_name}/netapp_server_port": value => $netapp_server_port; + "${volume_backend_name}/netapp_size_multiplier": value => $netapp_size_multiplier; + "${volume_backend_name}/netapp_storage_family": value => $netapp_storage_family; + "${volume_backend_name}/netapp_storage_protocol": value => $netapp_storage_protocol; + "${volume_backend_name}/netapp_transport_type": value => $netapp_transport_type; + "${volume_backend_name}/netapp_vfiler": value => $netapp_vfiler; + "${volume_backend_name}/netapp_volume_list": value => $netapp_volume_list; + "${volume_backend_name}/netapp_vserver": value => $netapp_vserver; + "${volume_backend_name}/expiry_thres_minutes": value => $expiry_thres_minutes; + "${volume_backend_name}/thres_avl_size_perc_start": value => $thres_avl_size_perc_start; + "${volume_backend_name}/thres_avl_size_perc_stop": value => $thres_avl_size_perc_stop; + "${volume_backend_name}/nfs_shares_config": value => $nfs_shares_config; + } +} diff --git a/cinder/manifests/backend/nfs.pp b/cinder/manifests/backend/nfs.pp new file mode 100644 index 000000000..01cf0f3ea --- /dev/null +++ b/cinder/manifests/backend/nfs.pp @@ -0,0 +1,35 @@ +# ==define cinder::backend::nfs +# +# ===Paramiters +# [*volume_backend_name*] +# (optional) Allows for the volume_backend_name to be separate of $name. +# Defaults to: $name +# +# +define cinder::backend::nfs ( + $volume_backend_name = $name, + $nfs_servers = [], + $nfs_mount_options = undef, + $nfs_disk_util = undef, + $nfs_sparsed_volumes = undef, + $nfs_mount_point_base = undef, + $nfs_shares_config = '/etc/cinder/shares.conf' +) { + + file {$nfs_shares_config: + content => join($nfs_servers, "\n"), + require => Package['cinder'], + notify => Service['cinder-volume'] + } + + cinder_config { + "${name}/volume_backend_name": value => $volume_backend_name; + "${name}/volume_driver": value => + 'cinder.volume.drivers.nfs.NfsDriver'; + "${name}/nfs_shares_config": value => $nfs_shares_config; + "${name}/nfs_mount_options": value => $nfs_mount_options; + "${name}/nfs_disk_util": value => $nfs_disk_util; + "${name}/nfs_sparsed_volumes": value => $nfs_sparsed_volumes; + "${name}/nfs_mount_point_base": value => $nfs_mount_point_base; + } +} diff --git a/cinder/manifests/backend/rbd.pp b/cinder/manifests/backend/rbd.pp new file mode 100644 index 000000000..d8262ac42 --- /dev/null +++ b/cinder/manifests/backend/rbd.pp @@ -0,0 +1,109 @@ +# == define: cinder::backend::rbd +# +# Setup Cinder to use the RBD driver. +# Compatible for multiple backends +# +# === Parameters +# +# [*rbd_pool*] +# (required) Specifies the pool name for the block device driver. +# +# [*rbd_user*] +# (required) A required parameter to configure OS init scripts and cephx. +# +# [*volume_backend_name*] +# (optional) Allows for the volume_backend_name to be separate of $name. +# Defaults to: $name +# +# [*rbd_ceph_conf*] +# (optional) Path to the ceph configuration file to use +# Defaults to '/etc/ceph/ceph.conf' +# +# [*rbd_flatten_volume_from_snapshot*] +# (optional) Enable flatten volumes created from snapshots. +# Defaults to false +# +# [*rbd_secret_uuid*] +# (optional) A required parameter to use cephx. +# Defaults to false +# +# [*volume_tmp_dir*] +# (optional) Location to store temporary image files if the volume +# driver does not write them directly to the volume +# Defaults to false +# +# [*rbd_max_clone_depth*] +# (optional) Maximum number of nested clones that can be taken of a +# volume before enforcing a flatten prior to next clone. +# A value of zero disables cloning +# Defaults to '5' +# +# [*glance_api_version*] +# (optional) DEPRECATED: Use cinder::glance Class instead. +# Glance API version. (Defaults to '2') +# Setting this parameter cause a duplicate resource declaration +# with cinder::glance +# +define cinder::backend::rbd ( + $rbd_pool, + $rbd_user, + $volume_backend_name = $name, + $rbd_ceph_conf = '/etc/ceph/ceph.conf', + $rbd_flatten_volume_from_snapshot = false, + $rbd_secret_uuid = false, + $volume_tmp_dir = false, + $rbd_max_clone_depth = '5', + # DEPRECATED PARAMETERS + $glance_api_version = undef, +) { + + include cinder::params + + if $glance_api_version { + warning('The glance_api_version parameter is deprecated, use glance_api_version of cinder::glance class instead.') + } + + cinder_config { + "${name}/volume_backend_name": value => $volume_backend_name; + "${name}/volume_driver": value => 'cinder.volume.drivers.rbd.RBDDriver'; + "${name}/rbd_ceph_conf": value => $rbd_ceph_conf; + "${name}/rbd_user": value => $rbd_user; + "${name}/rbd_pool": value => $rbd_pool; + "${name}/rbd_max_clone_depth": value => $rbd_max_clone_depth; + "${name}/rbd_flatten_volume_from_snapshot": value => $rbd_flatten_volume_from_snapshot; + } + + if $rbd_secret_uuid { + cinder_config {"${name}/rbd_secret_uuid": value => $rbd_secret_uuid;} + } else { + cinder_config {"${name}/rbd_secret_uuid": ensure => absent;} + } + + if $volume_tmp_dir { + cinder_config {"${name}/volume_tmp_dir": value => $volume_tmp_dir;} + } else { + cinder_config {"${name}/volume_tmp_dir": ensure => absent;} + } + + case $::osfamily { + 'Debian': { + $override_line = "env CEPH_ARGS=\"--id ${rbd_user}\"" + } + 'RedHat': { + $override_line = "export CEPH_ARGS=\"--id ${rbd_user}\"" + } + default: { + fail("unsuported osfamily ${::osfamily}, currently Debian and Redhat are the only supported platforms") + } + } + + # Creates an empty file if it doesn't yet exist + ensure_resource('file', $::cinder::params::ceph_init_override, {'ensure' => 'present'}) + + ensure_resource('file_line', 'set initscript env', { + line => $override_line, + path => $::cinder::params::ceph_init_override, + notify => Service['cinder-volume'] + }) + +} diff --git a/cinder/manifests/backend/vmdk.pp b/cinder/manifests/backend/vmdk.pp new file mode 100644 index 000000000..b390471b8 --- /dev/null +++ b/cinder/manifests/backend/vmdk.pp @@ -0,0 +1,88 @@ +# == define: cinder::backend::vmdk +# +# Configure the VMware VMDK driver for cinder. +# +# === Parameters +# +# [*host_ip*] +# The IP address of the VMware vCenter server. +# +# [*host_username*] +# The username for connection to VMware vCenter server. +# +# [*host_password*] +# The password for connection to VMware vCenter server. +# +# [*volume_backend_name*] +# Used to set the volume_backend_name in multiple backends. +# Defaults to $name as passed in the title. +# +# [*api_retry_count*] +# (optional) The number of times we retry on failures, +# e.g., socket error, etc. +# Defaults to 10. +# +# [*$max_object_retrieval*] +# (optional) The maximum number of ObjectContent data objects that should +# be returned in a single result. A positive value will cause +# the operation to suspend the retrieval when the count of +# objects reaches the specified maximum. The server may still +# limit the count to something less than the configured value. +# Any remaining objects may be retrieved with additional requests. +# Defaults to 100. +# +# [*task_poll_interval*] +# (optional) The interval in seconds used for polling of remote tasks. +# Defaults to 5. +# +# [*image_transfer_timeout_secs*] +# (optional) The timeout in seconds for VMDK volume transfer between Cinder and Glance. +# Defaults to 7200. +# +# [*wsdl_location*] +# (optional) VIM Service WSDL Location e.g +# http:///vimService.wsdl. Optional over-ride to +# default location for bug work-arounds. +# Defaults to None. +# +# [*volume_folder*] +# (optional) The name for the folder in the VC datacenter that will contain cinder volumes. +# Defaults to 'cinder-volumes'. +# +define cinder::backend::vmdk ( + $host_ip, + $host_username, + $host_password, + $volume_backend_name = $name, + $volume_folder = 'cinder-volumes', + $api_retry_count = 10, + $max_object_retrieval = 100, + $task_poll_interval = 5, + $image_transfer_timeout_secs = 7200, + $wsdl_location = undef + ) { + + cinder_config { + "${name}/volume_backend_name": value => $volume_backend_name; + "${name}/volume_driver": value => 'cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver'; + "${name}/vmware_host_ip": value => $host_ip; + "${name}/vmware_host_username": value => $host_username; + "${name}/vmware_host_password": value => $host_password; + "${name}/vmware_volume_folder": value => $volume_folder; + "${name}/vmware_api_retry_count": value => $api_retry_count; + "${name}/vmware_max_object_retrieval": value => $max_object_retrieval; + "${name}/vmware_task_poll_interval": value => $task_poll_interval; + "${name}/vmware_image_transfer_timeout_secs": value => $image_transfer_timeout_secs; + } + + if $wsdl_location { + cinder_config { + "${name}/vmware_wsdl_location": value => $wsdl_location; + } + } + + package { 'suds': + ensure => present, + provider => pip + } +} diff --git a/cinder/manifests/backends.pp b/cinder/manifests/backends.pp new file mode 100644 index 000000000..eb6747293 --- /dev/null +++ b/cinder/manifests/backends.pp @@ -0,0 +1,21 @@ +# == Class: cinder::backends +# +# Class to set the enabled_backends list +# +# === Parameters +# +# [*enabled_backends*] +# (required) a list of ini sections to enable. +# This should contain names used in ceph::backend::* resources. +# Example: ['volume1', 'volume2', 'sata3'] +# +# Author: Andrew Woodward +class cinder::backends ( + $enabled_backends = undef + ){ + + # Maybe this could be extented to dynamicly find the enabled names + cinder_config { + 'DEFAULT/enabled_backends': value => join($enabled_backends, ','); + } +} \ No newline at end of file diff --git a/cinder/manifests/backup.pp b/cinder/manifests/backup.pp new file mode 100644 index 000000000..a8c575a09 --- /dev/null +++ b/cinder/manifests/backup.pp @@ -0,0 +1,84 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# == Class: cinder::backup +# +# Setup Cinder backup service +# +# === Parameters +# +# [*backup_topic*] +# (optional) The topic volume backup nodes listen on. +# Defaults to 'cinder-backup' +# +# [*backup_manager*] +# (optional) Full class name for the Manager for volume backup. +# Defaults to 'cinder.backup.manager.BackupManager' +# +# [*backup_api_class*] +# (optional) The full class name of the volume backup API class. +# Defaults to 'cinder.backup.api.API' +# +# [*backup_name_template*] +# (optional) Template string to be used to generate backup names. +# Defaults to 'backup-%s' +# + +class cinder::backup ( + $enabled = true, + $package_ensure = 'present', + $backup_topic = 'cinder-backup', + $backup_manager = 'cinder.backup.manager.BackupManager', + $backup_api_class = 'cinder.backup.api.API', + $backup_name_template = 'backup-%s' +) { + + include cinder::params + + Cinder_config<||> ~> Service['cinder-backup'] + + if $::cinder::params::backup_package { + Package['cinder-backup'] -> Cinder_config<||> + Package['cinder-backup'] -> Service['cinder-backup'] + package { 'cinder-backup': + ensure => $package_ensure, + name => $::cinder::params::backup_package, + } + } + + if $enabled { + $ensure = 'running' + } else { + $ensure = 'stopped' + } + + service { 'cinder-backup': + ensure => $ensure, + name => $::cinder::params::backup_service, + enable => $enabled, + hasstatus => true, + require => Package['cinder'], + } + + cinder_config { + 'DEFAULT/backup_topic': value => $backup_topic; + 'DEFAULT/backup_manager': value => $backup_manager; + 'DEFAULT/backup_api_class': value => $backup_api_class; + 'DEFAULT/backup_name_template': value => $backup_name_template; + } + +} diff --git a/cinder/manifests/backup/ceph.pp b/cinder/manifests/backup/ceph.pp new file mode 100644 index 000000000..9ac208ab8 --- /dev/null +++ b/cinder/manifests/backup/ceph.pp @@ -0,0 +1,76 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# == Class: cinder::backup::ceph +# +# Setup Cinder to backup volumes into Ceph +# +# === Parameters +# +# [*backup_ceph_conf*] +# (optional) Ceph config file to use. +# Should be a valid ceph configuration file +# Defaults to '/etc/ceph/ceph.conf' +# +# [*backup_ceph_user*] +# (optional) The Ceph user to connect with. +# Should be a valid user +# Defaults to 'cinder' +# +# [*backup_ceph_chunk_size*] +# (optional) The chunk size in bytes that a backup will be broken into +# before transfer to backup store. +# Should be a valid integer +# Defaults to '134217728' +# +# [*backup_ceph_pool*] +# (optional) The Ceph pool to backup to. +# Should be a valid ceph pool +# Defaults to 'backups' +# +# [*backup_ceph_stripe_unit*] +# (optional) RBD stripe unit to use when creating a backup image. +# Should be a valid integer +# Defaults to '0' +# +# [*backup_ceph_stripe_count*] +# (optional) RBD stripe count to use when creating a backup image. +# Should be a valid integer +# Defaults to '0' +# + +class cinder::backup::ceph ( + $backup_driver = 'cinder.backup.driver.ceph', + $backup_ceph_conf = '/etc/ceph/ceph.conf', + $backup_ceph_user = 'cinder', + $backup_ceph_chunk_size = '134217728', + $backup_ceph_pool = 'backups', + $backup_ceph_stripe_unit = '0', + $backup_ceph_stripe_count = '0' +) { + + cinder_config { + 'DEFAULT/backup_driver': value => $backup_driver; + 'DEFAULT/backup_ceph_conf': value => $backup_ceph_conf; + 'DEFAULT/backup_ceph_user': value => $backup_ceph_user; + 'DEFAULT/backup_ceph_chunk_size': value => $backup_ceph_chunk_size; + 'DEFAULT/backup_ceph_pool': value => $backup_ceph_pool; + 'DEFAULT/backup_ceph_stripe_unit': value => $backup_ceph_stripe_unit; + 'DEFAULT/backup_ceph_stripe_count': value => $backup_ceph_stripe_count; + } + +} diff --git a/cinder/manifests/backup/swift.pp b/cinder/manifests/backup/swift.pp new file mode 100644 index 000000000..5b1fedfb8 --- /dev/null +++ b/cinder/manifests/backup/swift.pp @@ -0,0 +1,64 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# == Class: cinder::backup::swift +# +# Setup Cinder to backup volumes into Swift +# +# === Parameters +# +# [*backup_swift_url*] +# (optional) The URL of the Swift endpoint. +# Should be a valid Swift URL +# Defaults to 'http://localhost:8080/v1/AUTH_' +# +# [*backup_swift_container*] +# (optional) The default Swift container to use. +# Defaults to 'volumes_backup' +# +# [*backup_swift_object_size*] +# (optional) The size in bytes of Swift backup objects. +# Defaults to '52428800' +# +# [*backup_swift_retry_attempts*] +# (optional) The number of retries to make for Swift operations. +# Defaults to '3' +# +# [*backup_swift_retry_backoff*] +# (optional) The backoff time in seconds between Swift retries. +# Defaults to '2' +# + +class cinder::backup::swift ( + $backup_driver = 'cinder.backup.drivers.swift', + $backup_swift_url = 'http://localhost:8080/v1/AUTH_', + $backup_swift_container = 'volumes_backup', + $backup_swift_object_size = '52428800', + $backup_swift_retry_attempts = '3', + $backup_swift_retry_backoff = '2' +) { + + cinder_config { + 'DEFAULT/backup_driver': value => $backup_driver; + 'DEFAULT/backup_swift_url': value => $backup_swift_url; + 'DEFAULT/backup_swift_container': value => $backup_swift_container; + 'DEFAULT/backup_swift_object_size': value => $backup_swift_object_size; + 'DEFAULT/backup_swift_retry_attempts': value => $backup_swift_retry_attempts; + 'DEFAULT/backup_swift_retry_backoff': value => $backup_swift_retry_backoff; + } + +} diff --git a/cinder/manifests/base.pp b/cinder/manifests/base.pp new file mode 100644 index 000000000..575d49689 --- /dev/null +++ b/cinder/manifests/base.pp @@ -0,0 +1,30 @@ +# +class cinder::base ( + $rabbit_password, + $sql_connection, + $rabbit_host = '127.0.0.1', + $rabbit_port = 5672, + $rabbit_hosts = undef, + $rabbit_virtual_host = '/', + $rabbit_userid = 'nova', + $package_ensure = 'present', + $api_paste_config = '/etc/cinder/api-paste.ini', + $verbose = false +) { + + warning('The cinder::base class is deprecated. Use cinder instead.') + + class { 'cinder': + rabbit_password => $rabbit_password, + sql_connection => $sql_connection, + rabbit_host => $rabbit_host, + rabbit_port => $rabbit_port, + rabbit_hosts => $rabbit_hosts, + rabbit_virtual_host => $rabbit_virtual_host, + rabbit_userid => $rabbit_userid, + package_ensure => $package_ensure, + api_paste_config => $api_paste_config, + verbose => $verbose, + } + +} diff --git a/cinder/manifests/ceilometer.pp b/cinder/manifests/ceilometer.pp new file mode 100644 index 000000000..813ea687d --- /dev/null +++ b/cinder/manifests/ceilometer.pp @@ -0,0 +1,22 @@ +# == Class: cinder::ceilometer +# +# Setup Cinder to enable ceilometer can retrieve volume samples +# Ref: http://docs.openstack.org/developer/ceilometer/install/manual.html +# +# === Parameters +# +# [*notification_driver*] +# (option) Driver or drivers to handle sending notifications. +# Notice: rabbit_notifier has been deprecated in Grizzly, use rpc_notifier instead. +# + + +class cinder::ceilometer ( + $notification_driver = 'cinder.openstack.common.notifier.rpc_notifier' +) { + + cinder_config { + 'DEFAULT/notification_driver': value => $notification_driver; + } +} + diff --git a/cinder/manifests/client.pp b/cinder/manifests/client.pp new file mode 100644 index 000000000..1b6ad8226 --- /dev/null +++ b/cinder/manifests/client.pp @@ -0,0 +1,20 @@ +# == Class: cinder::client +# +# Installs Cinder python client. +# +# === Parameters +# +# [*ensure*] +# Ensure state for package. Defaults to 'present'. +# +class cinder::client( + $package_ensure = 'present' +) { + + include cinder::params + + package { 'python-cinderclient': + ensure => $package_ensure, + name => $::cinder::params::client_package, + } +} diff --git a/cinder/manifests/config.pp b/cinder/manifests/config.pp new file mode 100644 index 000000000..0fa29a3e9 --- /dev/null +++ b/cinder/manifests/config.pp @@ -0,0 +1,39 @@ +# == Class: cinder::config +# +# This class is used to manage arbitrary cinder configurations. +# +# === Parameters +# +# [*xxx_config*] +# (optional) Allow configuration of arbitrary cinder configurations. +# The value is an hash of xxx_config resources. Example: +# { 'DEFAULT/foo' => { value => 'fooValue'}, +# 'DEFAULT/bar' => { value => 'barValue'} +# } +# +# In yaml format, Example: +# xxx_config: +# DEFAULT/foo: +# value: fooValue +# DEFAULT/bar: +# value: barValue +# +# [**cinder_config**] +# (optional) Allow configuration of cinder.conf configurations. +# +# [**api_paste_ini_config**] +# (optional) Allow configuration of /etc/cinder/api-paste.ini configurations. +# +# NOTE: The configuration MUST NOT be already handled by this module +# or Puppet catalog compilation will fail with duplicate resources. +# +class cinder::config ( + $cinder_config = {}, + $api_paste_ini_config = {}, +) { + validate_hash($cinder_config) + validate_hash($api_paste_ini_config) + + create_resources('cinder_config', $cinder_config) + create_resources('cinder_api_paste_ini', $api_paste_ini_config) +} diff --git a/cinder/manifests/db/mysql.pp b/cinder/manifests/db/mysql.pp new file mode 100644 index 000000000..3cc850c04 --- /dev/null +++ b/cinder/manifests/db/mysql.pp @@ -0,0 +1,39 @@ +# +class cinder::db::mysql ( + $password, + $dbname = 'cinder', + $user = 'cinder', + $host = '127.0.0.1', + $allowed_hosts = undef, + $charset = 'latin1', + $cluster_id = 'localzone' +) { + + Class['cinder::db::mysql'] -> Exec<| title == 'cinder-manage db_sync' |> + Database[$dbname] ~> Exec<| title == 'cinder-manage db_sync' |> + + mysql::db { $dbname: + user => $user, + password => $password, + host => $host, + charset => $charset, + require => Class['mysql::config'], + } + + # Check allowed_hosts to avoid duplicate resource declarations + if is_array($allowed_hosts) and delete($allowed_hosts,$host) != [] { + $real_allowed_hosts = delete($allowed_hosts,$host) + } elsif is_string($allowed_hosts) and ($allowed_hosts != $host) { + $real_allowed_hosts = $allowed_hosts + } + + if $real_allowed_hosts { + # TODO this class should be in the mysql namespace + cinder::db::mysql::host_access { $real_allowed_hosts: + user => $user, + password => $password, + database => $dbname, + } + } + +} diff --git a/cinder/manifests/db/mysql/host_access.pp b/cinder/manifests/db/mysql/host_access.pp new file mode 100644 index 000000000..993f5a034 --- /dev/null +++ b/cinder/manifests/db/mysql/host_access.pp @@ -0,0 +1,16 @@ +# +# Used to grant access to the cinder mysql DB +# +define cinder::db::mysql::host_access ($user, $password, $database) { + database_user { "${user}@${name}": + password_hash => mysql_password($password), + provider => 'mysql', + require => Database[$database], + } + database_grant { "${user}@${name}/${database}": + # TODO figure out which privileges to grant. + privileges => 'all', + provider => 'mysql', + require => Database_user["${user}@${name}"] + } +} diff --git a/cinder/manifests/db/postgresql.pp b/cinder/manifests/db/postgresql.pp new file mode 100644 index 000000000..52aa15bb7 --- /dev/null +++ b/cinder/manifests/db/postgresql.pp @@ -0,0 +1,21 @@ +# +# Class that configures postgresql for cinder +# +# Requires the Puppetlabs postgresql module. +class cinder::db::postgresql( + $password, + $dbname = 'cinder', + $user = 'cinder' +) { + + require postgresql::python + + Postgresql::Db[$dbname] ~> Exec<| title == 'cinder-manage db_sync' |> + Package['python-psycopg2'] -> Exec<| title == 'cinder-manage db_sync' |> + + postgresql::db { $dbname: + user => $user, + password => $password, + } + +} diff --git a/cinder/manifests/db/sync.pp b/cinder/manifests/db/sync.pp new file mode 100644 index 000000000..942f25213 --- /dev/null +++ b/cinder/manifests/db/sync.pp @@ -0,0 +1,14 @@ +# +class cinder::db::sync { + + include cinder::params + + exec { 'cinder-manage db_sync': + command => $::cinder::params::db_sync_command, + path => '/usr/bin', + user => 'cinder', + refreshonly => true, + require => [File[$::cinder::params::cinder_conf], Class['cinder']], + logoutput => 'on_failure', + } +} diff --git a/cinder/manifests/glance.pp b/cinder/manifests/glance.pp new file mode 100644 index 000000000..a9f570784 --- /dev/null +++ b/cinder/manifests/glance.pp @@ -0,0 +1,82 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# == Class: cinder::glance +# +# Glance drive Cinder as a block storage backend to store image data. +# +# === Parameters +# +# [*glance_api_servers*] +# (optional) A list of the glance api servers available to cinder. +# Should be an array with [hostname|ip]:port +# Defaults to undef +# +# [*glance_api_version*] +# (optional) Glance API version. +# Should be 1 or 2 +# Defaults to 2 (current version) +# +# [*glance_num_retries*] +# (optional) Number retries when downloading an image from glance. +# Defaults to 0 +# +# [*glance_api_insecure*] +# (optional) Allow to perform insecure SSL (https) requests to glance. +# Defaults to false +# +# [*glance_api_ssl_compression*] +# (optional) Whether to attempt to negotiate SSL layer compression when +# using SSL (https) requests. Set to False to disable SSL +# layer compression. In some cases disabling this may improve +# data throughput, eg when high network bandwidth is available +# and you are using already compressed image formats such as qcow2. +# Defaults to false +# +# [*glance_request_timeout*] +# (optional) http/https timeout value for glance operations. +# Defaults to undef +# + +class cinder::glance ( + $glance_api_servers = undef, + $glance_api_version = '2', + $glance_num_retries = '0', + $glance_api_insecure = false, + $glance_api_ssl_compression = false, + $glance_request_timeout = undef +) { + + if is_array($glance_api_servers) { + cinder_config { + 'DEFAULT/glance_api_servers': value => join($glance_api_servers, ','); + } + } elsif is_string($glance_api_servers) { + cinder_config { + 'DEFAULT/glance_api_servers': value => $glance_api_servers; + } + } + + cinder_config { + 'DEFAULT/glance_api_version': value => $glance_api_version; + 'DEFAULT/glance_num_retries': value => $glance_num_retries; + 'DEFAULT/glance_api_insecure': value => $glance_api_insecure; + 'DEFAULT/glance_api_ssl_compression': value => $glance_api_ssl_compression; + 'DEFAULT/glance_request_timeout': value => $glance_request_timeout; + } + +} diff --git a/cinder/manifests/init.pp b/cinder/manifests/init.pp new file mode 100644 index 000000000..1bdfa0999 --- /dev/null +++ b/cinder/manifests/init.pp @@ -0,0 +1,161 @@ +# +# == Parameters +# +# [sql_idle_timeout] +# Timeout when db connections should be reaped. +# (Optional) Defaults to 3600. +# +# [use_syslog] +# Use syslog for logging. +# (Optional) Defaults to false. +# +# [log_facility] +# Syslog facility to receive log lines. +# (Optional) Defaults to LOG_USER. +# +# [*log_dir*] +# (optional) Directory where logs should be stored. +# If set to boolean false, it will not log to any directory. +# Defaults to '/var/log/cinder' +# +class cinder ( + $sql_connection, + $sql_idle_timeout = '3600', + $rpc_backend = 'cinder.openstack.common.rpc.impl_kombu', + $control_exchange = 'openstack', + $rabbit_host = '127.0.0.1', + $rabbit_port = 5672, + $rabbit_hosts = false, + $rabbit_virtual_host = '/', + $rabbit_userid = 'guest', + $rabbit_password = false, + $qpid_hostname = 'localhost', + $qpid_port = '5672', + $qpid_username = 'guest', + $qpid_password = false, + $qpid_reconnect = true, + $qpid_reconnect_timeout = 0, + $qpid_reconnect_limit = 0, + $qpid_reconnect_interval_min = 0, + $qpid_reconnect_interval_max = 0, + $qpid_reconnect_interval = 0, + $qpid_heartbeat = 60, + $qpid_protocol = 'tcp', + $qpid_tcp_nodelay = true, + $package_ensure = 'present', + $api_paste_config = '/etc/cinder/api-paste.ini', + $use_syslog = false, + $log_facility = 'LOG_USER', + $log_dir = '/var/log/cinder', + $verbose = false, + $debug = false +) { + + include cinder::params + + Package['cinder'] -> Cinder_config<||> + Package['cinder'] -> Cinder_api_paste_ini<||> + + # this anchor is used to simplify the graph between cinder components by + # allowing a resource to serve as a point where the configuration of cinder begins + anchor { 'cinder-start': } + + package { 'cinder': + ensure => $package_ensure, + name => $::cinder::params::package_name, + require => Anchor['cinder-start'], + } + + file { $::cinder::params::cinder_conf: + ensure => present, + owner => 'cinder', + group => 'cinder', + mode => '0600', + require => Package['cinder'], + } + + file { $::cinder::params::cinder_paste_api_ini: + ensure => present, + owner => 'cinder', + group => 'cinder', + mode => '0600', + require => Package['cinder'], + } + + if $rpc_backend == 'cinder.openstack.common.rpc.impl_kombu' { + + if ! $rabbit_password { + fail('Please specify a rabbit_password parameter.') + } + + cinder_config { + 'DEFAULT/rabbit_password': value => $rabbit_password, secret => true; + 'DEFAULT/rabbit_userid': value => $rabbit_userid; + 'DEFAULT/rabbit_virtual_host': value => $rabbit_virtual_host; + 'DEFAULT/control_exchange': value => $control_exchange; + } + + if $rabbit_hosts { + cinder_config { 'DEFAULT/rabbit_hosts': value => join($rabbit_hosts, ',') } + cinder_config { 'DEFAULT/rabbit_ha_queues': value => true } + } else { + cinder_config { 'DEFAULT/rabbit_host': value => $rabbit_host } + cinder_config { 'DEFAULT/rabbit_port': value => $rabbit_port } + cinder_config { 'DEFAULT/rabbit_hosts': value => "${rabbit_host}:${rabbit_port}" } + cinder_config { 'DEFAULT/rabbit_ha_queues': value => false } + } + } + + if $rpc_backend == 'cinder.openstack.common.rpc.impl_qpid' { + + if ! $qpid_password { + fail('Please specify a qpid_password parameter.') + } + + cinder_config { + 'DEFAULT/qpid_hostname': value => $qpid_hostname; + 'DEFAULT/qpid_port': value => $qpid_port; + 'DEFAULT/qpid_username': value => $qpid_username; + 'DEFAULT/qpid_password': value => $qpid_password, secret => true; + 'DEFAULT/qpid_reconnect': value => $qpid_reconnect; + 'DEFAULT/qpid_reconnect_timeout': value => $qpid_reconnect_timeout; + 'DEFAULT/qpid_reconnect_limit': value => $qpid_reconnect_limit; + 'DEFAULT/qpid_reconnect_interval_min': value => $qpid_reconnect_interval_min; + 'DEFAULT/qpid_reconnect_interval_max': value => $qpid_reconnect_interval_max; + 'DEFAULT/qpid_reconnect_interval': value => $qpid_reconnect_interval; + 'DEFAULT/qpid_heartbeat': value => $qpid_heartbeat; + 'DEFAULT/qpid_protocol': value => $qpid_protocol; + 'DEFAULT/qpid_tcp_nodelay': value => $qpid_tcp_nodelay; + } + } + + cinder_config { + 'DEFAULT/sql_connection': value => $sql_connection, secret => true; + 'DEFAULT/sql_idle_timeout': value => $sql_idle_timeout; + 'DEFAULT/verbose': value => $verbose; + 'DEFAULT/debug': value => $debug; + 'DEFAULT/api_paste_config': value => $api_paste_config; + 'DEFAULT/rpc_backend': value => $rpc_backend; + } + + if $log_dir { + cinder_config { + 'DEFAULT/log_dir': value => $log_dir; + } + } else { + cinder_config { + 'DEFAULT/log_dir': ensure => absent; + } + } + + if $use_syslog { + cinder_config { + 'DEFAULT/use_syslog': value => true; + 'DEFAULT/syslog_log_facility': value => $log_facility; + } + } else { + cinder_config { + 'DEFAULT/use_syslog': value => false; + } + } +} diff --git a/cinder/manifests/keystone/auth.pp b/cinder/manifests/keystone/auth.pp new file mode 100644 index 000000000..a1b73b653 --- /dev/null +++ b/cinder/manifests/keystone/auth.pp @@ -0,0 +1,93 @@ +# == Class: cinder::keystone::auth +# +# Configures Cinder user, service and endpoint in Keystone. +# +# === Parameters +# +# [*password*] +# Password for Cinder user. Required. +# +# [*email*] +# Email for Cinder user. Optional. Defaults to 'cinder@localhost'. +# +# [*auth_name*] +# Username for Cinder service. Optional. Defaults to 'cinder'. +# +# [*configure_endpoint*] +# Should Cinder endpoint be configured? Optional. Defaults to 'true'. +# +# [*service_type*] +# Type of service. Optional. Defaults to 'volume'. +# +# [*public_address*] +# Public address for endpoint. Optional. Defaults to '127.0.0.1'. +# +# [*admin_address*] +# Admin address for endpoint. Optional. Defaults to '127.0.0.1'. +# +# [*internal_address*] +# Internal address for endpoint. Optional. Defaults to '127.0.0.1'. +# +# [*port*] +# Port for endpoint. Optional. Defaults to '8776'. +# +# [*region*] +# Region for endpoint. Optional. Defaults to 'RegionOne'. +# +# [*tenant*] +# Tenant for Cinder user. Optional. Defaults to 'services'. +# +# [*public_protocol*] +# Protocol for public endpoint. Optional. Defaults to 'http'. +# +# [*internal_protocol*] +# Protocol for internal endpoint. Optional. Defaults to 'http'. +# +# [*admin_protocol*] +# Protocol for admin endpoint. Optional. Defaults to 'http'. +# +class cinder::keystone::auth ( + $password, + $auth_name = 'cinder', + $email = 'cinder@localhost', + $tenant = 'services', + $configure_endpoint = true, + $service_type = 'volume', + $public_address = '127.0.0.1', + $admin_address = '127.0.0.1', + $internal_address = '127.0.0.1', + $port = '8776', + $volume_version = 'v1', + $region = 'RegionOne', + $public_protocol = 'http', + $admin_protocol = 'http', + $internal_protocol = 'http' +) { + + Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'cinder-api' |> + + keystone_user { $auth_name: + ensure => present, + password => $password, + email => $email, + tenant => $tenant, + } + keystone_user_role { "${auth_name}@${tenant}": + ensure => present, + roles => 'admin', + } + keystone_service { $auth_name: + ensure => present, + type => $service_type, + description => 'Cinder Service', + } + + if $configure_endpoint { + keystone_endpoint { "${region}/${auth_name}": + ensure => present, + public_url => "${public_protocol}://${public_address}:${port}/${volume_version}/%(tenant_id)s", + admin_url => "${admin_protocol}://${admin_address}:${port}/${volume_version}/%(tenant_id)s", + internal_url => "${internal_protocol}://${internal_address}:${port}/${volume_version}/%(tenant_id)s", + } + } +} diff --git a/cinder/manifests/params.pp b/cinder/manifests/params.pp new file mode 100644 index 000000000..56b51edc0 --- /dev/null +++ b/cinder/manifests/params.pp @@ -0,0 +1,43 @@ +# +class cinder::params { + + $cinder_conf = '/etc/cinder/cinder.conf' + $cinder_paste_api_ini = '/etc/cinder/api-paste.ini' + + if $::osfamily == 'Debian' { + $package_name = 'cinder-common' + $client_package = 'python-cinderclient' + $api_package = 'cinder-api' + $api_service = 'cinder-api' + $backup_package = 'cinder-backup' + $backup_service = 'cinder-backup' + $scheduler_package = 'cinder-scheduler' + $scheduler_service = 'cinder-scheduler' + $volume_package = 'cinder-volume' + $volume_service = 'cinder-volume' + $db_sync_command = 'cinder-manage db sync' + $tgt_package_name = 'tgt' + $tgt_service_name = 'tgt' + $ceph_init_override = '/etc/init/cinder-volume.override' + + } elsif($::osfamily == 'RedHat') { + + $package_name = 'openstack-cinder' + $client_package = 'python-cinderclient' + $api_package = false + $api_service = 'openstack-cinder-api' + $backup_package = false + $backup_service = 'openstack-cinder-backup' + $scheduler_package = false + $scheduler_service = 'openstack-cinder-scheduler' + $volume_package = false + $volume_service = 'openstack-cinder-volume' + $db_sync_command = 'cinder-manage db sync' + $tgt_package_name = 'scsi-target-utils' + $tgt_service_name = 'tgtd' + $ceph_init_override = '/etc/sysconfig/openstack-cinder-volume' + + } else { + fail("unsuported osfamily ${::osfamily}, currently Debian and Redhat are the only supported platforms") + } +} diff --git a/cinder/manifests/qpid.pp b/cinder/manifests/qpid.pp new file mode 100644 index 000000000..6b4d29852 --- /dev/null +++ b/cinder/manifests/qpid.pp @@ -0,0 +1,35 @@ +# +# class for installing qpid server for cinder +# +# +class cinder::qpid( + $enabled = true, + $user='guest', + $password='guest', + $file='/var/lib/qpidd/qpidd.sasldb', + $realm='OPENSTACK' +) { + + # only configure cinder after the queue is up + Class['qpid::server'] -> Package<| title == 'cinder' |> + + if ($enabled) { + $service_ensure = 'running' + + qpid_user { $user: + password => $password, + file => $file, + realm => $realm, + provider => 'saslpasswd2', + require => Class['qpid::server'], + } + + } else { + $service_ensure = 'stopped' + } + + class { 'qpid::server': + service_ensure => $service_ensure + } + +} diff --git a/cinder/manifests/quota.pp b/cinder/manifests/quota.pp new file mode 100644 index 000000000..4542d468f --- /dev/null +++ b/cinder/manifests/quota.pp @@ -0,0 +1,34 @@ +# == Class: cinder::quota +# +# Setup and configure Cinder quotas. +# +# === Parameters +# +# [*quota_volumes*] +# (optional) Number of volumes allowed per project. Defaults to 10. +# +# [*quota_snapshots*] +# (optional) Number of volume snapshots allowed per project. Defaults to 10. +# +# [*quota_gigabytes*] +# (optional) Number of volume gigabytes (snapshots are also included) +# allowed per project. Defaults to 1000. +# +# [*quota_driver*] +# (optional) Default driver to use for quota checks. +# Defaults to 'cinder.quota.DbQuotaDriver'. +# +class cinder::quota ( + $quota_volumes = 10, + $quota_snapshots = 10, + $quota_gigabytes = 1000, + $quota_driver = 'cinder.quota.DbQuotaDriver' +) { + + cinder_config { + 'DEFAULT/quota_volumes': value => $quota_volumes; + 'DEFAULT/quota_snapshots': value => $quota_snapshots; + 'DEFAULT/quota_gigabytes': value => $quota_gigabytes; + 'DEFAULT/quota_driver': value => $quota_driver; + } +} diff --git a/cinder/manifests/rabbitmq.pp b/cinder/manifests/rabbitmq.pp new file mode 100644 index 000000000..df10c004e --- /dev/null +++ b/cinder/manifests/rabbitmq.pp @@ -0,0 +1,52 @@ +# +# class for installing rabbitmq server for cinder +# +# +class cinder::rabbitmq( + $userid = 'guest', + $password = 'guest', + $port = '5672', + $virtual_host = '/', + $enabled = true +) { + + # only configure cinder after the queue is up + Class['rabbitmq::service'] -> Anchor<| title == 'cinder-start' |> + + if ($enabled) { + if $userid == 'guest' { + $delete_guest_user = false + } else { + $delete_guest_user = true + rabbitmq_user { $userid: + admin => true, + password => $password, + provider => 'rabbitmqctl', + require => Class['rabbitmq::server'], + } + # I need to figure out the appropriate permissions + rabbitmq_user_permissions { "${userid}@${virtual_host}": + configure_permission => '.*', + write_permission => '.*', + read_permission => '.*', + provider => 'rabbitmqctl', + }->Anchor<| title == 'cinder-start' |> + } + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + class { 'rabbitmq::server': + service_ensure => $service_ensure, + port => $port, + delete_guest_user => $delete_guest_user, + } + + if ($enabled) { + rabbitmq_vhost { $virtual_host: + provider => 'rabbitmqctl', + require => Class['rabbitmq::server'], + } + } +} diff --git a/cinder/manifests/scheduler.pp b/cinder/manifests/scheduler.pp new file mode 100644 index 000000000..abb87470a --- /dev/null +++ b/cinder/manifests/scheduler.pp @@ -0,0 +1,43 @@ +# +class cinder::scheduler ( + $scheduler_driver = false, + $package_ensure = 'present', + $enabled = true +) { + + include cinder::params + + Cinder_config<||> ~> Service['cinder-scheduler'] + Cinder_api_paste_ini<||> ~> Service['cinder-scheduler'] + Exec<| title == 'cinder-manage db_sync' |> ~> Service['cinder-scheduler'] + + if $scheduler_driver { + cinder_config { + 'DEFAULT/scheduler_driver': value => $scheduler_driver; + } + } + + if $::cinder::params::scheduler_package { + Package['cinder-scheduler'] -> Cinder_config<||> + Package['cinder-scheduler'] -> Cinder_api_paste_ini<||> + Package['cinder-scheduler'] -> Service['cinder-scheduler'] + package { 'cinder-scheduler': + ensure => $package_ensure, + name => $::cinder::params::scheduler_package, + } + } + + if $enabled { + $ensure = 'running' + } else { + $ensure = 'stopped' + } + + service { 'cinder-scheduler': + ensure => $ensure, + name => $::cinder::params::scheduler_service, + enable => $enabled, + hasstatus => true, + require => Package['cinder'], + } +} diff --git a/cinder/manifests/setup_test_volume.pp b/cinder/manifests/setup_test_volume.pp new file mode 100644 index 000000000..f8a2452de --- /dev/null +++ b/cinder/manifests/setup_test_volume.pp @@ -0,0 +1,47 @@ +# == Class: cinder::setup_test_volume +# +# Setup a volume group on a loop device for test purposes. +# +# === Parameters +# +# [*volume_name*] +# Volume group name. Defaults to 'cinder-volumes'. +# +# [*size*] +# Volume group size. Defaults to '4G'. +# +# [*loopback_device*] +# Loop device name. Defaults to '/dev/loop2'. +# +class cinder::setup_test_volume( + $volume_name = 'cinder-volumes', + $size = '4G', + $loopback_device = '/dev/loop2' +) { + + Exec { + cwd => '/tmp/', + } + + package { 'lvm2': + ensure => present, + } ~> + + exec { "/bin/dd if=/dev/zero of=${volume_name} bs=1 count=0 seek=${size}": + unless => "/sbin/vgdisplay ${volume_name}" + } ~> + + exec { "/sbin/losetup ${loopback_device} ${volume_name}": + refreshonly => true, + } ~> + + exec { "/sbin/pvcreate ${loopback_device}": + refreshonly => true, + } ~> + + exec { "/sbin/vgcreate ${volume_name} ${loopback_device}": + refreshonly => true, + } + +} + diff --git a/cinder/manifests/type.pp b/cinder/manifests/type.pp new file mode 100644 index 000000000..2134d1866 --- /dev/null +++ b/cinder/manifests/type.pp @@ -0,0 +1,67 @@ +# ==Define: cinder::type +# +# Creates cinder type and assigns backends. +# +# === Parameters +# +# [*os_password*] +# (required) The keystone tenant:username password. +# +# [*set_key*] +# (optional) Must be used with set_value. Accepts a single string be used +# as the key in type_set +# +# [*set_value*] +# (optional) Accepts list of strings or singular string. A list of values +# passed to type_set +# +# [*os_tenant_name*] +# (optional) The keystone tenant name. Defaults to 'admin'. +# +# [*os_username*] +# (optional) The keystone user name. Defaults to 'admin. +# +# [*os_auth_url*] +# (optional) The keystone auth url. Defaults to 'http://127.0.0.1:5000/v2.0/'. +# +# Author: Andrew Woodward + +define cinder::type ( + $os_password, + $set_key = undef, + $set_value = undef, + $os_tenant_name = 'admin', + $os_username = 'admin', + $os_auth_url = 'http://127.0.0.1:5000/v2.0/', + ) { + + $volume_name = $name + +# TODO: (xarses) This should be moved to a ruby provider so that among other +# reasons, the credential discovery magic can occur like in neutron. + + exec {"cinder type-create ${volume_name}": + path => '/usr/bin', + command => "cinder type-create ${volume_name}", + environment => [ + "OS_TENANT_NAME=${os_tenant_name}", + "OS_USERNAME=${os_username}", + "OS_PASSWORD=${os_password}", + "OS_AUTH_URL=${os_auth_url}", + ], + require => Package['python-cinderclient'] + } + + if ($set_value and $set_key) { + + Exec["cinder type-create ${volume_name}"] -> + cinder::type_set { $set_value: + type => $volume_name, + key => $set_key, + os_password => $os_password, + os_tenant_name => $os_tenant_name, + os_username => $os_username, + os_auth_url => $os_auth_url, + } + } +} \ No newline at end of file diff --git a/cinder/manifests/type_set.pp b/cinder/manifests/type_set.pp new file mode 100644 index 000000000..6f7ecc78a --- /dev/null +++ b/cinder/manifests/type_set.pp @@ -0,0 +1,51 @@ +# ==Define: cinder::type_set +# +# Assigns keys after the volume type is set. +# +# === Parameters +# +# [*os_password*] +# (required) The keystone tenant:username password. +# +# [*type*] +# (required) Accepts single name of type to set. +# +# [*key*] +# (required) the key name that we are setting the value for. +# +# [*os_tenant_name*] +# (optional) The keystone tenant name. Defaults to 'admin'. +# +# [*os_username*] +# (optional) The keystone user name. Defaults to 'admin. +# +# [*os_auth_url*] +# (optional) The keystone auth url. Defaults to 'http://127.0.0.1:5000/v2.0/'. +# +# Author: Andrew Woodward + + +define cinder::type_set ( + $type, + $key, + $os_password, + $os_tenant_name = 'admin', + $os_username = 'admin', + $os_auth_url = 'http://127.0.0.1:5000/v2.0/', + ) { + +# TODO: (xarses) This should be moved to a ruby provider so that among other +# reasons, the credential discovery magic can occur like in neutron. + + exec {"cinder type-key ${type} set ${key}=${name}": + path => '/usr/bin', + command => "cinder type-key ${type} set ${key}=${name}", + environment => [ + "OS_TENANT_NAME=${os_tenant_name}", + "OS_USERNAME=${os_username}", + "OS_PASSWORD=${os_password}", + "OS_AUTH_URL=${os_auth_url}", + ], + require => Package['python-cinderclient'] + } +} diff --git a/cinder/manifests/vmware.pp b/cinder/manifests/vmware.pp new file mode 100644 index 000000000..b254eaf2d --- /dev/null +++ b/cinder/manifests/vmware.pp @@ -0,0 +1,53 @@ +# ==Define: cinder::vmware +# +# Creates vmdk specific disk file type & clone type. +# +# === Parameters +# +# [*os_password*] +# (required) The keystone tenant:username password. +# +# [*os_tenant_name*] +# (optional) The keystone tenant name. Defaults to 'admin'. +# +# [*os_username*] +# (optional) The keystone user name. Defaults to 'admin. +# +# [*os_auth_url*] +# (optional) The keystone auth url. Defaults to 'http://127.0.0.1:5000/v2.0/'. +# +class cinder::vmware ( + $os_password, + $os_tenant_name = 'admin', + $os_username = 'admin', + $os_auth_url = 'http://127.0.0.1:5000/v2.0/' + ) { + + Cinder::Type { + os_password => $os_password, + os_tenant_name => $os_tenant_name, + os_username => $os_username, + os_auth_url => $os_auth_url + } + + cinder::type {'vmware-thin': + set_value => 'thin', + set_key => 'vmware:vmdk_type' + } + cinder::type {'vmware-thick': + set_value => 'thick', + set_key => 'vmware:vmdk_type' + } + cinder::type {'vmware-eagerZeroedThick': + set_value => 'eagerZeroedThick', + set_key => 'vmware:vmdk_type' + } + cinder::type {'vmware-full': + set_value => 'full', + set_key => 'vmware:clone_type' + } + cinder::type {'vmware-linked': + set_value => 'linked', + set_key => 'vmware:clone_type' + } +} \ No newline at end of file diff --git a/cinder/manifests/volume.pp b/cinder/manifests/volume.pp new file mode 100644 index 000000000..d53510d4e --- /dev/null +++ b/cinder/manifests/volume.pp @@ -0,0 +1,38 @@ +# $volume_name_template = volume-%s +class cinder::volume ( + $package_ensure = 'present', + $enabled = true +) { + + include cinder::params + + Cinder_config<||> ~> Service['cinder-volume'] + Cinder_api_paste_ini<||> ~> Service['cinder-volume'] + Exec<| title == 'cinder-manage db_sync' |> ~> Service['cinder-volume'] + + if $::cinder::params::volume_package { + Package['cinder-volume'] -> Cinder_config<||> + Package['cinder-volume'] -> Cinder_api_paste_ini<||> + Package['cinder'] -> Package['cinder-volume'] + Package['cinder-volume'] -> Service['cinder-volume'] + package { 'cinder-volume': + ensure => $package_ensure, + name => $::cinder::params::volume_package, + } + } + + if $enabled { + $ensure = 'running' + } else { + $ensure = 'stopped' + } + + service { 'cinder-volume': + ensure => $ensure, + name => $::cinder::params::volume_service, + enable => $enabled, + hasstatus => true, + require => Package['cinder'], + } + +} diff --git a/cinder/manifests/volume/glusterfs.pp b/cinder/manifests/volume/glusterfs.pp new file mode 100644 index 000000000..f1c35bf7b --- /dev/null +++ b/cinder/manifests/volume/glusterfs.pp @@ -0,0 +1,59 @@ +# +# == Class: cinder::volume::glusterfs +# +# Configures Cinder to use GlusterFS as a volume driver +# +# === Parameters +# +# [*glusterfs_shares*] +# (required) An array of GlusterFS volume locations. +# Must be an array even if there is only one volume. +# +# [*glusterfs_disk_util*] +# (optional) The utility used to calculate how much free +# space is available on each Gluster volume. +# Defaults to undef which uses the driver's default of "df". +# +# [*glusterfs_sparsed_volumes*] +# (optional) Whether or not to use sparse (thin) volumes. +# Defaults to undef which uses the driver's default of "true". +# +# [*glusterfs_mount_point_base*] +# (optional) Where to mount the Gluster volumes. +# Defaults to undef which uses the driver's default of "$state_path/mnt". +# +# [*glusterfs_shares_config*] +# (optional) The config file to store the given $glusterfs_shares. +# Defaults to '/etc/cinder/shares.conf' +# +# === Examples +# +# class { 'cinder::volume::glusterfs': +# glusterfs_shares = ['192.168.1.1:/volumes'], +# } +# +class cinder::volume::glusterfs ( + $glusterfs_shares, + $glusterfs_disk_util = undef, + $glusterfs_sparsed_volumes = undef, + $glusterfs_mount_point_base = undef, + $glusterfs_shares_config = '/etc/cinder/shares.conf' +) { + + $content = join($glusterfs_shares, "\n") + + file {$glusterfs_shares_config: + content => "${content}\n", + require => Package['cinder'], + notify => Service['cinder-volume'] + } + + cinder_config { + 'DEFAULT/volume_driver': value => + 'cinder.volume.drivers.glusterfs.GlusterfsDriver'; + 'DEFAULT/glusterfs_shares_config': value => $glusterfs_shares_config; + 'DEFAULT/glusterfs_disk_util': value => $glusterfs_disk_util; + 'DEFAULT/glusterfs_sparsed_volumes': value => $glusterfs_sparsed_volumes; + 'DEFAULT/glusterfs_mount_point_base': value => $glusterfs_mount_point_base; + } +} diff --git a/cinder/manifests/volume/iscsi.pp b/cinder/manifests/volume/iscsi.pp new file mode 100644 index 000000000..4c6e05dc2 --- /dev/null +++ b/cinder/manifests/volume/iscsi.pp @@ -0,0 +1,46 @@ +# +class cinder::volume::iscsi ( + $iscsi_ip_address, + $volume_group = 'cinder-volumes', + $iscsi_helper = 'tgtadm' +) { + + include cinder::params + + cinder_config { + 'DEFAULT/iscsi_ip_address': value => $iscsi_ip_address; + 'DEFAULT/iscsi_helper': value => $iscsi_helper; + 'DEFAULT/volume_group': value => $volume_group; + } + + case $iscsi_helper { + 'tgtadm': { + package { 'tgt': + ensure => present, + name => $::cinder::params::tgt_package_name, + } + + if($::osfamily == 'RedHat') { + file_line { 'cinder include': + path => '/etc/tgt/targets.conf', + line => 'include /etc/cinder/volumes/*', + match => '#?include /', + require => Package['tgt'], + notify => Service['tgtd'], + } + } + + service { 'tgtd': + ensure => running, + name => $::cinder::params::tgt_service_name, + enable => true, + require => Class['cinder::volume'], + } + } + + default: { + fail("Unsupported iscsi helper: ${iscsi_helper}.") + } + } + +} diff --git a/cinder/manifests/volume/netapp.pp b/cinder/manifests/volume/netapp.pp new file mode 100644 index 000000000..e4a670b18 --- /dev/null +++ b/cinder/manifests/volume/netapp.pp @@ -0,0 +1,149 @@ +# == Class: cinder::volume::netapp +# +# Configures Cinder to use the NetApp unified volume driver +# +# === Parameters +# +# [*netapp_login*] +# (required) Administrative user account name used to access the storage +# system. +# +# [*netapp_password*] +# (required) Password for the administrative user account specified in the +# netapp_login parameter. +# +# [*netapp_server_hostname*] +# (required) The hostname (or IP address) for the storage system. +# +# [*netapp_server_port*] +# (optional) The TCP port to use for communication with ONTAPI on the +# storage system. Traditionally, port 80 is used for HTTP and port 443 is +# used for HTTPS; however, this value should be changed if an alternate +# port has been configured on the storage system. +# Defaults to 80 +# +# [*netapp_size_multiplier*] +# (optional) The quantity to be multiplied by the requested volume size to +# ensure enough space is available on the virtual storage server (Vserver) to +# fulfill the volume creation request. +# Defaults to 1.2 +# +# [*netapp_storage_family*] +# (optional) The storage family type used on the storage system; valid values +# are ontap_7mode for using Data ONTAP operating in 7-Mode or ontap_cluster +# for using clustered Data ONTAP. +# Defaults to ontap_cluster +# +# [*netapp_storage_protocol*] +# (optional) The storage protocol to be used on the data path with the storage +# system; valid values are iscsi or nfs. +# Defaults to nfs +# +# [*netapp_transport_type*] +# (optional) The transport protocol used when communicating with ONTAPI on the +# storage system. Valid values are http or https. +# Defaults to http +# +# [*netapp_vfiler*] +# (optional) The vFiler unit on which provisioning of block storage volumes +# will be done. This parameter is only used by the driver when connecting to +# an instance with a storage family of Data ONTAP operating in 7-Mode and the +# storage protocol selected is iSCSI. Only use this parameter when utilizing +# the MultiStore feature on the NetApp storage system. +# Defaults to '' +# +# [*netapp_volume_list*] +# (optional) This parameter is only utilized when the storage protocol is +# configured to use iSCSI. This parameter is used to restrict provisioning to +# the specified controller volumes. Specify the value of this parameter to be +# a comma separated list of NetApp controller volume names to be used for +# provisioning. +# Defaults to '' +# +# [*netapp_vserver*] +# (optional) This parameter specifies the virtual storage server (Vserver) +# name on the storage cluster on which provisioning of block storage volumes +# should occur. If using the NFS storage protocol, this parameter is mandatory +# for storage service catalog support (utilized by Cinder volume type +# extra_specs support). If this parameter is specified, the exports belonging +# to the Vserver will only be used for provisioning in the future. Block +# storage volumes on exports not belonging to the Vserver specified by +# this parameter will continue to function normally. +# Defaults to '' +# +# [*expiry_thres_minutes*] +# (optional) This parameter specifies the threshold for last access time for +# images in the NFS image cache. When a cache cleaning cycle begins, images +# in the cache that have not been accessed in the last M minutes, where M is +# the value of this parameter, will be deleted from the cache to create free +# space on the NFS share. +# Defaults to 720 +# +# [*thres_avl_size_perc_start*] +# (optional) If the percentage of available space for an NFS share has +# dropped below the value specified by this parameter, the NFS image cache +# will be cleaned. +# Defaults to 20 +# +# [*thres_avl_size_perc_stop*] +# (optional) When the percentage of available space on an NFS share has reached the +# percentage specified by this parameter, the driver will stop clearing files +# from the NFS image cache that have not been accessed in the last M +# 'minutes, where M is the value of the expiry_thres_minutes parameter. +# Defaults to 60 +# +# === Examples +# +# class { 'cinder::volume::netapp': +# netapp_login => 'clusterAdmin', +# netapp_password => 'password', +# netapp_server_hostname => 'netapp.mycorp.com', +# netapp_server_port => '443', +# netapp_transport_type => 'https', +# netapp_vserver => 'openstack-vserver', +# } +# +# === Authors +# +# Bob Callaway +# +# === Copyright +# +# Copyright 2013 NetApp, Inc. +# +class cinder::volume::netapp ( + $netapp_login, + $netapp_password, + $netapp_server_hostname, + $netapp_server_port = '80', + $netapp_size_multiplier = '1.2', + $netapp_storage_family = 'ontap_cluster', + $netapp_storage_protocol = 'nfs', + $netapp_transport_type = 'http', + $netapp_vfiler = '', + $netapp_volume_list = '', + $netapp_vserver = '', + $expiry_thres_minutes = '720', + $thres_avl_size_perc_start = '20', + $thres_avl_size_perc_stop = '60' +) { + + cinder_config { + 'DEFAULT/volume_driver': value => 'cinder.volume.drivers.netapp.common.NetAppDriver'; + 'DEFAULT/netapp_login': value => $netapp_login; + 'DEFAULT/netapp_password': value => $netapp_password, secret => true; + 'DEFAULT/netapp_server_hostname': value => $netapp_server_hostname; + 'DEFAULT/netapp_server_port': value => $netapp_server_port; + 'DEFAULT/netapp_size_multiplier': value => $netapp_size_multiplier; + 'DEFAULT/netapp_storage_family': value => $netapp_storage_family; + 'DEFAULT/netapp_storage_protocol': value => $netapp_storage_protocol; + 'DEFAULT/netapp_transport_type': value => $netapp_transport_type; + 'DEFAULT/netapp_vfiler': value => $netapp_vfiler; + 'DEFAULT/netapp_volume_list': value => $netapp_volume_list; + 'DEFAULT/netapp_vserver': value => $netapp_vserver; + 'DEFAULT/expiry_thres_minutes': value => $expiry_thres_minutes; + 'DEFAULT/thres_avl_size_perc_start': value => $thres_avl_size_perc_start; + 'DEFAULT/thres_avl_size_perc_stop': value => $thres_avl_size_perc_stop; + } + +} diff --git a/cinder/manifests/volume/nexenta.pp b/cinder/manifests/volume/nexenta.pp new file mode 100644 index 000000000..3ada6ad11 --- /dev/null +++ b/cinder/manifests/volume/nexenta.pp @@ -0,0 +1,53 @@ +# == Class: cinder::volume::nexenta +# +# Setups Cinder with Nexenta volume driver. +# +# === Parameters +# +# [*nexenta_user*] +# (required) User name to connect to Nexenta SA. +# +# [*nexenta_password*] +# (required) Password to connect to Nexenta SA. +# +# [*nexenta_host*] +# (required) IP address of Nexenta SA. +# +# [*nexenta_volume*] +# (optional) Pool on SA that will hold all volumes. Defaults to 'cinder'. +# +# [*nexenta_target_prefix*] +# (optional) IQN prefix for iSCSI targets. Defaults to 'iqn:'. +# +# [*nexenta_target_group_prefix*] +# (optional) Prefix for iSCSI target groups on SA. Defaults to 'cinder/'. +# +# [*nexenta_blocksize*] +# (optional) Block size for volumes. Defaults to '8k'. +# +# [*nexenta_sparse*] +# (optional) Flag to create sparse volumes. Defaults to true. +# +class cinder::volume::nexenta ( + $nexenta_user, + $nexenta_password, + $nexenta_host, + $nexenta_volume = 'cinder', + $nexenta_target_prefix = 'iqn:', + $nexenta_target_group_prefix = 'cinder/', + $nexenta_blocksize = '8k', + $nexenta_sparse = true +) { + + cinder_config { + 'DEFAULT/nexenta_user': value => $nexenta_user; + 'DEFAULT/nexenta_password': value => $nexenta_password; + 'DEFAULT/nexenta_host': value => $nexenta_host; + 'DEFAULT/nexenta_volume': value => $nexenta_volume; + 'DEFAULT/nexenta_target_prefix': value => $nexenta_target_prefix; + 'DEFAULT/nexenta_target_group_prefix': value => $nexenta_target_group_prefix; + 'DEFAULT/nexenta_blocksize': value => $nexenta_blocksize; + 'DEFAULT/nexenta_sparse': value => $nexenta_sparse; + 'DEFAULT/volume_driver': value => 'cinder.volume.drivers.nexenta.volume.NexentaDriver'; + } +} diff --git a/cinder/manifests/volume/nfs.pp b/cinder/manifests/volume/nfs.pp new file mode 100644 index 000000000..ffdbbaf04 --- /dev/null +++ b/cinder/manifests/volume/nfs.pp @@ -0,0 +1,26 @@ +# +class cinder::volume::nfs ( + $nfs_servers = [], + $nfs_mount_options = undef, + $nfs_disk_util = undef, + $nfs_sparsed_volumes = undef, + $nfs_mount_point_base = undef, + $nfs_shares_config = '/etc/cinder/shares.conf' +) { + + file {$nfs_shares_config: + content => join($nfs_servers, "\n"), + require => Package['cinder'], + notify => Service['cinder-volume'] + } + + cinder_config { + 'DEFAULT/volume_driver': value => + 'cinder.volume.drivers.nfs.NfsDriver'; + 'DEFAULT/nfs_shares_config': value => $nfs_shares_config; + 'DEFAULT/nfs_mount_options': value => $nfs_mount_options; + 'DEFAULT/nfs_disk_util': value => $nfs_disk_util; + 'DEFAULT/nfs_sparsed_volumes': value => $nfs_sparsed_volumes; + 'DEFAULT/nfs_mount_point_base': value => $nfs_mount_point_base; + } +} diff --git a/cinder/manifests/volume/rbd.pp b/cinder/manifests/volume/rbd.pp new file mode 100644 index 000000000..00a776515 --- /dev/null +++ b/cinder/manifests/volume/rbd.pp @@ -0,0 +1,104 @@ +# == Class: cinder::volume::rbd +# +# Setup Cinder to use the RBD driver. +# +# === Parameters +# +# [*rbd_pool*] +# (required) Specifies the pool name for the block device driver. +# +# [*rbd_user*] +# (required) A required parameter to configure OS init scripts and cephx. +# +# [*rbd_ceph_conf*] +# (optional) Path to the ceph configuration file to use +# Defaults to '/etc/ceph/ceph.conf' +# +# [*rbd_flatten_volume_from_snapshot*] +# (optional) Enable flatten volumes created from snapshots. +# Defaults to false +# +# [*rbd_secret_uuid*] +# (optional) A required parameter to use cephx. +# Defaults to false +# +# [*volume_tmp_dir*] +# (optional) Location to store temporary image files if the volume +# driver does not write them directly to the volume +# Defaults to false +# +# [*rbd_max_clone_depth*] +# (optional) Maximum number of nested clones that can be taken of a +# volume before enforcing a flatten prior to next clone. +# A value of zero disables cloning +# Defaults to '5' +# +# [*glance_api_version*] +# (optional) DEPRECATED: Use cinder::glance Class instead. +# Glance API version. (Defaults to '2') +# Setting this parameter cause a duplicate resource declaration +# with cinder::glance +# +class cinder::volume::rbd ( + $rbd_pool, + $rbd_user, + $rbd_ceph_conf = '/etc/ceph/ceph.conf', + $rbd_flatten_volume_from_snapshot = false, + $rbd_secret_uuid = false, + $volume_tmp_dir = false, + $rbd_max_clone_depth = '5', + # DEPRECATED PARAMETERS + $glance_api_version = undef, +) { + + include cinder::params + + if $glance_api_version { + warning('The glance_api_version is deprecated, use glance_api_version of cinder::glance class instead.') + } + + cinder_config { + 'DEFAULT/volume_driver': value => 'cinder.volume.drivers.rbd.RBDDriver'; + 'DEFAULT/rbd_ceph_conf': value => $rbd_ceph_conf; + 'DEFAULT/rbd_user': value => $rbd_user; + 'DEFAULT/rbd_pool': value => $rbd_pool; + 'DEFAULT/rbd_max_clone_depth': value => $rbd_max_clone_depth; + 'DEFAULT/rbd_flatten_volume_from_snapshot': value => $rbd_flatten_volume_from_snapshot; + } + + if $rbd_secret_uuid { + cinder_config {'DEFAULT/rbd_secret_uuid': value => $rbd_secret_uuid;} + } else { + cinder_config {'DEFAULT/rbd_secret_uuid': ensure => absent;} + } + + if $volume_tmp_dir { + cinder_config {'DEFAULT/volume_tmp_dir': value => $volume_tmp_dir;} + } else { + cinder_config {'DEFAULT/volume_tmp_dir': ensure => absent;} + } + + case $::osfamily { + 'Debian': { + $override_line = "env CEPH_ARGS=\"--id ${rbd_user}\"" + } + 'RedHat': { + $override_line = "export CEPH_ARGS=\"--id ${rbd_user}\"" + } + default: { + fail("unsuported osfamily ${::osfamily}, currently Debian and Redhat are the only supported platforms") + } + } + + # Creates an empty file if it doesn't yet exist + file { $::cinder::params::ceph_init_override: + ensure => present, + } + + file_line { 'set initscript env': + line => $override_line, + path => $::cinder::params::ceph_init_override, + notify => Service['cinder-volume'], + } + +} diff --git a/cinder/manifests/volume/san.pp b/cinder/manifests/volume/san.pp new file mode 100644 index 000000000..0471b1560 --- /dev/null +++ b/cinder/manifests/volume/san.pp @@ -0,0 +1,74 @@ +# == Class: cinder::volume::san +# +# Configures Cinder volume SAN driver. +# Parameters are particular to each volume driver. +# +# === Parameters +# +# [*volume_driver*] +# (required) Setup cinder-volume to use volume driver. +# +# [*san_thin_provision*] +# (optional) Use thin provisioning for SAN volumes? Defaults to true. +# +# [*san_ip*] +# (optional) IP address of SAN controller. +# +# [*san_login*] +# (optional) Username for SAN controller. Defaults to 'admin'. +# +# [*san_password*] +# (optional) Password for SAN controller. +# +# [*san_private_key*] +# (optional) Filename of private key to use for SSH authentication. +# +# [*san_clustername*] +# (optional) Cluster name to use for creating volumes. +# +# [*san_ssh_port*] +# (optional) SSH port to use with SAN. Defaults to 22. +# +# [*san_is_local*] +# (optional) Execute commands locally instead of over SSH +# use if the volume service is running on the SAN device. +# +# [*ssh_conn_timeout*] +# (optional) SSH connection timeout in seconds. Defaults to 30. +# +# [*ssh_min_pool_conn*] +# (optional) Minimum ssh connections in the pool. +# +# [*ssh_min_pool_conn*] +# (optional) Maximum ssh connections in the pool. +# +class cinder::volume::san ( + $volume_driver, + $san_thin_provision = true, + $san_ip = undef, + $san_login = 'admin', + $san_password = undef, + $san_private_key = undef, + $san_clustername = undef, + $san_ssh_port = 22, + $san_is_local = false, + $ssh_conn_timeout = 30, + $ssh_min_pool_conn = 1, + $ssh_max_pool_conn = 5 +) { + + cinder_config { + 'DEFAULT/volume_driver': value => $volume_driver; + 'DEFAULT/san_thin_provision': value => $san_thin_provision; + 'DEFAULT/san_ip': value => $san_ip; + 'DEFAULT/san_login': value => $san_login; + 'DEFAULT/san_password': value => $san_password; + 'DEFAULT/san_private_key': value => $san_private_key; + 'DEFAULT/san_clustername': value => $san_clustername; + 'DEFAULT/san_ssh_port': value => $san_ssh_port; + 'DEFAULT/san_is_local': value => $san_is_local; + 'DEFAULT/ssh_conn_timeout': value => $ssh_conn_timeout; + 'DEFAULT/ssh_min_pool_conn': value => $ssh_min_pool_conn; + 'DEFAULT/ssh_max_pool_conn': value => $ssh_max_pool_conn; + } +} diff --git a/cinder/manifests/volume/solidfire.pp b/cinder/manifests/volume/solidfire.pp new file mode 100644 index 000000000..041c96699 --- /dev/null +++ b/cinder/manifests/volume/solidfire.pp @@ -0,0 +1,59 @@ +# == Class: cinder::volume::solidfire +# +# Configures Cinder volume SolidFire driver. +# Parameters are particular to each volume driver. +# +# === Parameters +# +# [*volume_driver*] +# (optional) Setup cinder-volume to use SolidFire volume driver. +# Defaults to 'cinder.volume.drivers.solidfire.SolidFire' +# +# [*san_ip*] +# (required) IP address of SolidFire clusters MVIP. +# +# [*san_login*] +# (required) Username for SolidFire admin account. +# +# [*san_password*] +# (required) Password for SolidFire admin account. +# +# [*sf_emulate_512*] +# (optional) Use 512 byte emulation for volumes. +# Defaults to True +# +# [*sf_allow_tenant_qos*] +# (optional) Allow tenants to specify QoS via volume metadata. +# Defaults to False +# +# [*sf_account_prefix*] +# (optional) Prefix to use when creating tenant accounts on SolidFire Cluster. +# Defaults to None, so account name is simply the tenant-uuid +# +# [*sf_api_port*] +# (optional) Port ID to use to connect to SolidFire API. +# Defaults to 443 +# +class cinder::volume::solidfire( + $volume_driver = 'cinder.volume.drivers.solidfire.SolidFire', + $san_ip, + $san_login, + $san_password, + $sf_emulate_512 = true, + $sf_allow_tenant_qos = false, + $sf_account_prefix = '', + $sf_api_port = '443' + +) { + + cinder_config { + 'DEFAULT/volume_driver': value => $volume_driver; + 'DEFAULT/san_ip': value => $san_ip; + 'DEFAULT/san_login': value => $san_login; + 'DEFAULT/san_password': value => $san_password; + 'DEFAULT/sf_emulate_512': value => $sf_emulate_512; + 'DEFAULT/sf_allow_tenant_qos':value => $sf_allow_tenant_qos; + 'DEFAULT/sf_account_prefix': value => $sf_account_prefix; + 'DEFAULT/sf_api_port': value => $sf_api_port; + } +} diff --git a/cinder/manifests/volume/vmdk.pp b/cinder/manifests/volume/vmdk.pp new file mode 100644 index 000000000..c435e1dbd --- /dev/null +++ b/cinder/manifests/volume/vmdk.pp @@ -0,0 +1,82 @@ +# == define: cinder::volume::vmdk +# +# Configure the VMware VMDK driver for cinder. +# +# === Parameters +# +# [*host_ip*] +# The IP address of the VMware vCenter server. +# +# [*host_username*] +# The username for connection to VMware vCenter server. +# +# [*host_password*] +# The password for connection to VMware vCenter server. +# +# [*api_retry_count*] +# (optional) The number of times we retry on failures, +# e.g., socket error, etc. +# Defaults to 10. +# +# [*max_object_retrieval*] +# (optional) The maximum number of ObjectContent data objects that should +# be returned in a single result. A positive value will cause +# the operation to suspend the retrieval when the count of +# objects reaches the specified maximum. The server may still +# limit the count to something less than the configured value. +# Any remaining objects may be retrieved with additional requests. +# Defaults to 100. +# +# [*task_poll_interval*] +# (optional) The interval in seconds used for polling of remote tasks. +# Defaults to 5. +# +# [*image_transfer_timeout_secs*] +# (optional) The timeout in seconds for VMDK volume transfer between Cinder and Glance. +# Defaults to 7200. +# +# [*wsdl_location*] +# (optional) VIM Service WSDL Location e.g +# http:///vimService.wsdl. Optional over-ride to +# default location for bug work-arounds. +# Defaults to None. +# +# [*volume_folder*] +# (optional) The name for the folder in the VC datacenter that will contain cinder volumes. +# Defaults to 'cinder-volumes'. +# + +class cinder::volume::vmdk( + $host_ip, + $host_username, + $host_password, + $volume_folder = 'cinder-volumes', + $api_retry_count = 10, + $max_object_retrieval = 100, + $task_poll_interval = 5, + $image_transfer_timeout_secs = 7200, + $wsdl_location = undef +) { + + cinder_config { + 'DEFAULT/volume_driver': value => 'cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver'; + 'DEFAULT/vmware_host_ip': value => $host_ip; + 'DEFAULT/vmware_host_username': value => $host_username; + 'DEFAULT/vmware_host_password': value => $host_password; + 'DEFAULT/vmware_volume_folder': value => $volume_folder; + 'DEFAULT/vmware_api_retry_count': value => $api_retry_count; + 'DEFAULT/vmware_max_object_retrieval': value => $max_object_retrieval; + 'DEFAULT/vmware_task_poll_interval': value => $task_poll_interval; + 'DEFAULT/vmware_image_transfer_timeout_secs': value => $image_transfer_timeout_secs; + } + + if $wsdl_location { + cinder_config { + 'DEFAULT/vmware_wsdl_location': value => $wsdl_location; + } + } + + package{ 'python-suds': + ensure => present, + } +} diff --git a/cinder/spec/classes/cinder_api_spec.rb b/cinder/spec/classes/cinder_api_spec.rb new file mode 100644 index 000000000..9b00328b0 --- /dev/null +++ b/cinder/spec/classes/cinder_api_spec.rb @@ -0,0 +1,155 @@ +require 'spec_helper' + +describe 'cinder::api' do + + let :req_params do + {:keystone_password => 'foo'} + end + let :facts do + {:osfamily => 'Debian'} + end + + describe 'with only required params' do + let :params do + req_params + end + + it { should contain_service('cinder-api').with( + 'hasstatus' => true + )} + + it 'should configure cinder api correctly' do + should contain_cinder_config('DEFAULT/auth_strategy').with( + :value => 'keystone' + ) + should contain_cinder_config('DEFAULT/osapi_volume_listen').with( + :value => '0.0.0.0' + ) + should contain_cinder_api_paste_ini('filter:authtoken/service_protocol').with( + :value => 'http' + ) + should contain_cinder_api_paste_ini('filter:authtoken/service_host').with( + :value => 'localhost' + ) + should contain_cinder_api_paste_ini('filter:authtoken/service_port').with( + :value => '5000' + ) + should contain_cinder_api_paste_ini('filter:authtoken/auth_protocol').with( + :value => 'http' + ) + should contain_cinder_api_paste_ini('filter:authtoken/auth_host').with( + :value => 'localhost' + ) + should contain_cinder_api_paste_ini('filter:authtoken/auth_port').with( + :value => '35357' + ) + should contain_cinder_api_paste_ini('filter:authtoken/auth_admin_prefix').with( + :ensure => 'absent' + ) + should contain_cinder_api_paste_ini('filter:authtoken/admin_tenant_name').with( + :value => 'services' + ) + should contain_cinder_api_paste_ini('filter:authtoken/admin_user').with( + :value => 'cinder' + ) + should contain_cinder_api_paste_ini('filter:authtoken/admin_password').with( + :value => 'foo', + :secret => true + ) + + should contain_cinder_api_paste_ini('filter:authtoken/auth_uri').with( + :value => 'http://localhost:5000/' + ) + + end + end + + describe 'with custom auth_uri' do + let :params do + req_params.merge({'keystone_auth_uri' => 'http://foo.bar:8080/v2.0/'}) + end + it 'should configure cinder auth_uri correctly' do + should contain_cinder_api_paste_ini('filter:authtoken/auth_uri').with( + :value => 'http://foo.bar:8080/v2.0/' + ) + end + end + + describe 'with only required params' do + let :params do + req_params.merge({'bind_host' => '192.168.1.3'}) + end + it 'should configure cinder api correctly' do + should contain_cinder_config('DEFAULT/osapi_volume_listen').with( + :value => '192.168.1.3' + ) + should contain_cinder_config('DEFAULT/default_volume_type').with_ensure('absent') + end + end + + [ '/keystone', '/keystone/admin', '' ].each do |keystone_auth_admin_prefix| + describe "with keystone_auth_admin_prefix containing incorrect value #{keystone_auth_admin_prefix}" do + let :params do + { + :keystone_auth_admin_prefix => keystone_auth_admin_prefix, + :keystone_password => 'dummy' + } + end + + it { should contain_cinder_api_paste_ini('filter:authtoken/auth_admin_prefix').with( + :value => keystone_auth_admin_prefix + )} + end + end + + [ + '/keystone/', + 'keystone/', + 'keystone', + '/keystone/admin/', + 'keystone/admin/', + 'keystone/admin' + ].each do |keystone_auth_admin_prefix| + describe "with keystone_auth_admin_prefix containing incorrect value #{keystone_auth_admin_prefix}" do + let :params do + { + :keystone_auth_admin_prefix => keystone_auth_admin_prefix, + :keystone_password => 'dummy' + } + end + + it { expect { should contain_cinder_api_paste_ini('filter:authtoken/auth_admin_prefix') }.to \ + raise_error(Puppet::Error, /validate_re\(\): "#{keystone_auth_admin_prefix}" does not match/) } + end + end + + describe 'with enabled false' do + let :params do + req_params.merge({'enabled' => false}) + end + it 'should contain db_sync exec' do + should_not contain_exec('cinder-manage db_sync') + end + end + + describe 'with ratelimits' do + let :params do + req_params.merge({ :ratelimits => '(GET, "*", .*, 100, MINUTE);(POST, "*", .*, 200, MINUTE)' }) + end + + it { should contain_cinder_api_paste_ini('filter:ratelimit/limits').with( + :value => '(GET, "*", .*, 100, MINUTE);(POST, "*", .*, 200, MINUTE)' + )} + end + + describe 'with default volume type' do + let :params do + req_params.merge({ :default_volume_type => 'LVM_iSCSI' }) + end + + it { should contain_cinder_config('DEFAULT/default_volume_type').with( + :value => 'LVM_iSCSI' + )} + end + +end diff --git a/cinder/spec/classes/cinder_backup_ceph_spec.rb b/cinder/spec/classes/cinder_backup_ceph_spec.rb new file mode 100644 index 000000000..bedd7cdc5 --- /dev/null +++ b/cinder/spec/classes/cinder_backup_ceph_spec.rb @@ -0,0 +1,89 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# Unit tests for cinder::ceph class +# + +require 'spec_helper' + +describe 'cinder::backup::ceph' do + + let :default_params do + { :backup_ceph_conf => '/etc/ceph/ceph.conf', + :backup_ceph_user => 'cinder', + :backup_ceph_chunk_size => '134217728', + :backup_ceph_pool => 'backups', + :backup_ceph_stripe_unit => '0', + :backup_ceph_stripe_count => '0' } + end + + let :params do + {} + end + + shared_examples_for 'cinder backup with ceph' do + let :p do + default_params.merge(params) + end + + it 'configures cinder.conf' do + should contain_cinder_config('DEFAULT/backup_driver').with_value('cinder.backup.driver.ceph') + should contain_cinder_config('DEFAULT/backup_ceph_conf').with_value(p[:backup_ceph_conf]) + should contain_cinder_config('DEFAULT/backup_ceph_user').with_value(p[:backup_ceph_user]) + should contain_cinder_config('DEFAULT/backup_ceph_chunk_size').with_value(p[:backup_ceph_chunk_size]) + should contain_cinder_config('DEFAULT/backup_ceph_pool').with_value(p[:backup_ceph_pool]) + should contain_cinder_config('DEFAULT/backup_ceph_stripe_unit').with_value(p[:backup_ceph_stripe_unit]) + should contain_cinder_config('DEFAULT/backup_ceph_stripe_count').with_value(p[:backup_ceph_stripe_count]) + end + + context 'when overriding default parameters' do + before :each do + params.merge!(:backup_ceph_conf => '/tmp/ceph.conf') + params.merge!(:backup_ceph_user => 'toto') + params.merge!(:backup_ceph_chunk_size => '123') + params.merge!(:backup_ceph_pool => 'foo') + params.merge!(:backup_ceph_stripe_unit => '56') + params.merge!(:backup_ceph_stripe_count => '67') + end + it 'should replace default parameters with new values' do + should contain_cinder_config('DEFAULT/backup_ceph_conf').with_value(p[:backup_ceph_conf]) + should contain_cinder_config('DEFAULT/backup_ceph_user').with_value(p[:backup_ceph_user]) + should contain_cinder_config('DEFAULT/backup_ceph_chunk_size').with_value(p[:backup_ceph_chunk_size]) + should contain_cinder_config('DEFAULT/backup_ceph_pool').with_value(p[:backup_ceph_pool]) + should contain_cinder_config('DEFAULT/backup_ceph_stripe_unit').with_value(p[:backup_ceph_stripe_unit]) + should contain_cinder_config('DEFAULT/backup_ceph_stripe_count').with_value(p[:backup_ceph_stripe_count]) + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'cinder backup with ceph' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'cinder backup with ceph' + end + +end diff --git a/cinder/spec/classes/cinder_backup_spec.rb b/cinder/spec/classes/cinder_backup_spec.rb new file mode 100644 index 000000000..d3e571765 --- /dev/null +++ b/cinder/spec/classes/cinder_backup_spec.rb @@ -0,0 +1,101 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# Unit tests for cinder::backup class +# + +require 'spec_helper' + +describe 'cinder::backup' do + + let :default_params do + { :enable => true, + :backup_topic => 'cinder-backup', + :backup_manager => 'cinder.backup.manager.BackupManager', + :backup_api_class => 'cinder.backup.api.API', + :backup_name_template => 'backup-%s' } + end + + let :params do + {} + end + + shared_examples_for 'cinder backup' do + let :p do + default_params.merge(params) + end + + it { should include_class('cinder::params') } + + it 'installs cinder backup package' do + if platform_params.has_key?(:backup_package) + should contain_package('cinder-backup').with( + :name => platform_params[:backup_package], + :ensure => 'present' + ) + should contain_package('cinder-backup').with_before(/Cinder_config\[.+\]/) + should contain_package('cinder-backup').with_before(/Service\[cinder-backup\]/) + end + end + + it 'ensure cinder backup service is running' do + should contain_service('cinder-backup').with('hasstatus' => true) + end + + it 'configures cinder.conf' do + should contain_cinder_config('DEFAULT/backup_topic').with_value(p[:backup_topic]) + should contain_cinder_config('DEFAULT/backup_manager').with_value(p[:backup_manager]) + should contain_cinder_config('DEFAULT/backup_api_class').with_value(p[:backup_api_class]) + should contain_cinder_config('DEFAULT/backup_name_template').with_value(p[:backup_name_template]) + end + + context 'when overriding backup_name_template' do + before :each do + params.merge!(:backup_name_template => 'foo-bar-%s') + end + it 'should replace default parameter with new value' do + should contain_cinder_config('DEFAULT/backup_name_template').with_value(p[:backup_name_template]) + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + let :platform_params do + { :backup_package => 'cinder-backup', + :backup_service => 'cinder-backup' } + end + + it_configures 'cinder backup' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :platform_params do + { :backup_service => 'cinder-backup' } + end + + it_configures 'cinder backup' + end + +end diff --git a/cinder/spec/classes/cinder_backup_swift_spec.rb b/cinder/spec/classes/cinder_backup_swift_spec.rb new file mode 100644 index 000000000..dae1bd61d --- /dev/null +++ b/cinder/spec/classes/cinder_backup_swift_spec.rb @@ -0,0 +1,85 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# Unit tests for cinder::backup::swift class +# + +require 'spec_helper' + +describe 'cinder::backup::swift' do + + let :default_params do + { :backup_swift_url => 'http://localhost:8080/v1/AUTH_', + :backup_swift_container => 'volumes_backup', + :backup_swift_object_size => '52428800', + :backup_swift_retry_attempts => '3', + :backup_swift_retry_backoff => '2' } + end + + let :params do + {} + end + + shared_examples_for 'cinder backup with swift' do + let :p do + default_params.merge(params) + end + + it 'configures cinder.conf' do + should contain_cinder_config('DEFAULT/backup_driver').with_value('cinder.backup.drivers.swift') + should contain_cinder_config('DEFAULT/backup_swift_url').with_value(p[:backup_swift_url]) + should contain_cinder_config('DEFAULT/backup_swift_container').with_value(p[:backup_swift_container]) + should contain_cinder_config('DEFAULT/backup_swift_object_size').with_value(p[:backup_swift_object_size]) + should contain_cinder_config('DEFAULT/backup_swift_retry_attempts').with_value(p[:backup_swift_retry_attempts]) + should contain_cinder_config('DEFAULT/backup_swift_retry_backoff').with_value(p[:backup_swift_retry_backoff]) + end + + context 'when overriding default parameters' do + before :each do + params.merge!(:backup_swift_url => 'https://controller2:8080/v1/AUTH_') + params.merge!(:backup_swift_container => 'toto') + params.merge!(:backup_swift_object_size => '123') + params.merge!(:backup_swift_retry_attempts => '99') + params.merge!(:backup_swift_retry_backoff => '56') + end + it 'should replace default parameters with new values' do + should contain_cinder_config('DEFAULT/backup_swift_url').with_value(p[:backup_swift_url]) + should contain_cinder_config('DEFAULT/backup_swift_container').with_value(p[:backup_swift_container]) + should contain_cinder_config('DEFAULT/backup_swift_object_size').with_value(p[:backup_swift_object_size]) + should contain_cinder_config('DEFAULT/backup_swift_retry_attempts').with_value(p[:backup_swift_retry_attempts]) + should contain_cinder_config('DEFAULT/backup_swift_retry_backoff').with_value(p[:backup_swift_retry_backoff]) + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'cinder backup with swift' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'cinder backup with swift' + end + +end diff --git a/cinder/spec/classes/cinder_ceilometer_spec.rb b/cinder/spec/classes/cinder_ceilometer_spec.rb new file mode 100644 index 000000000..e1b52a10d --- /dev/null +++ b/cinder/spec/classes/cinder_ceilometer_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' + +describe 'cinder::ceilometer' do + + describe 'with default parameters' do + it 'contains default values' do + should contain_cinder_config('DEFAULT/notification_driver').with( + :value => 'cinder.openstack.common.notifier.rpc_notifier') + end + end +end diff --git a/cinder/spec/classes/cinder_client_spec.rb b/cinder/spec/classes/cinder_client_spec.rb new file mode 100644 index 000000000..77b51ced8 --- /dev/null +++ b/cinder/spec/classes/cinder_client_spec.rb @@ -0,0 +1,14 @@ +require 'spec_helper' + +describe 'cinder::client' do + it { should contain_package('python-cinderclient').with_ensure('present') } + let :facts do + {:osfamily => 'Debian'} + end + context 'with params' do + let :params do + {:package_ensure => 'latest'} + end + it { should contain_package('python-cinderclient').with_ensure('latest') } + end +end diff --git a/cinder/spec/classes/cinder_db_mysql_spec.rb b/cinder/spec/classes/cinder_db_mysql_spec.rb new file mode 100644 index 000000000..a35b16c7e --- /dev/null +++ b/cinder/spec/classes/cinder_db_mysql_spec.rb @@ -0,0 +1,76 @@ +require 'spec_helper' + +describe 'cinder::db::mysql' do + + let :req_params do + {:password => 'pw'} + end + + let :facts do + {:osfamily => 'Debian'} + end + + let :pre_condition do + 'include mysql::server' + end + + describe 'with only required params' do + let :params do + req_params + end + it { should contain_mysql__db('cinder').with( + :user => 'cinder', + :password => 'pw', + :host => '127.0.0.1', + :charset => 'latin1' + ) } + end + describe "overriding allowed_hosts param to array" do + let :params do + { + :password => 'cinderpass', + :allowed_hosts => ['127.0.0.1','%'] + } + end + + it {should_not contain_cinder__db__mysql__host_access("127.0.0.1").with( + :user => 'cinder', + :password => 'cinderpass', + :database => 'cinder' + )} + it {should contain_cinder__db__mysql__host_access("%").with( + :user => 'cinder', + :password => 'cinderpass', + :database => 'cinder' + )} + end + describe "overriding allowed_hosts param to string" do + let :params do + { + :password => 'cinderpass2', + :allowed_hosts => '192.168.1.1' + } + end + + it {should contain_cinder__db__mysql__host_access("192.168.1.1").with( + :user => 'cinder', + :password => 'cinderpass2', + :database => 'cinder' + )} + end + + describe "overriding allowed_hosts param equals to host param " do + let :params do + { + :password => 'cinderpass2', + :allowed_hosts => '127.0.0.1' + } + end + + it {should_not contain_cinder__db__mysql__host_access("127.0.0.1").with( + :user => 'cinder', + :password => 'cinderpass2', + :database => 'cinder' + )} + end +end diff --git a/cinder/spec/classes/cinder_db_postgresql_spec.rb b/cinder/spec/classes/cinder_db_postgresql_spec.rb new file mode 100644 index 000000000..93296ae1d --- /dev/null +++ b/cinder/spec/classes/cinder_db_postgresql_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe 'cinder::db::postgresql' do + + let :req_params do + {:password => 'pw'} + end + + let :facts do + { + :postgres_default_version => '8.4', + :osfamily => 'RedHat', + } + end + + describe 'with only required params' do + let :params do + req_params + end + it { should contain_postgresql__db('cinder').with( + :user => 'cinder', + :password => 'pw' + ) } + end + +end diff --git a/cinder/spec/classes/cinder_db_sync_spec.rb b/cinder/spec/classes/cinder_db_sync_spec.rb new file mode 100644 index 000000000..906d4391a --- /dev/null +++ b/cinder/spec/classes/cinder_db_sync_spec.rb @@ -0,0 +1,16 @@ +require 'spec_helper' + +describe 'cinder::db::sync' do + + let :facts do + {:osfamily => 'Debian'} + end + it { should contain_exec('cinder-manage db_sync').with( + :command => 'cinder-manage db sync', + :path => '/usr/bin', + :user => 'cinder', + :refreshonly => true, + :logoutput => 'on_failure' + ) } + +end diff --git a/cinder/spec/classes/cinder_glance_spec.rb b/cinder/spec/classes/cinder_glance_spec.rb new file mode 100644 index 000000000..64bf89a52 --- /dev/null +++ b/cinder/spec/classes/cinder_glance_spec.rb @@ -0,0 +1,82 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# Unit tests for cinder::glance class +# + +require 'spec_helper' + +describe 'cinder::glance' do + + let :default_params do + { :glance_api_version => '2', + :glance_num_retries => '0', + :glance_api_insecure => false, + :glance_api_ssl_compression => false } + end + + let :params do + {} + end + + shared_examples_for 'cinder with glance' do + let :p do + default_params.merge(params) + end + + it 'configures cinder.conf with default params' do + should contain_cinder_config('DEFAULT/glance_api_version').with_value(p[:glance_api_version]) + should contain_cinder_config('DEFAULT/glance_num_retries').with_value(p[:glance_num_retries]) + should contain_cinder_config('DEFAULT/glance_api_insecure').with_value(p[:glance_api_insecure]) + end + + context 'configure cinder with one glance server' do + before :each do + params.merge!(:glance_api_servers => '10.0.0.1:9292') + end + it 'should configure one glance server' do + should contain_cinder_config('DEFAULT/glance_api_servers').with_value(p[:glance_api_servers]) + end + end + + context 'configure cinder with two glance servers' do + before :each do + params.merge!(:glance_api_servers => ['10.0.0.1:9292','10.0.0.2:9292']) + end + it 'should configure two glance servers' do + should contain_cinder_config('DEFAULT/glance_api_servers').with_value(p[:glance_api_servers].join(',')) + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'cinder with glance' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'cinder with glance' + end + +end diff --git a/cinder/spec/classes/cinder_keystone_auth_spec.rb b/cinder/spec/classes/cinder_keystone_auth_spec.rb new file mode 100644 index 000000000..c78f7f681 --- /dev/null +++ b/cinder/spec/classes/cinder_keystone_auth_spec.rb @@ -0,0 +1,75 @@ +require 'spec_helper' + +describe 'cinder::keystone::auth' do + + let :req_params do + {:password => 'pw'} + end + + describe 'with only required params' do + + let :params do + req_params + end + + it 'should contain auth info' do + + should contain_keystone_user('cinder').with( + :ensure => 'present', + :password => 'pw', + :email => 'cinder@localhost', + :tenant => 'services' + ) + should contain_keystone_user_role('cinder@services').with( + :ensure => 'present', + :roles => 'admin' + ) + should contain_keystone_service('cinder').with( + :ensure => 'present', + :type => 'volume', + :description => 'Cinder Service' + ) + + end + it { should contain_keystone_endpoint('RegionOne/cinder').with( + :ensure => 'present', + :public_url => 'http://127.0.0.1:8776/v1/%(tenant_id)s', + :admin_url => 'http://127.0.0.1:8776/v1/%(tenant_id)s', + :internal_url => 'http://127.0.0.1:8776/v1/%(tenant_id)s' + ) } + + end + + context 'when overriding endpoint params' do + let :params do + req_params.merge( + :public_address => '10.0.42.1', + :admin_address => '10.0.42.2', + :internal_address => '10.0.42.3', + :region => 'RegionThree', + :port => '4242', + :admin_protocol => 'https', + :internal_protocol => 'https', + :public_protocol => 'https', + :volume_version => 'v42' + ) + end + + it { should contain_keystone_endpoint('RegionThree/cinder').with( + :ensure => 'present', + :public_url => 'https://10.0.42.1:4242/v42/%(tenant_id)s', + :admin_url => 'https://10.0.42.2:4242/v42/%(tenant_id)s', + :internal_url => 'https://10.0.42.3:4242/v42/%(tenant_id)s' + )} + + end + + + describe 'when endpoint should not be configured' do + let :params do + req_params.merge(:configure_endpoint => false) + end + it { should_not contain_keystone_endpoint('RegionOne/cinder') } + end + +end diff --git a/cinder/spec/classes/cinder_params_spec.rb b/cinder/spec/classes/cinder_params_spec.rb new file mode 100644 index 000000000..306ce1811 --- /dev/null +++ b/cinder/spec/classes/cinder_params_spec.rb @@ -0,0 +1,12 @@ +require 'spec_helper' + +describe 'cinder::params' do + + let :facts do + {:osfamily => 'Debian'} + end + it 'should compile' do + subject + end + +end diff --git a/cinder/spec/classes/cinder_qpid_spec.rb b/cinder/spec/classes/cinder_qpid_spec.rb new file mode 100644 index 000000000..0ace32090 --- /dev/null +++ b/cinder/spec/classes/cinder_qpid_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe 'cinder::qpid' do + + let :facts do + {:puppetversion => '2.7', + :osfamily => 'RedHat'} + end + + describe 'with defaults' do + + it 'should contain all of the default resources' do + + should contain_class('qpid::server').with( + :service_ensure => 'running', + :port => '5672' + ) + + end + + it 'should contain user' do + + should contain_qpid_user('guest').with( + :password => 'guest', + :file => '/var/lib/qpidd/qpidd.sasldb', + :realm => 'OPENSTACK', + :provider => 'saslpasswd2' + ) + + end + + end + + describe 'when disabled' do + let :params do + { + :enabled => false + } + end + + it 'should be disabled' do + + should_not contain_qpid_user('guest') + should contain_class('qpid::server').with( + :service_ensure => 'stopped' + ) + + end + end + +end diff --git a/cinder/spec/classes/cinder_quota_spec.rb b/cinder/spec/classes/cinder_quota_spec.rb new file mode 100644 index 000000000..ecbc2d97b --- /dev/null +++ b/cinder/spec/classes/cinder_quota_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe 'cinder::quota' do + + describe 'with default parameters' do + it 'contains default values' do + should contain_cinder_config('DEFAULT/quota_volumes').with( + :value => 10) + should contain_cinder_config('DEFAULT/quota_snapshots').with( + :value => 10) + should contain_cinder_config('DEFAULT/quota_gigabytes').with( + :value => 1000) + should contain_cinder_config('DEFAULT/quota_driver').with( + :value => 'cinder.quota.DbQuotaDriver') + end + end + + describe 'with overridden parameters' do + let :params do + { :quota_volumes => 1000, + :quota_snapshots => 1000, + :quota_gigabytes => 100000 } + end + it 'contains overrided values' do + should contain_cinder_config('DEFAULT/quota_volumes').with( + :value => 1000) + should contain_cinder_config('DEFAULT/quota_snapshots').with( + :value => 1000) + should contain_cinder_config('DEFAULT/quota_gigabytes').with( + :value => 100000) + should contain_cinder_config('DEFAULT/quota_driver').with( + :value => 'cinder.quota.DbQuotaDriver') + end + end +end diff --git a/cinder/spec/classes/cinder_rabbitmq_spec.rb b/cinder/spec/classes/cinder_rabbitmq_spec.rb new file mode 100644 index 000000000..3fcfd7a0a --- /dev/null +++ b/cinder/spec/classes/cinder_rabbitmq_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +describe 'cinder::rabbitmq' do + + let :facts do + { :puppetversion => '2.7', + :osfamily => 'Debian', + } + end + + describe 'with defaults' do + + it 'should contain all of the default resources' do + + should contain_class('rabbitmq::server').with( + :service_ensure => 'running', + :port => '5672', + :delete_guest_user => false + ) + + should contain_rabbitmq_vhost('/').with( + :provider => 'rabbitmqctl' + ) + end + + end + + describe 'when a rabbitmq user is specified' do + + let :params do + { + :userid => 'dan', + :password => 'pass' + } + end + + it 'should contain user and permissions' do + + should contain_rabbitmq_user('dan').with( + :admin => true, + :password => 'pass', + :provider => 'rabbitmqctl' + ) + + should contain_rabbitmq_user_permissions('dan@/').with( + :configure_permission => '.*', + :write_permission => '.*', + :read_permission => '.*', + :provider => 'rabbitmqctl' + ) + + end + + end + + describe 'when disabled' do + let :params do + { + :userid => 'dan', + :password => 'pass', + :enabled => false + } + end + + it 'should be disabled' do + + should_not contain_rabbitmq_user('dan') + should_not contain_rabbitmq_user_permissions('dan@/') + should contain_class('rabbitmq::server').with( + :service_ensure => 'stopped', + :port => '5672', + :delete_guest_user => false + ) + + should_not contain_rabbitmq_vhost('/') + + end + end + + +end diff --git a/cinder/spec/classes/cinder_scheduler_spec.rb b/cinder/spec/classes/cinder_scheduler_spec.rb new file mode 100644 index 000000000..45222190a --- /dev/null +++ b/cinder/spec/classes/cinder_scheduler_spec.rb @@ -0,0 +1,71 @@ +require 'spec_helper' + +describe 'cinder::scheduler' do + + describe 'on debian plateforms' do + + let :facts do + { :osfamily => 'Debian' } + end + + describe 'with default parameters' do + + it { should include_class('cinder::params') } + + it { should contain_package('cinder-scheduler').with( + :name => 'cinder-scheduler', + :ensure => 'present', + :before => 'Service[cinder-scheduler]' + ) } + + it { should contain_service('cinder-scheduler').with( + :name => 'cinder-scheduler', + :enable => true, + :ensure => 'running', + :require => 'Package[cinder]', + :hasstatus => true + ) } + end + + describe 'with parameters' do + + let :params do + { :scheduler_driver => 'cinder.scheduler.filter_scheduler.FilterScheduler', + :package_ensure => 'present' + } + end + + it { should contain_cinder_config('DEFAULT/scheduler_driver').with_value('cinder.scheduler.filter_scheduler.FilterScheduler') } + it { should contain_package('cinder-scheduler').with_ensure('present') } + end + end + + + describe 'on rhel plateforms' do + + let :facts do + { :osfamily => 'RedHat' } + end + + describe 'with default parameters' do + + it { should include_class('cinder::params') } + + it { should contain_service('cinder-scheduler').with( + :name => 'openstack-cinder-scheduler', + :enable => true, + :ensure => 'running', + :require => 'Package[cinder]' + ) } + end + + describe 'with parameters' do + + let :params do + { :scheduler_driver => 'cinder.scheduler.filter_scheduler.FilterScheduler' } + end + + it { should contain_cinder_config('DEFAULT/scheduler_driver').with_value('cinder.scheduler.filter_scheduler.FilterScheduler') } + end + end +end diff --git a/cinder/spec/classes/cinder_setup_test_volume_spec.rb b/cinder/spec/classes/cinder_setup_test_volume_spec.rb new file mode 100644 index 000000000..42209548a --- /dev/null +++ b/cinder/spec/classes/cinder_setup_test_volume_spec.rb @@ -0,0 +1,15 @@ +require 'spec_helper' + +describe 'cinder::setup_test_volume' do + + it { should contain_package('lvm2').with( + :ensure => 'present' + ) } + + it 'should contain volume creation execs' do + should contain_exec('/bin/dd if=/dev/zero of=cinder-volumes bs=1 count=0 seek=4G') + should contain_exec('/sbin/losetup /dev/loop2 cinder-volumes') + should contain_exec('/sbin/pvcreate /dev/loop2') + should contain_exec('/sbin/vgcreate cinder-volumes /dev/loop2') + end +end diff --git a/cinder/spec/classes/cinder_spec.rb b/cinder/spec/classes/cinder_spec.rb new file mode 100644 index 000000000..7cbe53cd5 --- /dev/null +++ b/cinder/spec/classes/cinder_spec.rb @@ -0,0 +1,179 @@ +require 'spec_helper' +describe 'cinder' do + let :req_params do + {:rabbit_password => 'guest', :sql_connection => 'mysql://user:password@host/database'} + end + + let :facts do + {:osfamily => 'Debian'} + end + + describe 'with only required params' do + let :params do + req_params + end + + it { should contain_class('cinder::params') } + + it 'should contain default config' do + should contain_cinder_config('DEFAULT/rpc_backend').with( + :value => 'cinder.openstack.common.rpc.impl_kombu' + ) + should contain_cinder_config('DEFAULT/control_exchange').with( + :value => 'openstack' + ) + should contain_cinder_config('DEFAULT/rabbit_password').with( + :value => 'guest', + :secret => true + ) + should contain_cinder_config('DEFAULT/rabbit_host').with( + :value => '127.0.0.1' + ) + should contain_cinder_config('DEFAULT/rabbit_port').with( + :value => '5672' + ) + should contain_cinder_config('DEFAULT/rabbit_hosts').with( + :value => '127.0.0.1:5672' + ) + should contain_cinder_config('DEFAULT/rabbit_ha_queues').with( + :value => false + ) + should contain_cinder_config('DEFAULT/rabbit_virtual_host').with( + :value => '/' + ) + should contain_cinder_config('DEFAULT/rabbit_userid').with( + :value => 'guest' + ) + should contain_cinder_config('DEFAULT/sql_connection').with( + :value => 'mysql://user:password@host/database', + :secret => true + ) + should contain_cinder_config('DEFAULT/sql_idle_timeout').with( + :value => '3600' + ) + should contain_cinder_config('DEFAULT/verbose').with( + :value => false + ) + should contain_cinder_config('DEFAULT/debug').with( + :value => false + ) + should contain_cinder_config('DEFAULT/api_paste_config').with( + :value => '/etc/cinder/api-paste.ini' + ) + should contain_cinder_config('DEFAULT/log_dir').with(:value => '/var/log/cinder') + end + + it { should contain_file('/etc/cinder/cinder.conf').with( + :owner => 'cinder', + :group => 'cinder', + :mode => '0600', + :require => 'Package[cinder]' + ) } + + it { should contain_file('/etc/cinder/api-paste.ini').with( + :owner => 'cinder', + :group => 'cinder', + :mode => '0600', + :require => 'Package[cinder]' + ) } + + end + describe 'with modified rabbit_hosts' do + let :params do + req_params.merge({'rabbit_hosts' => ['rabbit1:5672', 'rabbit2:5672']}) + end + + it 'should contain many' do + should_not contain_cinder_config('DEFAULT/rabbit_host') + should_not contain_cinder_config('DEFAULT/rabbit_port') + should contain_cinder_config('DEFAULT/rabbit_hosts').with( + :value => 'rabbit1:5672,rabbit2:5672' + ) + should contain_cinder_config('DEFAULT/rabbit_ha_queues').with( + :value => true + ) + end + end + + describe 'with a single rabbit_hosts entry' do + let :params do + req_params.merge({'rabbit_hosts' => ['rabbit1:5672']}) + end + + it 'should contain many' do + should_not contain_cinder_config('DEFAULT/rabbit_host') + should_not contain_cinder_config('DEFAULT/rabbit_port') + should contain_cinder_config('DEFAULT/rabbit_hosts').with( + :value => 'rabbit1:5672' + ) + should contain_cinder_config('DEFAULT/rabbit_ha_queues').with( + :value => true + ) + end + end + + describe 'with qpid rpc supplied' do + + let :params do + { + :sql_connection => 'mysql://user:password@host/database', + :qpid_password => 'guest', + :rpc_backend => 'cinder.openstack.common.rpc.impl_qpid' + } + end + + it { should contain_cinder_config('DEFAULT/sql_connection').with_value('mysql://user:password@host/database') } + it { should contain_cinder_config('DEFAULT/rpc_backend').with_value('cinder.openstack.common.rpc.impl_qpid') } + it { should contain_cinder_config('DEFAULT/qpid_hostname').with_value('localhost') } + it { should contain_cinder_config('DEFAULT/qpid_port').with_value('5672') } + it { should contain_cinder_config('DEFAULT/qpid_username').with_value('guest') } + it { should contain_cinder_config('DEFAULT/qpid_password').with_value('guest').with_secret(true) } + it { should contain_cinder_config('DEFAULT/qpid_reconnect').with_value(true) } + it { should contain_cinder_config('DEFAULT/qpid_reconnect_timeout').with_value('0') } + it { should contain_cinder_config('DEFAULT/qpid_reconnect_limit').with_value('0') } + it { should contain_cinder_config('DEFAULT/qpid_reconnect_interval_min').with_value('0') } + it { should contain_cinder_config('DEFAULT/qpid_reconnect_interval_max').with_value('0') } + it { should contain_cinder_config('DEFAULT/qpid_reconnect_interval').with_value('0') } + it { should contain_cinder_config('DEFAULT/qpid_heartbeat').with_value('60') } + it { should contain_cinder_config('DEFAULT/qpid_protocol').with_value('tcp') } + it { should contain_cinder_config('DEFAULT/qpid_tcp_nodelay').with_value(true) } + + end + + describe 'with syslog disabled' do + let :params do + req_params + end + + it { should contain_cinder_config('DEFAULT/use_syslog').with_value(false) } + end + + describe 'with syslog enabled' do + let :params do + req_params.merge({ + :use_syslog => 'true', + }) + end + + it { should contain_cinder_config('DEFAULT/use_syslog').with_value(true) } + it { should contain_cinder_config('DEFAULT/syslog_log_facility').with_value('LOG_USER') } + end + + describe 'with syslog enabled and custom settings' do + let :params do + req_params.merge({ + :use_syslog => 'true', + :log_facility => 'LOG_LOCAL0' + }) + end + + it { should contain_cinder_config('DEFAULT/use_syslog').with_value(true) } + it { should contain_cinder_config('DEFAULT/syslog_log_facility').with_value('LOG_LOCAL0') } + end + + describe 'with log_dir disabled' do + let(:params) { req_params.merge!({:log_dir => false}) } + it { should contain_cinder_config('DEFAULT/log_dir').with_ensure('absent') } + end + +end diff --git a/cinder/spec/classes/cinder_vmware_spec.rb b/cinder/spec/classes/cinder_vmware_spec.rb new file mode 100644 index 000000000..705613626 --- /dev/null +++ b/cinder/spec/classes/cinder_vmware_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe 'cinder::vmware' do + + let :params do + {:os_password => 'asdf', + :os_tenant_name => 'admin', + :os_username => 'admin', + :os_auth_url => 'http://127.127.127.1:5000/v2.0/'} + end + + describe 'with defaults' do + it 'should create vmware special types' do + should contain_cinder__type('vmware-thin').with( + :set_key => 'vmware:vmdk_type', + :set_value => 'thin') + + should contain_cinder__type('vmware-thick').with( + :set_key => 'vmware:vmdk_type', + :set_value => 'thick') + + should contain_cinder__type('vmware-eagerZeroedThick').with( + :set_key => 'vmware:vmdk_type', + :set_value => 'eagerZeroedThick') + + should contain_cinder__type('vmware-full').with( + :set_key => 'vmware:clone_type', + :set_value => 'full') + + should contain_cinder__type('vmware-linked').with( + :set_key => 'vmware:clone_type', + :set_value => 'linked') + end + end +end diff --git a/cinder/spec/classes/cinder_volume_glusterfs_spec.rb b/cinder/spec/classes/cinder_volume_glusterfs_spec.rb new file mode 100644 index 000000000..57e7ff2ac --- /dev/null +++ b/cinder/spec/classes/cinder_volume_glusterfs_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe 'cinder::volume::glusterfs' do + + let :params do + { + :glusterfs_shares => ['10.10.10.10:/volumes', '10.10.10.11:/volumes'], + :glusterfs_shares_config => '/etc/cinder/other_shares.conf', + :glusterfs_disk_util => 'du', + :glusterfs_sparsed_volumes => true, + :glusterfs_mount_point_base => '/cinder_mount_point', + } + end + + describe 'glusterfs volume driver' do + it 'configures glusterfs volume driver' do + should contain_cinder_config('DEFAULT/volume_driver').with_value( + 'cinder.volume.drivers.glusterfs.GlusterfsDriver') + should contain_cinder_config('DEFAULT/glusterfs_shares_config').with_value( + '/etc/cinder/other_shares.conf') + should contain_cinder_config('DEFAULT/glusterfs_sparsed_volumes').with_value( + true) + should contain_cinder_config('DEFAULT/glusterfs_mount_point_base').with_value( + '/cinder_mount_point') + should contain_cinder_config('DEFAULT/glusterfs_disk_util').with_value( + 'du') + should contain_file('/etc/cinder/other_shares.conf').with( + :content => "10.10.10.10:/volumes\n10.10.10.11:/volumes\n", + :require => 'Package[cinder]', + :notify => 'Service[cinder-volume]' + ) + end + end +end diff --git a/cinder/spec/classes/cinder_volume_iscsi_spec.rb b/cinder/spec/classes/cinder_volume_iscsi_spec.rb new file mode 100644 index 000000000..fe0a7f770 --- /dev/null +++ b/cinder/spec/classes/cinder_volume_iscsi_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe 'cinder::volume::iscsi' do + + let :req_params do + {:iscsi_ip_address => '127.0.0.2'} + end + + let :facts do + {:osfamily => 'Debian'} + end + + describe 'with default params' do + + let :params do + req_params + end + + it { should contain_cinder_config('DEFAULT/iscsi_ip_address').with( + :value => '127.0.0.2' + ) } + it { should contain_cinder_config('DEFAULT/iscsi_helper').with( + :value => 'tgtadm' + ) } + it { should contain_cinder_config('DEFAULT/volume_group').with( + :value => 'cinder-volumes' + ) } + + end + + describe 'with RedHat' do + + let :params do + req_params + end + + let :facts do + {:osfamily => 'RedHat'} + end + + it { should contain_file_line('cinder include').with( + :line => 'include /etc/cinder/volumes/*', + :path => '/etc/tgt/targets.conf' + ) } + + end + +end diff --git a/cinder/spec/classes/cinder_volume_netapp_spec.rb b/cinder/spec/classes/cinder_volume_netapp_spec.rb new file mode 100644 index 000000000..fde204ac2 --- /dev/null +++ b/cinder/spec/classes/cinder_volume_netapp_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe 'cinder::volume::netapp' do + + let :params do + { + :netapp_login => 'netapp', + :netapp_password => 'password', + :netapp_server_hostname => '127.0.0.2', + } + end + + let :default_params do + { + :netapp_server_port => '80', + :netapp_size_multiplier => '1.2', + :netapp_storage_family => 'ontap_cluster', + :netapp_storage_protocol => 'nfs', + :netapp_transport_type => 'http', + :netapp_vfiler => '', + :netapp_volume_list => '', + :netapp_vserver => '', + :expiry_thres_minutes => '720', + :thres_avl_size_perc_start => '20', + :thres_avl_size_perc_stop => '60', + } + end + + + shared_examples_for 'netapp volume driver' do + let :params_hash do + default_params.merge(params) + end + + it 'configures netapp volume driver' do + should contain_cinder_config('DEFAULT/volume_driver').with_value( + 'cinder.volume.drivers.netapp.common.NetAppDriver') + params_hash.each_pair do |config,value| + should contain_cinder_config("DEFAULT/#{config}").with_value( value ) + end + end + + it 'marks netapp_password as secret' do + should contain_cinder_config('DEFAULT/netapp_password').with_secret( true ) + end + end + + + context 'with default parameters' do + before do + params = {} + end + + it_configures 'netapp volume driver' + end + + context 'with provided parameters' do + it_configures 'netapp volume driver' + end +end diff --git a/cinder/spec/classes/cinder_volume_nexenta_spec.rb b/cinder/spec/classes/cinder_volume_nexenta_spec.rb new file mode 100644 index 000000000..422646c6a --- /dev/null +++ b/cinder/spec/classes/cinder_volume_nexenta_spec.rb @@ -0,0 +1,38 @@ +# author 'Aimon Bustardo ' +# license 'Apache License 2.0' +# description 'configures openstack cinder nexenta driver' +require 'spec_helper' + +describe 'cinder::volume::nexenta' do + + let :params do + { :nexenta_user => 'nexenta', + :nexenta_password => 'password', + :nexenta_host => '127.0.0.2' } + end + + let :default_params do + { :nexenta_volume => 'cinder', + :nexenta_target_prefix => 'iqn:', + :nexenta_target_group_prefix => 'cinder/', + :nexenta_blocksize => '8k', + :nexenta_sparse => true } + end + + let :facts do + { :osfamily => 'Debian' } + end + + + context 'with required params' do + let :params_hash do + default_params.merge(params) + end + + it 'configures nexenta volume driver' do + params_hash.each_pair do |config, value| + should contain_cinder_config("DEFAULT/#{config}").with_value(value) + end + end + end +end diff --git a/cinder/spec/classes/cinder_volume_nfs_spec.rb b/cinder/spec/classes/cinder_volume_nfs_spec.rb new file mode 100644 index 000000000..033ea2c48 --- /dev/null +++ b/cinder/spec/classes/cinder_volume_nfs_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe 'cinder::volume::nfs' do + + let :params do + { + :nfs_servers => ['10.10.10.10:/shares', '10.10.10.10:/shares2'], + :nfs_mount_options => 'vers=3', + :nfs_shares_config => '/etc/cinder/other_shares.conf', + :nfs_disk_util => 'du', + :nfs_sparsed_volumes => true, + :nfs_mount_point_base => '/cinder_mount_point', + } + end + + describe 'nfs volume driver' do + it 'configures nfs volume driver' do + should contain_cinder_config('DEFAULT/volume_driver').with_value( + 'cinder.volume.drivers.nfs.NfsDriver') + should contain_cinder_config('DEFAULT/nfs_shares_config').with_value( + '/etc/cinder/other_shares.conf') + should contain_cinder_config('DEFAULT/nfs_mount_options').with_value( + 'vers=3') + should contain_cinder_config('DEFAULT/nfs_sparsed_volumes').with_value( + true) + should contain_cinder_config('DEFAULT/nfs_mount_point_base').with_value( + '/cinder_mount_point') + should contain_cinder_config('DEFAULT/nfs_disk_util').with_value( + 'du') + should contain_file('/etc/cinder/other_shares.conf').with( + :content => "10.10.10.10:/shares\n10.10.10.10:/shares2", + :require => 'Package[cinder]', + :notify => 'Service[cinder-volume]' + ) + end + end +end diff --git a/cinder/spec/classes/cinder_volume_rbd_spec.rb b/cinder/spec/classes/cinder_volume_rbd_spec.rb new file mode 100644 index 000000000..ec3a92677 --- /dev/null +++ b/cinder/spec/classes/cinder_volume_rbd_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe 'cinder::volume::rbd' do + let :req_params do + { + :rbd_pool => 'volumes', + :glance_api_version => '2', + :rbd_user => 'test', + :rbd_secret_uuid => '0123456789', + :rbd_ceph_conf => '/foo/boo/zoo/ceph.conf', + :rbd_flatten_volume_from_snapshot => true, + :volume_tmp_dir => '/foo/tmp', + :rbd_max_clone_depth => '0' + } + end + + it { should contain_class('cinder::params') } + + let :params do + req_params + end + + let :facts do + {:osfamily => 'Debian'} + end + + describe 'rbd volume driver' do + it 'configure rbd volume driver' do + should contain_cinder_config('DEFAULT/volume_driver').with_value('cinder.volume.drivers.rbd.RBDDriver') + + should contain_cinder_config('DEFAULT/rbd_ceph_conf').with_value(req_params[:rbd_ceph_conf]) + should contain_cinder_config('DEFAULT/rbd_flatten_volume_from_snapshot').with_value(req_params[:rbd_flatten_volume_from_snapshot]) + should contain_cinder_config('DEFAULT/volume_tmp_dir').with_value(req_params[:volume_tmp_dir]) + should contain_cinder_config('DEFAULT/rbd_max_clone_depth').with_value(req_params[:rbd_max_clone_depth]) + should contain_cinder_config('DEFAULT/rbd_pool').with_value(req_params[:rbd_pool]) + should contain_cinder_config('DEFAULT/rbd_user').with_value(req_params[:rbd_user]) + should contain_cinder_config('DEFAULT/rbd_secret_uuid').with_value(req_params[:rbd_secret_uuid]) + should contain_file('/etc/init/cinder-volume.override').with(:ensure => 'present') + should contain_file_line('set initscript env').with( + :line => /env CEPH_ARGS=\"--id test\"/, + :path => '/etc/init/cinder-volume.override', + :notify => 'Service[cinder-volume]') + end + + context 'with rbd_secret_uuid disabled' do + let(:params) { req_params.merge!({:rbd_secret_uuid => false}) } + it { should contain_cinder_config('DEFAULT/rbd_secret_uuid').with_ensure('absent') } + end + + context 'with volume_tmp_dir disabled' do + let(:params) { req_params.merge!({:volume_tmp_dir => false}) } + it { should contain_cinder_config('DEFAULT/volume_tmp_dir').with_ensure('absent') } + end + + end + + + describe 'with RedHat' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :params do + req_params + end + + it 'should ensure that the cinder-volume sysconfig file is present' do + should contain_file('/etc/sysconfig/openstack-cinder-volume').with( + :ensure => 'present' + ) + end + + it 'should configure RedHat init override' do + should contain_file_line('set initscript env').with( + :line => /export CEPH_ARGS=\"--id test\"/, + :path => '/etc/sysconfig/openstack-cinder-volume', + :notify => 'Service[cinder-volume]') + end + end + +end + diff --git a/cinder/spec/classes/cinder_volume_san_spec.rb b/cinder/spec/classes/cinder_volume_san_spec.rb new file mode 100644 index 000000000..15c32843f --- /dev/null +++ b/cinder/spec/classes/cinder_volume_san_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'cinder::volume::san' do + + let :params do + { :volume_driver => 'cinder.volume.san.SolarisISCSIDriver', + :san_ip => '127.0.0.1', + :san_login => 'cluster_operator', + :san_password => '007', + :san_clustername => 'storage_cluster' } + end + + let :default_params do + { :san_thin_provision => true, + :san_login => 'admin', + :san_ssh_port => 22, + :san_is_local => false, + :ssh_conn_timeout => 30, + :ssh_min_pool_conn => 1, + :ssh_max_pool_conn => 5 } + end + + shared_examples_for 'a san volume driver' do + let :params_hash do + default_params.merge(params) + end + + it 'configures cinder volume driver' do + params_hash.each_pair do |config,value| + should contain_cinder_config("DEFAULT/#{config}").with_value( value ) + end + end + end + + + context 'with parameters' do + it_configures 'a san volume driver' + end +end diff --git a/cinder/spec/classes/cinder_volume_solidfire_spec.rb b/cinder/spec/classes/cinder_volume_solidfire_spec.rb new file mode 100644 index 000000000..ddafe0926 --- /dev/null +++ b/cinder/spec/classes/cinder_volume_solidfire_spec.rb @@ -0,0 +1,28 @@ +require 'spec_helper' + +describe 'cinder::volume::solidfire' do + let :req_params do + { + :san_ip => '127.0.0.2', + :san_login => 'solidfire', + :san_password => 'password', + } + end + + let :params do + req_params + end + + describe 'solidfire volume driver' do + it 'configure solidfire volume driver' do + should contain_cinder_config('DEFAULT/volume_driver').with_value( + 'cinder.volume.drivers.solidfire.SolidFire') + should contain_cinder_config('DEFAULT/san_ip').with_value( + '127.0.0.2') + should contain_cinder_config('DEFAULT/san_login').with_value( + 'solidfire') + should contain_cinder_config('DEFAULT/san_password').with_value( + 'password') + end + end +end diff --git a/cinder/spec/classes/cinder_volume_spec.rb b/cinder/spec/classes/cinder_volume_spec.rb new file mode 100644 index 000000000..725cfa34f --- /dev/null +++ b/cinder/spec/classes/cinder_volume_spec.rb @@ -0,0 +1,18 @@ +require 'spec_helper' + +describe 'cinder::volume' do + + let :pre_condition do + 'class { "cinder": rabbit_password => "fpp", sql_connection => "mysql://a:b@c/d" }' + end + + let :facts do + {:osfamily => 'Debian'} + end + + it { should contain_package('cinder-volume').with_ensure('present') } + it { should contain_service('cinder-volume').with( + 'hasstatus' => true + )} + +end diff --git a/cinder/spec/classes/cinder_volume_vmdk_spec.rb b/cinder/spec/classes/cinder_volume_vmdk_spec.rb new file mode 100644 index 000000000..0995797d4 --- /dev/null +++ b/cinder/spec/classes/cinder_volume_vmdk_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' + +describe 'cinder::volume::vmdk' do + + let :params do + { + :host_ip => '172.16.16.16', + :host_password => 'asdf', + :host_username => 'user' + } + end + + let :optional_params do + { + :volume_folder => 'cinder-volume-folder', + :api_retry_count => 5, + :max_object_retrieval => 200, + :task_poll_interval => 10, + :image_transfer_timeout_secs => 3600, + :wsdl_location => 'http://127.0.0.1:8080/vmware/SDK/wsdl/vim25/vimService.wsdl' + } + end + + it 'should configure vmdk driver in cinder.conf' do + should contain_cinder_config('DEFAULT/volume_driver').with_value('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver') + should contain_cinder_config('DEFAULT/vmware_host_ip').with_value(params[:host_ip]) + should contain_cinder_config('DEFAULT/vmware_host_username').with_value(params[:host_username]) + should contain_cinder_config('DEFAULT/vmware_host_password').with_value(params[:host_password]) + should contain_cinder_config('DEFAULT/vmware_volume_folder').with_value('cinder-volumes') + should contain_cinder_config('DEFAULT/vmware_api_retry_count').with_value(10) + should contain_cinder_config('DEFAULT/vmware_max_object_retrieval').with_value(100) + should contain_cinder_config('DEFAULT/vmware_task_poll_interval').with_value(5) + should contain_cinder_config('DEFAULT/vmware_image_transfer_timeout_secs').with_value(7200) + should_not contain_cinder_config('DEFAULT/vmware_wsdl_location') + end + + it 'installs vmdk python driver' do + should contain_package('python-suds').with( + :ensure => 'present' + ) + end + + context 'with optional parameters' do + before :each do + params.merge!(optional_params) + end + + it 'should configure vmdk driver in cinder.conf' do + should contain_cinder_config('DEFAULT/vmware_volume_folder').with_value(params[:volume_folder]) + should contain_cinder_config('DEFAULT/vmware_api_retry_count').with_value(params[:api_retry_count]) + should contain_cinder_config('DEFAULT/vmware_max_object_retrieval').with_value(params[:max_object_retrieval]) + should contain_cinder_config('DEFAULT/vmware_task_poll_interval').with_value(params[:task_poll_interval]) + should contain_cinder_config('DEFAULT/vmware_image_transfer_timeout_secs').with_value(params[:image_transfer_timeout_secs]) + should contain_cinder_config('DEFAULT/vmware_wsdl_location').with_value(params[:wsdl_location]) + end + end +end diff --git a/cinder/spec/defines/cinder_backend_iscsi_spec.rb b/cinder/spec/defines/cinder_backend_iscsi_spec.rb new file mode 100644 index 000000000..bf91bc1ec --- /dev/null +++ b/cinder/spec/defines/cinder_backend_iscsi_spec.rb @@ -0,0 +1,47 @@ +require 'spec_helper' + +describe 'cinder::backend::iscsi' do + + let(:title) {'hippo'} + + let :req_params do { + :iscsi_ip_address => '127.0.0.2', + } + end + + let :facts do + {:osfamily => 'Debian'} + end + + let :params do + req_params + end + + describe 'with default params' do + + it 'should configure iscsi driver' do + should contain_cinder_config('hippo/volume_backend_name').with( + :value => 'hippo') + should contain_cinder_config('hippo/iscsi_ip_address').with( + :value => '127.0.0.2') + should contain_cinder_config('hippo/iscsi_helper').with( + :value => 'tgtadm') + should contain_cinder_config('hippo/volume_group').with( + :value => 'cinder-volumes') + end + end + + describe 'with RedHat' do + + let :facts do + {:osfamily => 'RedHat'} + end + + it { should contain_file_line('cinder include').with( + :line => 'include /etc/cinder/volumes/*', + :path => '/etc/tgt/targets.conf' + ) } + + end + +end diff --git a/cinder/spec/defines/cinder_backend_netapp_spec.rb b/cinder/spec/defines/cinder_backend_netapp_spec.rb new file mode 100644 index 000000000..4a943b560 --- /dev/null +++ b/cinder/spec/defines/cinder_backend_netapp_spec.rb @@ -0,0 +1,64 @@ +require 'spec_helper' + +describe 'cinder::backend::netapp' do + + let(:title) {'hippo'} + + let :params do + { + :volume_backend_name => 'netapp-cdot-nfs', + :netapp_login => 'netapp', + :netapp_password => 'password', + :netapp_server_hostname => '127.0.0.2', + } + end + + let :default_params do + { + :netapp_server_port => '80', + :netapp_size_multiplier => '1.2', + :netapp_storage_family => 'ontap_cluster', + :netapp_storage_protocol => 'nfs', + :netapp_transport_type => 'http', + :netapp_vfiler => '', + :netapp_volume_list => '', + :netapp_vserver => '', + :expiry_thres_minutes => '720', + :thres_avl_size_perc_start => '20', + :thres_avl_size_perc_stop => '60', + :nfs_shares_config => '', + } + end + + shared_examples_for 'netapp volume driver' do + let :params_hash do + default_params.merge(params) + end + + it 'configures netapp volume driver' do + should contain_cinder_config("#{params_hash[:volume_backend_name]}/volume_driver").with_value( + 'cinder.volume.drivers.netapp.common.NetAppDriver') + params_hash.each_pair do |config,value| + should contain_cinder_config("#{params_hash[:volume_backend_name]}/#{config}").with_value( value ) + end + end + + it 'marks netapp_password as secret' do + should contain_cinder_config("#{params_hash[:volume_backend_name]}/netapp_password").with_secret( true ) + end + end + + + context 'with default parameters' do + before do + params = {} + end + + it_configures 'netapp volume driver' + end + + context 'with provided parameters' do + it_configures 'netapp volume driver' + end + +end diff --git a/cinder/spec/defines/cinder_backend_nfs_spec.rb b/cinder/spec/defines/cinder_backend_nfs_spec.rb new file mode 100644 index 000000000..a871cd4ec --- /dev/null +++ b/cinder/spec/defines/cinder_backend_nfs_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +describe 'cinder::backend::nfs' do + + let(:title) {'hippo'} + + let :params do + { + :nfs_servers => ['10.10.10.10:/shares', '10.10.10.10:/shares2'], + :nfs_mount_options => 'vers=3', + :nfs_shares_config => '/etc/cinder/other_shares.conf', + :nfs_disk_util => 'du', + :nfs_sparsed_volumes => true, + :nfs_mount_point_base => '/cinder_mount_point', + } + end + + describe 'nfs volume driver' do + + it 'configures nfs volume driver' do + should contain_cinder_config('hippo/volume_backend_name').with( + :value => 'hippo') + should contain_cinder_config('hippo/volume_driver').with_value( + 'cinder.volume.drivers.nfs.NfsDriver') + should contain_cinder_config('hippo/nfs_shares_config').with_value( + '/etc/cinder/other_shares.conf') + should contain_cinder_config('hippo/nfs_mount_options').with_value( + 'vers=3') + should contain_cinder_config('hippo/nfs_sparsed_volumes').with_value( + true) + should contain_cinder_config('hippo/nfs_mount_point_base').with_value( + '/cinder_mount_point') + should contain_cinder_config('hippo/nfs_disk_util').with_value( + 'du') + should contain_file('/etc/cinder/other_shares.conf').with( + :content => "10.10.10.10:/shares\n10.10.10.10:/shares2", + :require => 'Package[cinder]', + :notify => 'Service[cinder-volume]' + ) + end + end +end diff --git a/cinder/spec/defines/cinder_backend_rbd_spec.rb b/cinder/spec/defines/cinder_backend_rbd_spec.rb new file mode 100644 index 000000000..0636344c0 --- /dev/null +++ b/cinder/spec/defines/cinder_backend_rbd_spec.rb @@ -0,0 +1,98 @@ +require 'spec_helper' + +describe 'cinder::backend::rbd' do + + let(:title) {'rbd-ssd'} + + let :req_params do + { + :volume_backend_name => 'rbd-ssd', + :rbd_pool => 'volumes', + :glance_api_version => '2', + :rbd_user => 'test', + :rbd_secret_uuid => '0123456789', + :rbd_ceph_conf => '/foo/boo/zoo/ceph.conf', + :rbd_flatten_volume_from_snapshot => true, + :volume_tmp_dir => '/foo/tmp', + :rbd_max_clone_depth => '0' + } + end + + it { should contain_class('cinder::params') } + + let :params do + req_params + end + + let :facts do + {:osfamily => 'Debian'} + end + + describe 'rbd backend volume driver' do + it 'configure rbd volume driver' do + should contain_cinder_config("#{req_params[:volume_backend_name]}/volume_backend_name").with_value(req_params[:volume_backend_name]) + should contain_cinder_config("#{req_params[:volume_backend_name]}/volume_driver").with_value('cinder.volume.drivers.rbd.RBDDriver') + should contain_cinder_config("#{req_params[:volume_backend_name]}/rbd_ceph_conf").with_value(req_params[:rbd_ceph_conf]) + should contain_cinder_config("#{req_params[:volume_backend_name]}/rbd_flatten_volume_from_snapshot").with_value(req_params[:rbd_flatten_volume_from_snapshot]) + should contain_cinder_config("#{req_params[:volume_backend_name]}/volume_tmp_dir").with_value(req_params[:volume_tmp_dir]) + should contain_cinder_config("#{req_params[:volume_backend_name]}/rbd_max_clone_depth").with_value(req_params[:rbd_max_clone_depth]) + should contain_cinder_config("#{req_params[:volume_backend_name]}/rbd_pool").with_value(req_params[:rbd_pool]) + should contain_cinder_config("#{req_params[:volume_backend_name]}/rbd_user").with_value(req_params[:rbd_user]) + should contain_cinder_config("#{req_params[:volume_backend_name]}/rbd_secret_uuid").with_value(req_params[:rbd_secret_uuid]) + should contain_file('/etc/init/cinder-volume.override').with(:ensure => 'present') + should contain_file_line('set initscript env').with( + :line => /env CEPH_ARGS=\"--id test\"/, + :path => '/etc/init/cinder-volume.override', + :notify => 'Service[cinder-volume]') + end + + context 'with rbd_secret_uuid disabled' do + let(:params) { req_params.merge!({:rbd_secret_uuid => false}) } + it { should contain_cinder_config("#{req_params[:volume_backend_name]}/rbd_secret_uuid").with_ensure('absent') } + end + + context 'with volume_tmp_dir disabled' do + let(:params) { req_params.merge!({:volume_tmp_dir => false}) } + it { should contain_cinder_config("#{req_params[:volume_backend_name]}/volume_tmp_dir").with_ensure('absent') } + end + + context 'with another RBD backend' do + let :pre_condition do + "cinder::backend::rbd { 'ceph2': + rbd_pool => 'volumes2', + rbd_user => 'test' + }" + end + it { should contain_cinder_config("#{req_params[:volume_backend_name]}/volume_driver").with_value('cinder.volume.drivers.rbd.RBDDriver') } + it { should contain_cinder_config("#{req_params[:volume_backend_name]}/rbd_pool").with_value(req_params[:rbd_pool]) } + it { should contain_cinder_config("#{req_params[:volume_backend_name]}/rbd_user").with_value(req_params[:rbd_user]) } + it { should contain_cinder_config("ceph2/volume_driver").with_value('cinder.volume.drivers.rbd.RBDDriver') } + it { should contain_cinder_config("ceph2/rbd_pool").with_value('volumes2') } + it { should contain_cinder_config("ceph2/rbd_user").with_value('test') } + end + end + + describe 'with RedHat' do + let :facts do + { :osfamily => 'RedHat' } + end + + let :params do + req_params + end + + it 'should ensure that the cinder-volume sysconfig file is present' do + should contain_file('/etc/sysconfig/openstack-cinder-volume').with( + :ensure => 'present' + ) + end + + it 'should configure RedHat init override' do + should contain_file_line('set initscript env').with( + :line => /export CEPH_ARGS=\"--id test\"/, + :path => '/etc/sysconfig/openstack-cinder-volume', + :notify => 'Service[cinder-volume]') + end + end + +end diff --git a/cinder/spec/defines/cinder_backend_vmdk_spec.rb b/cinder/spec/defines/cinder_backend_vmdk_spec.rb new file mode 100644 index 000000000..dc59f9801 --- /dev/null +++ b/cinder/spec/defines/cinder_backend_vmdk_spec.rb @@ -0,0 +1,60 @@ +require 'spec_helper' + +describe 'cinder::backend::vmdk' do + + let(:title) { 'hippo' } + + let :params do + { + :host_ip => '172.16.16.16', + :host_password => 'asdf', + :host_username => 'user' + } + end + + let :optional_params do + { + :volume_folder => 'cinder-volume-folder', + :api_retry_count => 5, + :max_object_retrieval => 200, + :task_poll_interval => 10, + :image_transfer_timeout_secs => 3600, + :wsdl_location => 'http://127.0.0.1:8080/vmware/SDK/wsdl/vim25/vimService.wsdl' + } + end + + it 'should configure vmdk driver in cinder.conf' do + should contain_cinder_config('hippo/volume_backend_name').with_value('hippo') + should contain_cinder_config('hippo/volume_driver').with_value('cinder.volume.drivers.vmware.vmdk.VMwareVcVmdkDriver') + should contain_cinder_config('hippo/vmware_host_ip').with_value(params[:host_ip]) + should contain_cinder_config('hippo/vmware_host_username').with_value(params[:host_username]) + should contain_cinder_config('hippo/vmware_host_password').with_value(params[:host_password]) + should contain_cinder_config('hippo/vmware_volume_folder').with_value('cinder-volumes') + should contain_cinder_config('hippo/vmware_api_retry_count').with_value(10) + should contain_cinder_config('hippo/vmware_max_object_retrieval').with_value(100) + should contain_cinder_config('hippo/vmware_task_poll_interval').with_value(5) + should contain_cinder_config('hippo/vmware_image_transfer_timeout_secs').with_value(7200) + should_not contain_cinder_config('hippo/vmware_wsdl_location') + end + + it 'installs vmdk driver with pip' do + should contain_package('suds').with( + :ensure => 'present', + :provider => 'pip') + end + + context 'with optional parameters' do + before :each do + params.merge!(optional_params) + end + + it 'should configure vmdk driver in cinder.conf' do + should contain_cinder_config('hippo/vmware_volume_folder').with_value(params[:volume_folder]) + should contain_cinder_config('hippo/vmware_api_retry_count').with_value(params[:api_retry_count]) + should contain_cinder_config('hippo/vmware_max_object_retrieval').with_value(params[:max_object_retrieval]) + should contain_cinder_config('hippo/vmware_task_poll_interval').with_value(params[:task_poll_interval]) + should contain_cinder_config('hippo/vmware_image_transfer_timeout_secs').with_value(params[:image_transfer_timeout_secs]) + should contain_cinder_config('hippo/vmware_wsdl_location').with_value(params[:wsdl_location]) + end + end +end diff --git a/cinder/spec/defines/cinder_type_set_spec.rb b/cinder/spec/defines/cinder_type_set_spec.rb new file mode 100644 index 000000000..4cb57c189 --- /dev/null +++ b/cinder/spec/defines/cinder_type_set_spec.rb @@ -0,0 +1,29 @@ +#Author: Andrew Woodward + +require 'spec_helper' + +describe 'cinder::type_set' do + + let(:title) {'hippo'} + + let :params do { + :type => 'sith', + :key => 'monchichi', + :os_password => 'asdf', + :os_tenant_name => 'admin', + :os_username => 'admin', + :os_auth_url => 'http://127.127.127.1:5000/v2.0/', + } + end + + it 'should have its execs' do + should contain_exec('cinder type-key sith set monchichi=hippo').with( + :command => 'cinder type-key sith set monchichi=hippo', + :environment => [ + 'OS_TENANT_NAME=admin', + 'OS_USERNAME=admin', + 'OS_PASSWORD=asdf', + 'OS_AUTH_URL=http://127.127.127.1:5000/v2.0/'], + :require => 'Package[python-cinderclient]') + end +end diff --git a/cinder/spec/defines/cinder_type_spec.rb b/cinder/spec/defines/cinder_type_spec.rb new file mode 100644 index 000000000..bfc5bdc56 --- /dev/null +++ b/cinder/spec/defines/cinder_type_spec.rb @@ -0,0 +1,31 @@ +#Author: Andrew Woodward + +require 'spec_helper' + +describe 'cinder::type' do + + let(:title) {'hippo'} + + let :params do { + :set_value => ['name1','name2'], + :set_key => 'volume_backend_name', + :os_password => 'asdf', + :os_tenant_name => 'admin', + :os_username => 'admin', + :os_auth_url => 'http://127.127.127.1:5000/v2.0/', + } + end + + it 'should have its execs' do + should contain_exec('cinder type-create hippo').with( + :command => 'cinder type-create hippo', + :environment => [ + 'OS_TENANT_NAME=admin', + 'OS_USERNAME=admin', + 'OS_PASSWORD=asdf', + 'OS_AUTH_URL=http://127.127.127.1:5000/v2.0/'], + :require => 'Package[python-cinderclient]') + should contain_exec('cinder type-key hippo set volume_backend_name=name1') + should contain_exec('cinder type-key hippo set volume_backend_name=name2') + end +end diff --git a/cinder/spec/spec_helper.rb b/cinder/spec/spec_helper.rb new file mode 100644 index 000000000..e17ce17d6 --- /dev/null +++ b/cinder/spec/spec_helper.rb @@ -0,0 +1,5 @@ +require 'puppetlabs_spec_helper/module_spec_helper' + +RSpec.configure do |c| + c.alias_it_should_behave_like_to :it_configures, 'configures' +end diff --git a/common/.gitignore b/common/.gitignore new file mode 100644 index 000000000..2085807d2 --- /dev/null +++ b/common/.gitignore @@ -0,0 +1,3 @@ +old/ +rpmbuild/ + diff --git a/common/COPYING b/common/COPYING new file mode 100644 index 000000000..dba13ed2d --- /dev/null +++ b/common/COPYING @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/common/COPYRIGHT b/common/COPYRIGHT new file mode 100644 index 000000000..480149fb7 --- /dev/null +++ b/common/COPYRIGHT @@ -0,0 +1,16 @@ +Copyright (C) 2012-2013+ James Shubin +Written by James Shubin + +This puppet module is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This puppet module is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + diff --git a/common/INSTALL b/common/INSTALL new file mode 100644 index 000000000..f497841f7 --- /dev/null +++ b/common/INSTALL @@ -0,0 +1,18 @@ +To install this puppet module, copy this folder to your puppet modulepath. + +You can usually find out where this is by running: + +$ puppet config print modulepath + +on your puppetmaster. In my case, this contains the directory: + +/etc/puppet/modules/ + +I keep all of my puppet modules in git managed directories named: + +puppet- + +You must remove the 'puppet-' prefix from the directory name for it to work! + +Happy hacking! + diff --git a/common/Makefile b/common/Makefile new file mode 100644 index 000000000..5ff251842 --- /dev/null +++ b/common/Makefile @@ -0,0 +1,146 @@ +# Common puppet utilities module by James +# Copyright (C) 2012-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +.PHONY: all docs rpm srpm spec tar upload upload-sources upload-srpms upload-rpms +.SILENT: + +# version of the program +VERSION := $(shell cat VERSION) +RELEASE = 1 +SPEC = rpmbuild/SPECS/puppet-common.spec +SOURCE = rpmbuild/SOURCES/puppet-common-$(VERSION).tar.bz2 +SRPM = rpmbuild/SRPMS/puppet-common-$(VERSION)-$(RELEASE).src.rpm +RPM = rpmbuild/RPMS/puppet-common-$(VERSION)-$(RELEASE).rpm +SERVER = 'download.gluster.org' +REMOTE_PATH = 'purpleidea/puppet-common' + +all: docs rpm + +docs: puppet-common-documentation.pdf + +puppet-common-documentation.pdf: DOCUMENTATION.md + pandoc DOCUMENTATION.md -o 'puppet-common-documentation.pdf' + +# +# aliases +# +# TODO: does making an rpm depend on making a .srpm first ? +rpm: $(SRPM) $(RPM) + # do nothing + +srpm: $(SRPM) + # do nothing + +spec: $(SPEC) + # do nothing + +tar: $(SOURCE) + # do nothing + +upload: upload-sources upload-srpms upload-rpms + # do nothing + +# +# rpmbuild +# +$(RPM): $(SPEC) $(SOURCE) + @echo Running rpmbuild -bb... + rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bb $(SPEC) && \ + mv rpmbuild/RPMS/noarch/puppet-common-$(VERSION)-$(RELEASE).*.rpm $(RPM) + +$(SRPM): $(SPEC) $(SOURCE) + @echo Running rpmbuild -bs... + rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bs $(SPEC) + # renaming is not needed because we aren't using the dist variable + #mv rpmbuild/SRPMS/puppet-common-$(VERSION)-$(RELEASE).*.src.rpm $(SRPM) + +# +# spec +# +$(SPEC): rpmbuild/ puppet-common.spec.in + @echo Running templater... + #cat puppet-common.spec.in > $(SPEC) + sed -e s/__VERSION__/$(VERSION)/ -e s/__RELEASE__/$(RELEASE)/ < puppet-common.spec.in > $(SPEC) + # append a changelog to the .spec file + git log --format="* %cd %aN <%aE>%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $(SPEC) + +# +# archive +# +$(SOURCE): rpmbuild/ + @echo Running git archive... + # use HEAD if tag doesn't exist yet, so that development is easier... + git archive --prefix=puppet-common-$(VERSION)/ -o $(SOURCE) $(VERSION) 2> /dev/null || (echo 'Warning: $(VERSION) does not exist.' && git archive --prefix=puppet-common-$(VERSION)/ -o $(SOURCE) HEAD) + +# TODO: ensure that each sub directory exists +rpmbuild/: + mkdir -p rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} + +# +# sha256sum +# +rpmbuild/SOURCES/SHA256SUMS: rpmbuild/SOURCES/ $(SOURCE) + @echo Running SOURCES sha256sum... + cd rpmbuild/SOURCES/ && sha256sum *.tar.bz2 > SHA256SUMS; cd - + +rpmbuild/SRPMS/SHA256SUMS: rpmbuild/SRPMS/ $(SRPM) + @echo Running SRPMS sha256sum... + cd rpmbuild/SRPMS/ && sha256sum *src.rpm > SHA256SUMS; cd - + +rpmbuild/RPMS/SHA256SUMS: rpmbuild/RPMS/ $(RPM) + @echo Running RPMS sha256sum... + cd rpmbuild/RPMS/ && sha256sum *.rpm > SHA256SUMS; cd - + +# +# gpg +# +rpmbuild/SOURCES/SHA256SUMS.asc: rpmbuild/SOURCES/SHA256SUMS + @echo Running SOURCES gpg... + # the --yes forces an overwrite of the SHA256SUMS.asc if necessary + gpg2 --yes --clearsign rpmbuild/SOURCES/SHA256SUMS + +rpmbuild/SRPMS/SHA256SUMS.asc: rpmbuild/SRPMS/SHA256SUMS + @echo Running SRPMS gpg... + gpg2 --yes --clearsign rpmbuild/SRPMS/SHA256SUMS + +rpmbuild/RPMS/SHA256SUMS.asc: rpmbuild/RPMS/SHA256SUMS + @echo Running RPMS gpg... + gpg2 --yes --clearsign rpmbuild/RPMS/SHA256SUMS + +# +# upload +# +# upload to public server +upload-sources: rpmbuild/SOURCES/ rpmbuild/SOURCES/SHA256SUMS rpmbuild/SOURCES/SHA256SUMS.asc + if [ "`cat rpmbuild/SOURCES/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SOURCES/ && cat SHA256SUMS'`" ]; then \ + echo Running SOURCES upload...; \ + rsync -avz rpmbuild/SOURCES/ $(SERVER):$(REMOTE_PATH)/SOURCES/; \ + fi + +upload-srpms: rpmbuild/SRPMS/ rpmbuild/SRPMS/SHA256SUMS rpmbuild/SRPMS/SHA256SUMS.asc + if [ "`cat rpmbuild/SRPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SRPMS/ && cat SHA256SUMS'`" ]; then \ + echo Running SRPMS upload...; \ + rsync -avz rpmbuild/SRPMS/ $(SERVER):$(REMOTE_PATH)/SRPMS/; \ + fi + +upload-rpms: rpmbuild/RPMS/ rpmbuild/RPMS/SHA256SUMS rpmbuild/RPMS/SHA256SUMS.asc + if [ "`cat rpmbuild/RPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/RPMS/ && cat SHA256SUMS'`" ]; then \ + echo Running RPMS upload...; \ + rsync -avz --prune-empty-dirs rpmbuild/RPMS/ $(SERVER):$(REMOTE_PATH)/RPMS/; \ + fi + +# vim: ts=8 diff --git a/common/README b/common/README new file mode 100644 index 000000000..2e03bc9ea --- /dev/null +++ b/common/README @@ -0,0 +1,22 @@ +This is puppet-common a puppet module for common things. + +Please read the INSTALL file for instructions on getting this installed. +Look in the examples/ folder for usage. If none exist, please contribute one! +This code may be a work in progress. The interfaces may change without notice. +Patches are welcome, but please be patient. They are best received by email. +Please ping me if you have big changes in mind, before you write a giant patch. + +Module specific notes: +* This module is a useful dependency for some of my other modules. +* You may also find some of the individual pieces useful. +* I have removed functionality that now exists in the puppet "stdlib". +* Some of the functionality is so useful, that this imports types directly. + +Dependencies: +* puppetlabs-stdlib (required) +* my puppet-puppet module (optional) + + +Happy hacking, +James Shubin , https://ttboj.wordpress.com/ + diff --git a/common/VERSION b/common/VERSION new file mode 100644 index 000000000..4e379d2bf --- /dev/null +++ b/common/VERSION @@ -0,0 +1 @@ +0.0.2 diff --git a/common/examples/again-delta-example.pp b/common/examples/again-delta-example.pp new file mode 100644 index 000000000..d1e73d76f --- /dev/null +++ b/common/examples/again-delta-example.pp @@ -0,0 +1,23 @@ +# this is just a proof of concept example, to help you visualize how this works + +include common::again + +# when notified, this timer will run puppet again, delta seconds after it ends! +common::again::delta { 'delta-timer': + delta => 120, # 2 minutes + #start_timer_now => true, # use this to start the countdown asap! +} + +file { '/tmp/foo': + content => "Something happened!\n", + # NOTE: that as long as you don't remove or change the /tmp/foo file, + # this will only cause a notify when the file needs changes done. This + # is what prevents this from infinite recursion, and lets puppet sleep. + notify => Common::Again::Delta['delta-timer'], # notify puppet! +} + +# always exec so that i can easily see when puppet runs... proof that it works! +exec { 'proof': + command => '/bin/date >> /tmp/puppet.again', +} + diff --git a/common/examples/again-example.pp b/common/examples/again-example.pp new file mode 100644 index 000000000..25cc6255f --- /dev/null +++ b/common/examples/again-example.pp @@ -0,0 +1,17 @@ +# this is just a proof of concept example, to help you visualize how this works + +include common::again + +file { '/tmp/foo': + content => "Something happened!\n", + # NOTE: that as long as you don't remove or change the /tmp/foo file, + # this will only cause a notify when the file needs changes done. This + # is what prevents this from infinite recursion, and lets puppet sleep. + notify => Exec['again'], # notify puppet! +} + +# always exec so that i can easily see when puppet runs... proof that it works! +exec { 'proof': + command => '/bin/date >> /tmp/puppet.again', +} + diff --git a/common/manifests/again.pp b/common/manifests/again.pp new file mode 100644 index 000000000..cd2bf7418 --- /dev/null +++ b/common/manifests/again.pp @@ -0,0 +1,86 @@ +# Run puppet again if notified to do so +# Copyright (C) 2012-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +class common::again { + + include common::vardir + + #$vardir = $::common::vardir::module_vardir # with trailing slash + $vardir = regsubst($::common::vardir::module_vardir, '\/$', '') + + # store 'again' specific code in a separate directory + file { "${vardir}/again/": + ensure => directory, # make sure this is a directory + recurse => true, # don't recurse into directory + purge => true, # don't purge unmanaged files + force => true, # don't purge subdirs and links + require => File["${vardir}/"], + } + + file { "${vardir}/again/again.py": + # NOTE: this is actually templated, but no templating + # is actually being used. This gives us the option to + # pass in some variables if we decide we would like a + # way to get values in other than via command line... + # we could pass in some environ data or other data... + content => template('common/again/again.py.erb'), + owner => root, + group => root, + mode => 754, # if you're not root, you can't run it! + ensure => present, + require => File["${vardir}/again/"], + } + + # notify this command whenever you want to trigger another puppet run! + exec { 'again': + command => "${vardir}/again/again.py", + logoutput => on_failure, + refreshonly => true, # run whenever someone requests it! + require => File["${vardir}/again/again.py"], + } +} + +## NOTE: splitting this into a separate file didn't work properly in this module +#define common::again::delta( +# $delta = 0, +# # start timer counting now! (default is to start when puppet finishes!) +# $start_timer_now = false +#) { +# include common::vardir +# include common::again + +# #$vardir = $::common::vardir::module_vardir # with trailing slash +# $vardir = regsubst($::common::vardir::module_vardir, '\/$', '') + +# $valid_start_timer_now = $start_timer_now ? { +# true => '--start-timer-now', +# default => '', +# } + +# $arglist = ["--delta ${delta}", "${valid_start_timer_now}"] +# $args = join(delete($arglist, ''), ' ') + +# # notify this command whenever you want to trigger another puppet run! +# exec { "again-delta-${name}": +# command => "${vardir}/again/again.py ${args}", +# logoutput => on_failure, +# refreshonly => true, # run whenever someone requests it! +# require => File["${vardir}/again/again.py"], +# } +#} + +# vim: ts=8 diff --git a/common/manifests/again/delta.pp b/common/manifests/again/delta.pp new file mode 100644 index 000000000..2c2c27ed9 --- /dev/null +++ b/common/manifests/again/delta.pp @@ -0,0 +1,52 @@ +# Run puppet again if notified to do so +# Copyright (C) 2012-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +# NOTE: see ../again.pp for the delta code + +# NOTE: splitting this into a separate file didn't work properly in this module +define common::again::delta( + $delta = 0, + # start timer counting now! (default is to start when puppet finishes!) + $start_timer_now = false +) { + include common::vardir + include common::again + + #$vardir = $::common::vardir::module_vardir # with trailing slash + $vardir = regsubst($::common::vardir::module_vardir, '\/$', '') + + $valid_delta = inline_template('<%= [Fixnum, String].include?(@delta.class) ? @delta.to_i : 0 %>') + + $valid_start_timer_now = $start_timer_now ? { + true => '--start-timer-now', + default => '', + } + + $arglist = ["--delta ${valid_delta}", "${valid_start_timer_now}"] + $args = join(delete($arglist, ''), ' ') + + # notify this command whenever you want to trigger another puppet run! + exec { "again-delta-${name}": + command => "${vardir}/again/again.py ${args}", + logoutput => on_failure, + refreshonly => true, # run whenever someone requests it! + require => File["${vardir}/again/again.py"], + } +} + + +# vim: ts=8 diff --git a/common/manifests/frag/frag.pp b/common/manifests/frag/frag.pp new file mode 100644 index 000000000..3938006d9 --- /dev/null +++ b/common/manifests/frag/frag.pp @@ -0,0 +1,124 @@ +# Create a whole file (whole {}) from fragments (frag {}) +# Copyright (C) 2012-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +# thanks to #puppet@freenode.net for some good implementation advice + +define whole( + $dir, # the directory to store the fragment + $owner = root, # the file {} defaults were used here + $group = root, + $mode = '644', + $backup = undef, # the default value is actually: puppet + $pattern = '', # /usr/bin/find -name is used + $frag = false, # set true to also act like a frag obj! + $ensure = present # TODO: does absent even work properly? +) { + $d = sprintf("%s/", regsubst($dir, '\/$', '')) # ensure trailing slash + $safe_d = shellquote($d) + $safe_p = shellquote($pattern) + $command = $pattern ? { + '' => "/usr/bin/find ${safe_d} -maxdepth 1 -type f -print0 | /bin/sort -z | /usr/bin/xargs -0 /bin/cat", + default => "/usr/bin/find ${safe_d} -maxdepth 1 -name ${safe_p} -type f -print0 | /bin/sort -z | /usr/bin/xargs -0 /bin/cat", + } + + # this is the parent (basename) dir of $name which is special if i frag + $frag_d = sprintf("%s/", regsubst($name, '((\/[\w.-]+)*)(\/)([\w.-]+)', '\1')) + + # the file (used to set perms and as a placeholder so it's not deleted) + file { "${name}": + ensure => $ensure, + owner => $owner, + group => $group, + mode => $mode, + backup => $backup, + checksum => 'md5', + before => $frag ? { # used when this is also a frag + true => Exec["${frag_d}"], + default => undef, + } + } + + # ensure directory exists and is managed + file { "${d}": + ensure => directory, # make sure this is a directory + recurse => true, # recursively manage directory + purge => true, # purge all unmanaged files + force => true, # also purge subdirs and links + before => Exec["${d}"], + } + + # actually make the file from fragments with this command + # use >| to force target file clobbering (ignore a: "set -o noclobber") + exec { "${d}": + command => "${command} >| ${name}", + # NOTE: if we don't use 'bash -c' this spurriously sends notify + # actually check that the file matches what it really should be + unless => "/bin/bash -c '/usr/bin/diff -q <(/bin/cat ${name}) <(${command})'", + logoutput => on_failure, + # if we're a frag, make sure that we build ourself out before + # the parent `whole` object which uses us, builds itself out! + before => $frag ? { + true => Exec["${frag_d}"], + default => undef, + }, + # if we're a frag, then it's important to wait for the parent + # frag collection directory to get created before i make this + require => $frag ? { + true => File["${frag_d}"], + default => undef, + }, + } +} + +# frag supports both $source and $content. if $source is not empty, it is used, +# otherwise content is used. frag should behave like a first class file object. +define frag( # dir to store frag, is path in namevar + $owner = root, # the file {} defaults were chosen here + $group = root, + $mode = '644', + $backup = undef, # the default value is actually: puppet + $ensure = present, + $content = '', + $source = '' + # TODO: add more file object features if someone needs them or if bored +) { + # finds the file name in a complete path; eg: /tmp/dir/file => file + #$x = regsubst($name, '(\/[\w.]+)*(\/)([\w.]+)', '\3') + # finds the basepath in a complete path; eg: /tmp/dir/file => /tmp/dir/ + $d = sprintf("%s/", regsubst($name, '((\/[\w.-]+)*)(\/)([\w.-]+)', '\1')) + + # the file fragment + file { "${name}": + ensure => $ensure, + owner => $owner, + group => $group, + mode => $mode, + backup => $backup, + content => $source ? { + '' => $content, + default => undef, + }, + source => $source ? { + '' => undef, + default => $source, + }, + before => Exec["${d}"], + require => File["${d}"], + } +} + +# vim: ts=8 diff --git a/common/manifests/init.pp b/common/manifests/init.pp new file mode 100644 index 000000000..fa9bec3eb --- /dev/null +++ b/common/manifests/init.pp @@ -0,0 +1,23 @@ +# Common puppet utilities module by James +# Copyright (C) 2012-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import 'frag/*.pp' + +class common { + + +} diff --git a/common/manifests/vardir.pp b/common/manifests/vardir.pp new file mode 100644 index 000000000..f7626c94d --- /dev/null +++ b/common/manifests/vardir.pp @@ -0,0 +1,52 @@ +# Common puppet utilities module by James +# Copyright (C) 2010-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +class common::vardir { # module vardir snippet + if "${::puppet_vardirtmp}" == '' { + if "${::puppet_vardir}" == '' { + # here, we require that the puppetlabs fact exist! + fail('Fact: $puppet_vardir is missing!') + } + $tmp = sprintf("%s/tmp/", regsubst($::puppet_vardir, '\/$', '')) + # base directory where puppet modules can work and namespace in + file { "${tmp}": + ensure => directory, # make sure this is a directory + recurse => false, # don't recurse into directory + purge => true, # purge all unmanaged files + force => true, # also purge subdirs and links + owner => root, + group => nobody, + mode => 600, + backup => false, # don't backup to filebucket + #before => File["${module_vardir}"], # redundant + #require => Package['puppet'], # no puppet module seen + } + } else { + $tmp = sprintf("%s/", regsubst($::puppet_vardirtmp, '\/$', '')) + } + $module_vardir = sprintf("%s/common/", regsubst($tmp, '\/$', '')) + file { "${module_vardir}": # /var/lib/puppet/tmp/common/ + ensure => directory, # make sure this is a directory + recurse => true, # recursively manage directory + purge => true, # purge all unmanaged files + force => true, # also purge subdirs and links + owner => root, group => nobody, mode => 600, backup => false, + require => File["${tmp}"], # File['/var/lib/puppet/tmp/'] + } +} + +# vim: ts=8 diff --git a/common/puppet-common.spec.in b/common/puppet-common.spec.in new file mode 100644 index 000000000..9cd21ab8a --- /dev/null +++ b/common/puppet-common.spec.in @@ -0,0 +1,40 @@ +%global puppet_module_version __VERSION__ + +Name: puppet-common +Version: __VERSION__ +#Release: __RELEASE__%{?dist} # use this to make dist specific builds +Release: __RELEASE__ +Summary: A puppet module filled with common puppet utilities +License: AGPLv3+ +URL: https://github.com/purpleidea/puppet-common +Source0: https://download.gluster.org/pub/gluster/purpleidea/puppet-common/SOURCES/puppet-common-%{puppet_module_version}.tar.bz2 +BuildArch: noarch + +Requires: puppet >= 3.0.0 +Requires: puppetlabs-stdlib >= 4.1.0 + +%description +Common puppet utilities that are useful for other puppet modules. + +%prep +%setup -c -q -T -D -a 0 + +find %{_builddir} -type f -name ".*" -exec rm {} + +find %{_builddir} -size 0 -exec rm {} + +find %{_builddir} \( -name "*.pl" -o -name "*.sh" \) -exec chmod +x {} + +find %{_builddir} \( -name "*.pp" -o -name "*.py" \) -exec chmod -x {} + +find %{_builddir} \( -name "*.rb" -o -name "*.erb" \) -exec chmod -x {} + -exec sed -i "/^#!/{d;q}" {} + + +%build + +%install +rm -rf %{buildroot} +# _datadir is typically /usr/share/ +install -d -m 0755 %{buildroot}/%{_datadir}/puppet/modules/ +cp -r puppet-common-%{puppet_module_version} %{buildroot}/%{_datadir}/puppet/modules/common + +%files +%{_datadir}/puppet/modules/* + +# this changelog is auto-generated by git log +%changelog diff --git a/common/templates/again/again.py.erb b/common/templates/again/again.py.erb new file mode 100755 index 000000000..5eb1c3125 --- /dev/null +++ b/common/templates/again/again.py.erb @@ -0,0 +1,189 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +""" +Run puppet again if notified to do so + +This script is only executed by a puppet exec type. When run, it forks and then +waits for the parent process (puppet) to exit. It then runs puppet "again", +with the same arguments it was originally started with. + +The exec type for "again" is already provided. To activate it, you notify it: + + include again + sometype { 'foo': + notify => Exec['again'], # notify it like this + } + +This is particularly useful if you know that when one of your types runs, it +*will* need another puppet execution to finish building. Sadly, for certain +complex puppet modules, this is unavoidable. You can however make sure to avoid +infinite loops, which will just waste system resources. +""" +# Run puppet again if notified to do so +# Copyright (C) 2012-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +import os +import sys +import time +import math +import errno +import argparse + +DELAY = 0 # wait this many seconds after puppet exit before running again +LOCKFILE = '/var/lib/puppet/state/agent_catalog_run.lock' # lockfile path + +def merge(a, b): # merge two hashes and return the union + r = a.copy() + r.update(b) + return r + +def is_running(pid): + """ + os.waitpid() may not work if the process is not a child of the current + process. In that case, using os.kill(pid, 0) may indeed be the best + solution. Note that, in general, there are three likely outcomes of + calling os.kill() on a process: + + * If the process exists and belongs to you, the call succeeds. + * If the process exists but belong to another user, it throws an + OSError with the errno attribute set to errno.EPERM. + * If the process does not exist, it throws an OSError with the errno + attribute set to errno.ESRCH. + """ + try: + os.kill(pid, 0) + except OSError as e: + if e.errno == errno.ESRCH: + return False + return True + +def is_locked(): + """ + Lets us know if a puppet agent is currently running. + """ + # TODO: is there a more reliable way to do this ? This is sort of racy. + return os.path.isfile(LOCKFILE) + +parser = argparse.ArgumentParser(description="Utility used by Puppet Exec['again'].") +parser.add_argument('--delta', dest='delta', action='store', type=int, required=False, default=0) +# start delta timer immediately, instead of waiting till puppet finishes... +parser.add_argument('--start-timer-now', dest='start_timer_now', action='store_true', default=False, required=False) +args = parser.parse_args() + +start = time.time() # TODO: use time.monotonic() if exists +service = False # are we running as a service ? +pid = os.getpid() # my pid +ppid = os.getppid() # parent pid + +# parse parent cmdline +with open("/proc/%d/cmdline" % ppid, 'r') as f: + cmdline = f.read() +argv = cmdline.split("\0") # separated by nulls (the: ^@ character in vim) +if argv[-1] == '': argv.pop() # remove empty element at end of list if exists + +# parse parent environ +with open("/proc/%d/environ" % ppid, 'r') as f: + environ = f.read() + +env = environ.split("\0") +if env[-1] == '': env.pop() # as above, there is a trailing null to remove! +env = map(lambda x: {x.split('=')[0]: '='.join(x.split('=')[1:])}, env) # list! +env = reduce(lambda a,b: merge(a,b), env) # merge the hash list into hash + +# TODO: does the noop detection work when we run as a service ? (probably not!) +# if we're running as no op, then repeat execution won't help +if '--noop' in argv: + sys.exit(0) + +# TODO: do a sanity check to verify that we've got a valid puppet cmdline... + +# heuristic, based on previous experiments, that service agents have a weird $0 +# see: $0 = "puppet agent: applying configuration" in: [...]/puppet/agent.rb +if argv[0].startswith('puppet agent: '): + service = True # bonus + argv = ['/usr/bin/puppet', 'agent', '--test'] + # NOTE: create a shim, if you ever need help debugging :) + # argv.insert(0, '/tmp/shim.sh') + +# if we're running as a service, kick off a "one of" run +# TODO: is there a more reliable way to detect this ? +if not('--test' in argv) and not('-t' in argv): + service = True # this is the main setter of this variable + argv.append('--test') # TODO: this isn't ideal, but it's safe enough! + +# fork a child process. return 0 in the child and the child’s process id in the +# parent. if an error occurs OSError is raised. +try: + fork = os.fork() # TODO: forkpty() instead ? + +except OSError, e: + print >> sys.stderr, "Error forking: %s" % str(e) + sys.exit(1) + +# branch +if fork == 0: # child + # wait for ppid to exit... + # TODO: we can probably remove the service check altogether, because + # actually the ppid does spawn, and then exit i think... it doesn't + # mean we can skip the is_locked() check, but we don't have to verify + # that we're really a service, because the ppid does dissapear. + if not service: # the service pid shouldn't ever exit... + while is_running(ppid): + time.sleep(1) + + if not args.start_timer_now: + start = time.time() # TODO: use time.monotonic() if exists + + # wait for any agent runs to finish... + while is_locked(): + time.sleep(1) + + if service: # we need to wait for is_locked to end first... + if not args.start_timer_now: + start = time.time() # TODO: use time.monotonic() if exists + + # optionally delay before starting puppet again + if DELAY > 0: + time.sleep(int(DELAY)) + + # wait out the --delta timer + while True: + delta = time.time() - start # time elapsed + timeleft = args.delta - delta # time left + if int(timeleft) <= 0: + break + + time.sleep(int(math.ceil(timeleft))) # this much time left + + # wait for any agents to finish running, now that we waited for timers! + # NOTE: if puppet starts running immediately after this spinlock ended, + # but before our exec call runs, then when our exec runs puppet it will + # exit right away due to the puppet lock. this is a race condition, but + # it isn't a problem because the effect of having puppet run right away + # after this spinlock, will be successful, albeit by different means... + while is_locked(): + time.sleep(1) + + # now run puppet the same way it ran in cmdline + # NOTE: env is particularly important or puppet breaks :( + os.execvpe(argv[0], argv, env) # this command does not return! + +else: # parent + print "pid: %d will try to puppet again..." % fork + sys.exit(0) # let puppet exit successfully! + +# vim: ts=8 diff --git a/concat/CHANGELOG b/concat/CHANGELOG new file mode 100644 index 000000000..c506cf1a5 --- /dev/null +++ b/concat/CHANGELOG @@ -0,0 +1,29 @@ +KNOWN ISSUES: +- In 0.24.8 you will see inintended notifies, if you build a file + in a run, the next run will also see it as changed. This is due + to how 0.24.8 does the purging of unhandled files, this is improved + in 0.25.x and we cannot work around it in our code. + +CHANGELOG: +- 2010/02/19 - initial release +- 2010/03/12 - add support for 0.24.8 and newer + - make the location of sort configurable + - add the ability to add shell comment based warnings to + top of files + - add the ablity to create empty files +- 2010/04/05 - fix parsing of WARN and change code style to match rest + of the code + - Better and safer boolean handling for warn and force + - Don't use hard coded paths in the shell script, set PATH + top of the script + - Use file{} to copy the result and make all fragments owned + by root. This means we can chnage the ownership/group of the + resulting file at any time. + - You can specify ensure => "/some/other/file" in concat::fragment + to include the contents of a symlink into the final file. +- 2010/04/16 - Add more cleaning of the fragment name - removing / from the $name +- 2010/05/22 - Improve documentation and show the use of ensure => +- 2010/07/14 - Add support for setting the filebucket behavior of files +- 2010/10/04 - Make the warning message configurable +- 2010/12/03 - Add flags to make concat work better on Solaris - thanks Jonathan Boyett +- 2011/02/03 - Make the shell script more portable and add a config option for root group diff --git a/concat/README.markdown b/concat/README.markdown new file mode 100644 index 000000000..1945150dc --- /dev/null +++ b/concat/README.markdown @@ -0,0 +1,103 @@ +What is it? +=========== + +A Puppet module that can construct files from fragments. + +Please see the comments in the various .pp files for details +as well as posts on my blog at http://www.devco.net/ + +Released under the Apache 2.0 licence + +Usage: +------ + +If you wanted a /etc/motd file that listed all the major modules +on the machine. And that would be maintained automatically even +if you just remove the include lines for other modules you could +use code like below, a sample /etc/motd would be: + +
+Puppet modules on this server:
+
+    -- Apache
+    -- MySQL
+
+ +Local sysadmins can also append to the file by just editing /etc/motd.local +their changes will be incorporated into the puppet managed motd. + +
+# class to setup basic motd, include on all nodes
+class motd {
+   include concat::setup
+   $motd = "/etc/motd"
+
+   concat{$motd:
+      owner => root,
+      group => root,
+      mode  => 644
+   }
+
+   concat::fragment{"motd_header":
+      target => $motd,
+      content => "\nPuppet modules on this server:\n\n",
+      order   => 01,
+   }
+
+   # local users on the machine can append to motd by just creating
+   # /etc/motd.local
+   concat::fragment{"motd_local":
+      target => $motd,
+      ensure  => "/etc/motd.local",
+      order   => 15
+   }
+}
+
+# used by other modules to register themselves in the motd
+define motd::register($content="", $order=10) {
+   if $content == "" {
+      $body = $name
+   } else {
+      $body = $content
+   }
+
+   concat::fragment{"motd_fragment_$name":
+      target  => "/etc/motd",
+      content => "    -- $body\n"
+   }
+}
+
+# a sample apache module
+class apache {
+   include apache::install, apache::config, apache::service
+
+   motd::register{"Apache": }
+}
+
+ +Known Issues: +------------- +* In 0.24.8 you will see inintended notifies, if you build a file + in a run, the next run will also see it as changed. This is due + to how 0.24.8 does the purging of unhandled files, this is improved + in 0.25.x and we cannot work around it in our code. + +Contributors: +------------- +**Paul Elliot** + + * Provided 0.24.8 support, shell warnings and empty file creation support. + +**Chad Netzer** + + * Various patches to improve safety of file operations + * Symlink support + +**David Schmitt** + + * Patch to remove hard coded paths relying on OS path + * Patch to use file{} to copy the resulting file to the final destination. This means Puppet client will show diffs and that hopefully we can change file ownerships now + +Contact: +-------- +You can contact me on rip@devco.net or follow my blog at http://www.devco.net I am also on twitter as ripienaar diff --git a/concat/files/concatfragments.sh b/concat/files/concatfragments.sh new file mode 100644 index 000000000..52fd68453 --- /dev/null +++ b/concat/files/concatfragments.sh @@ -0,0 +1,130 @@ +#!/bin/sh + +# Script to concat files to a config file. +# +# Given a directory like this: +# /path/to/conf.d +# |-- fragments +# | |-- 00_named.conf +# | |-- 10_domain.net +# | `-- zz_footer +# +# The script supports a test option that will build the concat file to a temp location and +# use /usr/bin/cmp to verify if it should be run or not. This would result in the concat happening +# twice on each run but gives you the option to have an unless option in your execs to inhibit rebuilds. +# +# Without the test option and the unless combo your services that depend on the final file would end up +# restarting on each run, or in other manifest models some changes might get missed. +# +# OPTIONS: +# -o The file to create from the sources +# -d The directory where the fragments are kept +# -t Test to find out if a build is needed, basically concats the files to a temp +# location and compare with what's in the final location, return codes are designed +# for use with unless on an exec resource +# -w Add a shell style comment at the top of the created file to warn users that it +# is generated by puppet +# -f Enables the creation of empty output files when no fragments are found +# -n Sort the output numerically rather than the default alpha sort +# -g Do NOT use the GNU entensions to find, xargs and sort; might cause problems on suitably funky filenames +# +# the command: +# +# concatfragments.sh -o /path/to/conffile.cfg -d /path/to/conf.d +# +# creates /path/to/conf.d/fragments.concat and copies the resulting +# file to /path/to/conffile.cfg. The files will be sorted alphabetically +# pass the -n switch to sort numerically. +# +# The script does error checking on the various dirs and files to make +# sure things don't fail. + +OUTFILE="" +WORKDIR="" +TEST="" +FORCE="" +WARN="" +SORT1="-z" +SORT2="" +FINDARG="-print0" +XARGSARG="-0" + +PATH=/sbin:/usr/sbin:/bin:/usr/bin + +while getopts "o:s:d:tnw:fg" options; do + case $options in + o ) OUTFILE=$OPTARG;; + d ) WORKDIR=$OPTARG;; + n ) SORT2="-n";; + w ) WARNMSG="$OPTARG";; + f ) FORCE="true";; + t ) TEST="true";; + g ) FINDARG="" ; XARGSARG="" ; SORT1="" ;; + * ) echo "Specify output file with -o and fragments directory with -d" + exit 1;; + esac +done + +SORTARG="$SORT1 $SORT2" + +# do we have -o? +if [ x${OUTFILE} = "x" ]; then + echo "Please specify an output file with -o" + exit 1 +fi + +# do we have -d? +if [ x${WORKDIR} = "x" ]; then + echo "Please fragments directory with -d" + exit 1 +fi + +# can we write to -o? +if [ -a ${OUTFILE} ]; then + if [ ! -w ${OUTFILE} ]; then + echo "Cannot write to ${OUTFILE}" + exit 1 + fi +else + if [ ! -w `dirname ${OUTFILE}` ]; then + echo "Cannot write to `dirname ${OUTFILE}` to create ${OUTFILE}" + exit 1 + fi +fi + +# do we have a fragments subdir inside the work dir? +if [ ! -d "${WORKDIR}/fragments" ] && [ ! -x "${WORKDIR}/fragments" ]; then + echo "Cannot access the fragments directory" + exit 1 +fi + +# are there actually any fragments? +if [ ! "$(ls -A ${WORKDIR}/fragments)" ]; then + if [ x${FORCE} = "x" ]; then + echo "The fragments directory is empty, cowardly refusing to make empty config files" + exit 1 + fi +fi + +cd ${WORKDIR} + +if [ x${WARNMSG} = "x" ]; then + : > "fragments.concat" +else + echo -e "$WARNMSG" > "fragments.concat" +fi + +# find all the files in the fragments directory, sort them numerically and concat to fragments.concat in the working dir +find fragments/ -type f -follow $FINDARG |sort ${SORTARG}|xargs $XARGSARG cat >>"fragments.concat" + +if [ x${TEST} = "x" ]; then + # This is a real run, copy the file to outfile + cp fragments.concat ${OUTFILE} + RETVAL=$? +else + # Just compare the result to outfile to help the exec decide + cmp ${OUTFILE} fragments.concat + RETVAL=$? +fi + +exit $RETVAL diff --git a/concat/files/null/.gitignore b/concat/files/null/.gitignore new file mode 100644 index 000000000..e69de29bb diff --git a/concat/manifests/fragment.pp b/concat/manifests/fragment.pp new file mode 100644 index 000000000..7afc4c077 --- /dev/null +++ b/concat/manifests/fragment.pp @@ -0,0 +1,51 @@ +# Puts a file fragment into a directory previous setup using concat +# +# OPTIONS: +# - target The file that these fragments belong to +# - content If present puts the content into the file +# - source If content was not specified, use the source +# - order By default all files gets a 10_ prefix in the directory +# you can set it to anything else using this to influence the +# order of the content in the file +# - ensure Present/Absent or destination to a file to include another file +# - mode Mode for the file +# - owner Owner of the file +# - group Owner of the file +# - backup Controls the filebucketing behavior of the final file and +# see File type reference for its use. Defaults to 'puppet' +define concat::fragment($target, $content='', $source='', $order=10, $ensure = "present", $mode = 0644, $owner = root, $group = $concat::setup::root_group, $backup = "puppet") { + $safe_name = regsubst($name, '/', '_', 'G') + $safe_target_name = regsubst($target, '/', '_', 'G') + $concatdir = $concat::setup::concatdir + $fragdir = "${concatdir}/${safe_target_name}" + + # if content is passed, use that, else if source is passed use that + # if neither passed, but $ensure is in symlink form, make a symlink + case $content { + "": { + case $source { + "": { + case $ensure { + "", "absent", "present", "file", "directory": { + crit("No content, source or symlink specified") + } + } + } + default: { File{ source => $source } } + } + } + default: { File{ content => $content } } + } + + file{"${fragdir}/fragments/${order}_${safe_name}": + mode => $mode, + owner => $owner, + group => $group, + ensure => $ensure, + backup => $backup, + alias => "concat_fragment_${name}", + notify => Exec["concat_${target}"] + } +} + +# vi:tabstop=4:expandtab:ai diff --git a/concat/manifests/init.pp b/concat/manifests/init.pp new file mode 100644 index 000000000..3a1734677 --- /dev/null +++ b/concat/manifests/init.pp @@ -0,0 +1,176 @@ +# A system to construct files using fragments from other files or templates. +# +# This requires at least puppet 0.25 to work correctly as we use some +# enhancements in recursive directory management and regular expressions +# to do the work here. +# +# USAGE: +# The basic use case is as below: +# +# concat{"/etc/named.conf": +# notify => Service["named"] +# } +# +# concat::fragment{"foo.com_config": +# target => "/etc/named.conf", +# order => 10, +# content => template("named_conf_zone.erb") +# } +# +# # add a fragment not managed by puppet so local users +# # can add content to managed file +# concat::fragment{"foo.com_user_config": +# target => "/etc/named.conf", +# order => 12, +# ensure => "/etc/named.conf.local" +# } +# +# This will use the template named_conf_zone.erb to build a single +# bit of config up and put it into the fragments dir. The file +# will have an number prefix of 10, you can use the order option +# to control that and thus control the order the final file gets built in. +# +# SETUP: +# The class concat::setup defines a variable $concatdir - you should set this +# to a directory where you want all the temporary files and fragments to be +# stored. Avoid placing this somewhere like /tmp since you should never +# delete files here, puppet will manage them. +# +# There's some regular expression magic to figure out the puppet version but +# if you're on an older 0.24 version just set $puppetversion = 24 +# +# Before you can use any of the concat features you should include the +# class concat::setup somewhere on your node first. +# +# DETAIL: +# We use a helper shell script called concatfragments.sh that gets placed +# in /usr/local/bin to do the concatenation. While this might seem more +# complex than some of the one-liner alternatives you might find on the net +# we do a lot of error checking and safety checks in the script to avoid +# problems that might be caused by complex escaping errors etc. +# +# LICENSE: +# Apache Version 2 +# +# LATEST: +# http://github.com/ripienaar/puppet-concat/ +# +# CONTACT: +# R.I.Pienaar +# Volcane on freenode +# @ripienaar on twitter +# www.devco.net + + +# Sets up so that you can use fragments to build a final config file, +# +# OPTIONS: +# - mode The mode of the final file +# - owner Who will own the file +# - group Who will own the file +# - force Enables creating empty files if no fragments are present +# - warn Adds a normal shell style comment top of the file indicating +# that it is built by puppet +# - backup Controls the filebucketing behavior of the final file and +# see File type reference for its use. Defaults to 'puppet' +# +# ACTIONS: +# - Creates fragment directories if it didn't exist already +# - Executes the concatfragments.sh script to build the final file, this script will create +# directory/fragments.concat. Execution happens only when: +# * The directory changes +# * fragments.concat != final destination, this means rebuilds will happen whenever +# someone changes or deletes the final file. Checking is done using /usr/bin/cmp. +# * The Exec gets notified by something else - like the concat::fragment define +# - Copies the file over to the final destination using a file resource +# +# ALIASES: +# - The exec can notified using Exec["concat_/path/to/file"] or Exec["concat_/path/to/directory"] +# - The final file can be referened as File["/path/to/file"] or File["concat_/path/to/file"] +define concat($mode = 0644, $owner = "root", $group = $concat::setup::root_group, $warn = "false", $force = "false", $backup = "puppet", $gnu = "true", $order="alpha") { + $safe_name = regsubst($name, '/', '_', 'G') + $concatdir = $concat::setup::concatdir + $version = $concat::setup::majorversion + $fragdir = "${concatdir}/${safe_name}" + $concat_name = "fragments.concat.out" + $default_warn_message = '# This file is managed by Puppet. DO NOT EDIT.' + + case $warn { + 'true',true,yes,on: { $warnmsg = "$default_warn_message" } + 'false',false,no,off: { $warnmsg = "" } + default: { $warnmsg = "$warn" } + } + + $warnmsg_escaped = regsubst($warnmsg, "'", "'\\\\''", 'G') + $warnflag = $warnmsg_escaped ? { + '' => '', + default => "-w '$warnmsg_escaped'" + } + + case $force { + 'true',true,yes,on: { $forceflag = "-f" } + 'false',false,no,off: { $forceflag = "" } + default: { fail("Improper 'force' value given to concat: $force") } + } + + case $gnu { + 'true',true,yes,on: { $gnuflag = "" } + 'false',false,no,off: { $gnuflag = "-g" } + default: { fail("Improper 'gnu' value given to concat: $gnu") } + } + + case $order { + numeric: { $orderflag = "-n" } + alpha: { $orderflag = "" } + default: { fail("Improper 'order' value given to concat: $order") } + } + + File{ + owner => root, + group => $group, + mode => $mode, + backup => $backup + } + + file{$fragdir: + ensure => directory; + + "${fragdir}/fragments": + ensure => directory, + recurse => true, + purge => true, + force => true, + ignore => [".svn", ".git", ".gitignore"], + source => $version ? { + 24 => "puppet:///concat/null", + default => undef, + }, + notify => Exec["concat_${name}"]; + + "${fragdir}/fragments.concat": + ensure => present; + + "${fragdir}/${concat_name}": + ensure => present; + + $name: + source => "${fragdir}/${concat_name}", + owner => $owner, + group => $group, + checksum => md5, + mode => $mode, + ensure => present, + alias => "concat_${name}"; + } + + exec{"concat_${name}": + user => root, + group => $group, + notify => File[$name], + subscribe => File[$fragdir], + alias => "concat_${fragdir}", + require => [ File["/usr/local/bin/concatfragments.sh"], File[$fragdir], File["${fragdir}/fragments"], File["${fragdir}/fragments.concat"] ], + unless => "/usr/local/bin/concatfragments.sh -o ${fragdir}/${concat_name} -d ${fragdir} -t ${warnflag} ${forceflag} ${orderflag} ${gnuflag}", + command => "/usr/local/bin/concatfragments.sh -o ${fragdir}/${concat_name} -d ${fragdir} ${warnflag} ${forceflag} ${orderflag} ${gnuflag}", + } +} diff --git a/concat/manifests/setup.pp b/concat/manifests/setup.pp new file mode 100644 index 000000000..77ba47f5c --- /dev/null +++ b/concat/manifests/setup.pp @@ -0,0 +1,37 @@ +# Sets up the concat system. +# +# $concatdir should point to a place where you wish the fragments to +# live. This should not be somewhere like /tmp since ideally these files +# should not be deleted ever, puppet should always manage them +# +# $puppetversion should be either 24 or 25 to enable a 24 compatible +# mode, in 24 mode you might see phantom notifies this is a side effect +# of the method we use to clear the fragments directory. +# +# The regular expression below will try to figure out your puppet version +# but this code will only work in 0.24.8 and newer. +# +# It also copies out the concatfragments.sh file to /usr/local/bin +class concat::setup { + $root_group = 0 + $concatdir = "/var/lib/puppet/concat" + $majorversion = regsubst($puppetversion, '^[0-9]+[.]([0-9]+)[.][0-9]+$', '\1') + + file{"/usr/local/bin/concatfragments.sh": + owner => root, + group => $root_group, + mode => 755, + source => $majorversion ? { + 24 => "puppet:///concat/concatfragments.sh", + default => "puppet:///modules/concat/concatfragments.sh" + }; + + $concatdir: + ensure => directory, + owner => root, + group => $root_group, + mode => 755; + } +} + +# vi:tabstop=4:expandtab:ai diff --git a/firewall/.fixtures.yml b/firewall/.fixtures.yml new file mode 100644 index 000000000..0d10d5cec --- /dev/null +++ b/firewall/.fixtures.yml @@ -0,0 +1,3 @@ +fixtures: + symlinks: + "firewall": "#{source_dir}" diff --git a/firewall/.gitignore b/firewall/.gitignore new file mode 100644 index 000000000..9a9567b59 --- /dev/null +++ b/firewall/.gitignore @@ -0,0 +1,9 @@ +pkg/ +Gemfile.lock +# TODO: Ignore this for now until we decide what to do with it +spec/fixtures/manifests/ +.ruby-version +.rspec_system +.bundle +.vagrant +vendor/ diff --git a/firewall/.nodeset.yml b/firewall/.nodeset.yml new file mode 100644 index 000000000..767f9cd2f --- /dev/null +++ b/firewall/.nodeset.yml @@ -0,0 +1,31 @@ +--- +default_set: 'centos-64-x64' +sets: + 'centos-59-x64': + nodes: + "main.foo.vm": + prefab: 'centos-59-x64' + 'centos-64-x64': + nodes: + "main.foo.vm": + prefab: 'centos-64-x64' + 'fedora-18-x64': + nodes: + "main.foo.vm": + prefab: 'fedora-18-x64' + 'debian-607-x64': + nodes: + "main.foo.vm": + prefab: 'debian-607-x64' + 'debian-70rc1-x64': + nodes: + "main.foo.vm": + prefab: 'debian-70rc1-x64' + 'ubuntu-server-10044-x64': + nodes: + "main.foo.vm": + prefab: 'ubuntu-server-10044-x64' + 'ubuntu-server-12042-x64': + nodes: + "main.foo.vm": + prefab: 'ubuntu-server-12042-x64' diff --git a/firewall/.travis.yml b/firewall/.travis.yml new file mode 100644 index 000000000..0e94c2876 --- /dev/null +++ b/firewall/.travis.yml @@ -0,0 +1,29 @@ +--- +language: ruby +bundler_args: --without development +script: "bundle exec rake ci SPEC_OPTS='--format documentation'" +rvm: + - 1.8.7 + - 1.9.3 + - 2.0.0 +env: + - PUPPET_GEM_VERSION="~> 2.7.0" + - PUPPET_GEM_VERSION="~> 3.0.0" + - PUPPET_GEM_VERSION="~> 3.1.0" + - PUPPET_GEM_VERSION="~> 3.2.0" + - PUPPET_GEM_VERSION="~> 3.4.0" +matrix: + fast_finish: true + exclude: + - rvm: 1.9.3 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 2.7.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.0.0" + - rvm: 2.0.0 + env: PUPPET_GEM_VERSION="~> 3.1.0" + - rvm: 1.8.7 + env: PUPPET_GEM_VERSION="~> 3.2.0" +notifications: + email: false diff --git a/firewall/CHANGELOG.md b/firewall/CHANGELOG.md new file mode 100644 index 000000000..a6c93cfa2 --- /dev/null +++ b/firewall/CHANGELOG.md @@ -0,0 +1,390 @@ +## 2014-03-04 Supported Release 1.0.2 +###Summary + +This is a supported release. This release removes a testing symlink that can +cause trouble on systems where /var is on a seperate filesystem from the +modulepath. + +####Features +####Bugfixes +####Known Bugs + +* For Oracle, the `owner` and `socket` parameters require a workaround to function. Please see the Limitations section of the README. + +### Supported release - 2014-03-04 1.0.1 + +####Summary + +An important bugfix was made to the offset calculation for unmanaged rules +to handle rules with 9000+ in the name. + +####Features + +####Bugfixes +- Offset calculations assumed unmanaged rules were numbered 9000+. +- Gracefully fail to manage ip6tables on iptables 1.3.x + +####Known Bugs + +* For Oracle, the `owner` and `socket` parameters require a workaround to function. Please see the Limitations section of the README. + +--- +### 1.0.0 - 2014-02-11 + +No changes, just renumbering to 1.0.0. + +--- +### 0.5.0 - 2014-02-10 + +##### Summary: +This is a bigger release that brings in "recent" connection limiting (think +"port knocking"), firewall chain purging on a per-chain/per-table basis, and +support for a few other use cases. This release also fixes a major bug which +could cause modifications to the wrong rules when unmanaged rules are present. + +##### New Features: +* Add "recent" limiting via parameters `rdest`, `reap`, `recent`, `rhitcount`, + `rname`, `rseconds`, `rsource`, and `rttl` +* Add negation support for source and destination +* Add per-chain/table purging support to `firewallchain` +* IPv4 specific + * Add random port forwarding support + * Add ipsec policy matching via `ipsec_dir` and `ipsec_policy` +* IPv6 specific + * Add support for hop limiting via `hop_limit` parameter + * Add fragmentation matchers via `ishasmorefrags`, `islastfrag`, and `isfirstfrag` + * Add support for conntrack stateful firewall matching via `ctstate` + +##### Bugfixes: +- Boolean fixups allowing false values +- Better detection of unmanaged rules +- Fix multiport rule detection +- Fix sport/dport rule detection +- Make INPUT, OUTPUT, and FORWARD not autorequired for firewall chain filter +- Allow INPUT with the nat table +- Fix `src_range` & `dst_range` order detection +- Documentation clarifications +- Fixes to spec tests + +--------------------------------------- + +### 0.4.2 - 2013-09-10 + +Another attempt to fix the packaging issue. We think we understand exactly +what is failing and this should work properly for the first time. + +--------------------------------------- + +### 0.4.1 - 2013-08-09 + +Bugfix release to fix a packaging issue that may have caused puppet module +install commands to fail. + +--------------------------------------- + +### 0.4.0 - 2013-07-11 + +This release adds support for address type, src/dest ip ranges, and adds +additional testing and bugfixes. + +#### Features +* Add `src_type` and `dst_type` attributes (Nick Stenning) +* Add `src_range` and `dst_range` attributes (Lei Zhang) +* Add SL and SLC operatingsystems as supported (Steve Traylen) + +#### Bugfixes +* Fix parser for bursts other than 5 (Chris Rutter) +* Fix parser for -f in --comment (Georg Koester) +* Add doc headers to class files (Dan Carley) +* Fix lint warnings/errors (Wolf Noble) + +--------------------------------------- + +### 0.3.1 - 2013/6/10 + +This minor release provides some bugfixes and additional tests. + +#### Changes + +* Update tests for rspec-system-puppet 2 (Ken Barber) +* Update rspec-system tests for rspec-system-puppet 1.5 (Ken Barber) +* Ensure all services have 'hasstatus => true' for Puppet 2.6 (Ken Barber) +* Accept pre-existing rule with invalid name (Joe Julian) +* Swap log_prefix and log_level order to match the way it's saved (Ken Barber) +* Fix log test to replicate bug #182 (Ken Barber) +* Split argments while maintaining quoted strings (Joe Julian) +* Add more log param tests (Ken Barber) +* Add extra tests for logging parameters (Ken Barber) +* Clarify OS support (Ken Barber) + +--------------------------------------- + +### 0.3.0 - 2013/4/25 + +This release introduces support for Arch Linux and extends support for Fedora 15 and up. There are also lots of bugs fixed and improved testing to prevent regressions. + +##### Changes + +* Fix error reporting for insane hostnames (Tomas Doran) +* Support systemd on Fedora 15 and up (Eduardo Gutierrez) +* Move examples to docs (Ken Barber) +* Add support for Arch Linux platform (Ingmar Steen) +* Add match rule for fragments (Georg Koester) +* Fix boolean rules being recognized as changed (Georg Koester) +* Same rules now get deleted (Anastasis Andronidis) +* Socket params test (Ken Barber) +* Ensure parameter can disable firewall (Marc Tardif) + +--------------------------------------- + +### 0.2.1 - 2012/3/13 + +This maintenance release introduces the new README layout, and fixes a bug with iptables_persistent_version. + +##### Changes + +* (GH-139) Throw away STDERR from dpkg-query in Fact +* Update README to be consistent with module documentation template +* Fix failing spec tests due to dpkg change in iptables_persistent_version + +--------------------------------------- + +### 0.2.0 - 2012/3/3 + +This release introduces automatic persistence, removing the need for the previous manual dependency requirement for persistent the running rules to the OS persistence file. + +Previously you would have required the following in your site.pp (or some other global location): + + # Always persist firewall rules + exec { 'persist-firewall': + command => $operatingsystem ? { + 'debian' => '/sbin/iptables-save > /etc/iptables/rules.v4', + /(RedHat|CentOS)/ => '/sbin/iptables-save > /etc/sysconfig/iptables', + }, + refreshonly => true, + } + Firewall { + notify => Exec['persist-firewall'], + before => Class['my_fw::post'], + require => Class['my_fw::pre'], + } + Firewallchain { + notify => Exec['persist-firewall'], + } + resources { "firewall": + purge => true + } + +You only need: + + class { 'firewall': } + Firewall { + before => Class['my_fw::post'], + require => Class['my_fw::pre'], + } + +To install pre-requisites and to create dependencies on your pre & post rules. Consult the README for more information. + +##### Changes + +* Firewall class manifests (Dan Carley) +* Firewall and firewallchain persistence (Dan Carley) +* (GH-134) Autorequire iptables related packages (Dan Carley) +* Typo in #persist_iptables OS normalisation (Dan Carley) +* Tests for #persist_iptables (Dan Carley) +* (GH-129) Replace errant return in autoreq block (Dan Carley) + +--------------------------------------- + +### 0.1.1 - 2012/2/28 + +This release primarily fixes changing parameters in 3.x + +##### Changes + +* (GH-128) Change method_missing usage to define_method for 3.x compatibility +* Update travis.yml gem specifications to actually test 2.6 +* Change source in Gemfile to use a specific URL for Ruby 2.0.0 compatibility + +--------------------------------------- + +### 0.1.0 - 2012/2/24 + +This release is somewhat belated, so no summary as there are far too many changes this time around. Hopefully we won't fall this far behind again :-). + +##### Changes + +* Add support for MARK target and set-mark property (Johan Huysmans) +* Fix broken call to super for ruby-1.9.2 in munge (Ken Barber) +* simple fix of the error message for allowed values of the jump property (Daniel Black) +* Adding OSPF(v3) protocol to puppetlabs-firewall (Arnoud Vermeer) +* Display multi-value: port, sport, dport and state command seperated (Daniel Black) +* Require jump=>LOG for log params (Daniel Black) +* Reject and document icmp => "any" (Dan Carley) +* add firewallchain type and iptables_chain provider (Daniel Black) +* Various fixes for firewallchain resource (Ken Barber) +* Modify firewallchain name to be chain:table:protocol (Ken Barber) +* Fix allvalidchain iteration (Ken Barber) +* Firewall autorequire Firewallchains (Dan Carley) +* Tests and docstring for chain autorequire (Dan Carley) +* Fix README so setup instructions actually work (Ken Barber) +* Support vlan interfaces (interface containing ".") (Johan Huysmans) +* Add tests for VLAN support for iniface/outiface (Ken Barber) +* Add the table when deleting rules (Johan Huysmans) +* Fix tests since we are now prefixing -t) +* Changed 'jump' to 'action', commands to lower case (Jason Short) +* Support interface names containing "+" (Simon Deziel) +* Fix for when iptables-save spews out "FATAL" errors (Sharif Nassar) +* Fix for incorrect limit command arguments for ip6tables provider (Michael Hsu) +* Document Util::Firewall.host_to_ip (Dan Carley) +* Nullify addresses with zero prefixlen (Dan Carley) +* Add support for --tcp-flags (Thomas Vander Stichele) +* Make tcp_flags support a feature (Ken Barber) +* OUTPUT is a valid chain for the mangle table (Adam Gibbins) +* Enable travis-ci support (Ken Barber) +* Convert an existing test to CIDR (Dan Carley) +* Normalise iptables-save to CIDR (Dan Carley) +* be clearer about what distributions we support (Ken Barber) +* add gre protocol to list of acceptable protocols (Jason Hancock) +* Added pkttype property (Ashley Penney) +* Fix mark to not repeat rules with iptables 1.4.1+ (Sharif Nassar) +* Stub iptables_version for now so tests run on non-Linux hosts (Ken Barber) +* Stub iptables facts for set_mark tests (Dan Carley) +* Update formatting of README to meet Puppet Labs best practices (Will Hopper) +* Support for ICMP6 type code resolutions (Dan Carley) +* Insert order hash included chains from different tables (Ken Barber) +* rspec 2.11 compatibility (Jonathan Boyett) +* Add missing class declaration in README (sfozz) +* array_matching is contraindicated (Sharif Nassar) +* Convert port Fixnum into strings (Sharif Nassar) +* Update test framework to the modern age (Ken Barber) +* working with ip6tables support (wuwx) +* Remove gemfile.lock and add to gitignore (William Van Hevelingen) +* Update travis and gemfile to be like stdlib travis files (William Van Hevelingen) +* Add support for -m socket option (Ken Barber) +* Add support for single --sport and --dport parsing (Ken Barber) +* Fix tests for Ruby 1.9.3 from 3e13bf3 (Dan Carley) +* Mock Resolv.getaddress in #host_to_ip (Dan Carley) +* Update docs for source and dest - they are not arrays (Ken Barber) + +--------------------------------------- + +### 0.0.4 - 2011/12/05 + +This release adds two new parameters, 'uid' and 'gid'. As a part of the owner module, these params allow you to specify a uid, username, gid, or group got a match: + + firewall { '497 match uid': + port => '123', + proto => 'mangle', + chain => 'OUTPUT', + action => 'drop' + uid => '123' + } + +This release also adds value munging for the 'log_level', 'source', and 'destination' parameters. The 'source' and 'destination' now support hostnames: + + firewall { '498 accept from puppetlabs.com': + port => '123', + proto => 'tcp', + source => 'puppetlabs.com', + action => 'accept' + } + + +The 'log_level' parameter now supports using log level names, such as 'warn', 'debug', and 'panic': + + firewall { '499 logging': + port => '123', + proto => 'udp', + log_level => 'debug', + action => 'drop' + } + +Additional changes include iptables and ip6tables version facts, general whitespace cleanup, and adding additional unit tests. + +##### Changes + +* (#10957) add iptables_version and ip6tables_version facts +* (#11093) Improve log_level property so it converts names to numbers +* (#10723) Munge hostnames and IPs to IPs with CIDR +* (#10718) Add owner-match support +* (#10997) Add fixtures for ipencap +* (#11034) Whitespace cleanup +* (#10690) add port property support to ip6tables + +--------------------------------------- + +### 0.0.3 - 2011/11/12 + +This release introduces a new parameter 'port' which allows you to set both +source and destination ports for a match: + + firewall { "500 allow NTP requests": + port => "123", + proto => "udp", + action => "accept", + } + +We also have the limit parameter finally working: + + firewall { "500 limit HTTP requests": + dport => 80, + proto => tcp, + limit => "60/sec", + burst => 30, + action => accept, + } + +State ordering has been fixed now, and more characters are allowed in the +namevar: + +* Alphabetical +* Numbers +* Punctuation +* Whitespace + +##### Changes + +* (#10693) Ensure -m limit is added for iptables when using 'limit' param +* (#10690) Create new port property +* (#10700) allow additional characters in comment string +* (#9082) Sort iptables --state option values internally to keep it consistent across runs +* (#10324) Remove extraneous whitespace from iptables rule line in spec tests + +--------------------------------------- + +### 0.0.2 - 2011/10/26 + +This is largely a maintanence and cleanup release, but includes the ability to +specify ranges of ports in the sport/dport parameter: + + firewall { "500 allow port range": + dport => ["3000-3030","5000-5050"], + sport => ["1024-65535"], + action => "accept", + } + +##### Changes + +* (#10295) Work around bug #4248 whereby the puppet/util paths are not being loaded correctly on the puppetmaster +* (#10002) Change to dport and sport to handle ranges, and fix handling of name to name to port +* (#10263) Fix tests on Puppet 2.6.x +* (#10163) Cleanup some of the inline documentation and README file to align with general forge usage + +--------------------------------------- + +### 0.0.1 - 2011/10/18 + +Initial release. + +##### Changes + +* (#9362) Create action property and perform transformation for accept, drop, reject value for iptables jump parameter +* (#10088) Provide a customised version of CONTRIBUTING.md +* (#10026) Re-arrange provider and type spec files to align with Puppet +* (#10026) Add aliases for test,specs,tests to Rakefile and provide -T as default +* (#9439) fix parsing and deleting existing rules +* (#9583) Fix provider detection for gentoo and unsupported linuxes for the iptables provider +* (#9576) Stub provider so it works properly outside of Linux +* (#9576) Align spec framework with Puppet core +* and lots of other earlier development tasks ... diff --git a/firewall/CONTRIBUTING.md b/firewall/CONTRIBUTING.md new file mode 100644 index 000000000..5f0dbf14a --- /dev/null +++ b/firewall/CONTRIBUTING.md @@ -0,0 +1,292 @@ +Checklist (and a short version for the impatient) +================================================= + + * Commits: + + - Make commits of logical units. + + - Check for unnecessary whitespace with "git diff --check" before + committing. + + - Commit using Unix line endings (check the settings around "crlf" in + git-config(1)). + + - Do not check in commented out code or unneeded files. + + - The first line of the commit message should be a short + description (50 characters is the soft limit, excluding ticket + number(s)), and should skip the full stop. + + - Associated the Redmine ticket in the message. The first line + should include the ticket number in the form "(#XXXX) Rest of + message". + + - The body should provide a meaningful commit message, which: + + - uses the imperative, present tense: "change", not "changed" or + "changes". + + - includes motivation for the change, and contrasts its + implementation with the previous behavior. + + - Make sure that you have tests for the bug you are fixing, or + feature you are adding. + + - Make sure the test suite passes after your commit (rake spec unit). + + * Submission: + + * Pre-requisites: + + - Make sure you have a [Redmine account](http://projects.puppetlabs.com) + + - Sign the [Contributor License Agreement](https://projects.puppetlabs.com/contributor_licenses/sign) + + - [Create a ticket](http://projects.puppetlabs.com/projects/modules/issues/new), or [watch the ticket](http://projects.puppetlabs.com/projects/modules/issues) you are patching for. + + * Preferred method: + + - Fork the repository on GitHub. + + - Push your changes to a topic branch in your fork of the + repository. (the format ticket/1234-short_description_of_change is + usually preferred for this project). + + - Submit a pull request to the repository in the puppetlabs + organization. + +The long version +================ + + 0. Decide what to base your work on. + + In general, you should always base your work on the oldest + branch that your change is relevant to. + + - A bug fix should be based on the current stable series. If the + bug is not present in the current stable release, then base it on + `master`. + + - A new feature should be based on `master`. + + - Security fixes should be based on the current maintenance series + (that is, the previous stable series). If the security issue + was not present in the maintenance series, then it should be + based on the current stable series if it was introduced there, + or on `master` if it is not yet present in a stable release. + + 1. Make separate commits for logically separate changes. + + Please break your commits down into logically consistent units + which include new or changed tests relevent to the rest of the + change. The goal of doing this is to make the diff easier to + read for whoever is reviewing your code. In general, the easier + your diff is to read, the more likely someone will be happy to + review it and get it into the code base. + + If you're going to refactor a piece of code, please do so as a + separate commit from your feature or bug fix changes. + + We also really appreciate changes that include tests to make + sure the bug isn't re-introduced, and that the feature isn't + accidentally broken. + + Describe the technical detail of the change(s). If your + description starts to get too long, that's a good sign that you + probably need to split up your commit into more finely grained + pieces. + + Commits which plainly describe the things which help + reviewers check the patch and future developers understand the + code are much more likely to be merged in with a minimum of + bike-shedding or requested changes. Ideally, the commit message + would include information, and be in a form suitable for + inclusion in the release notes for the version of Puppet that + includes them. + + Please also check that you are not introducing any trailing + whitespaces or other "whitespace errors". You can do this by + running "git diff --check" on your changes before you commit. + + 2. Sign the Contributor License Agreement + + Before we can accept your changes, we do need a signed Puppet + Labs Contributor License Agreement (CLA). + + You can access the CLA via the + [Contributor License Agreement link](https://projects.puppetlabs.com/contributor_licenses/sign) + in the top menu bar of our Redmine instance. Once you've signed + the CLA, a badge will show up next to your name on the + [Puppet Project Overview Page](http://projects.puppetlabs.com/projects/modules?jump=welcome), + and your name will be listed under "Contributor License Signers" + section. + + If you have any questions about the CLA, please feel free to + contact Puppet Labs via email at cla-submissions@puppetlabs.com. + + 3. Sending your patches + + We accept multiple ways of submitting your changes for + inclusion. They are listed below in order of preference. + + Please keep in mind that any method that involves sending email + to the mailing list directly requires you to be subscribed to + the mailing list, and that your first post to the list will be + held in a moderation queue. + + * GitHub Pull Requests + + To submit your changes via a GitHub pull request, we _highly_ + recommend that you have them on a topic branch, instead of + directly on "master" or one of the release, or RC branches. + It makes things much easier to keep track of, especially if + you decide to work on another thing before your first change + is merged in. + + GitHub has some pretty good + [general documentation](http://help.github.com/) on using + their site. They also have documentation on + [creating pull requests](http://help.github.com/send-pull-requests/). + + In general, after pushing your topic branch up to your + repository on GitHub, you'll switch to the branch in the + GitHub UI and click "Pull Request" towards the top of the page + in order to open a pull request. + + You'll want to make sure that you have the appropriate + destination branch in the repository under the puppetlabs + organization. This should be the same branch that you based + your changes off of. + + * Other pull requests + + If you already have a publicly accessible version of the + repository hosted elsewhere, and don't wish to or cannot use + GitHub, you can submit your change by requesting that we pull + the changes from your repository by sending an email to the + puppet-dev Google Groups mailing list. + + `git-request-pull(1)` provides a handy way to generate the text + for the email requesting that we pull your changes (and does + some helpful sanity checks in the process). + + * Mailing patches to the mailing list + + If neither of the previous methods works for you, then you can + also mail the patches inline to the puppet-dev Google Group + using either `rake mail_patches`, or by using + `git-format-patch(1)`, and `git-send-email(1)` directly. + + `rake mail_patches` handles setting the appropriate flags to + `git-format-patch(1)` and `git-send-email(1)` for you, but + doesn't allow adding any commentary between the '---', and the + diffstat in the resulting email. It also requires that you + have created your topic branch in the form + `//`. + + If you decide to use `git-format-patch(1)` and + `git-send-email(1)` directly, please be sure to use the + following flags for `git-format-patch(1)`: -C -M -s -n + --subject-prefix='PATCH/puppet' + + * Attaching patches to Redmine + + As a method of last resort you can also directly attach the + output of `git-format-patch(1)`, or `git-diff(1)` to a Redmine + ticket. + + If you are generating the diff outside of Git, please be sure + to generate a unified diff. + + 4. Update the related Redmine ticket. + + If there's a Redmine ticket associated with the change you + submitted, then you should update the ticket to include the + location of your branch, and change the status to "In Topic + Branch Pending Merge", along with any other commentary you may + wish to make. + +How to track the status of your change after it's been submitted +================================================================ + +Shortly after opening a pull request on GitHub, there should be an +automatic message sent to the puppet-dev Google Groups mailing list +notifying people of this. This notification is used to let the Puppet +development community know about your requested change to give them a +chance to review, test, and comment on the change(s). + +If you submitted your change via manually sending a pull request or +mailing the patches, then we keep track of these using +[patchwork](https://patchwork.puppetlabs.com). When code is merged +into the project it is automatically removed from patchwork, and the +Redmine ticket is manually updated with the commit SHA1. In addition, +the ticket status must be updated by the person who merges the topic +branch to a status of "Merged - Pending Release" + +We do our best to comment on or merge submitted changes within a week. +However, if there hasn't been any commentary on the pull request or +mailed patches, and it hasn't been merged in after a week, then feel +free to ask for an update by replying on the mailing list to the +automatic notification or mailed patches. It probably wasn't +intentional, and probably just slipped through the cracks. + +Additional Resources +==================== + +* [Getting additional help](http://projects.puppetlabs.com/projects/puppet/wiki/Getting_Help) + +* [Writing tests](http://projects.puppetlabs.com/projects/puppet/wiki/Development_Writing_Tests) + +* [Bug tracker (Redmine)](http://projects.puppetlabs.com/projects/modules) + +* [Patchwork](https://patchwork.puppetlabs.com) + +* [Contributor License Agreement](https://projects.puppetlabs.com/contributor_licenses/sign) + +* [General GitHub documentation](http://help.github.com/) + +* [GitHub pull request documentation](http://help.github.com/send-pull-requests/) + +If you have commit access to the repository +=========================================== + +Even if you have commit access to the repository, you'll still need to +go through the process above, and have someone else review and merge +in your changes. The rule is that all changes must be reviewed by a +developer on the project (that didn't write the code) to ensure that +all changes go through a code review process. + +Having someone other than the author of the topic branch recorded as +performing the merge is the record that they performed the code +review. + + * Merging topic branches + + When merging code from a topic branch into the integration branch + (Ex: master, 2.7.x, 1.6.x, etc.), there should always be a merge + commit. You can accomplish this by always providing the `--no-ff` + flag to `git merge`. + + git merge --no-ff --log ticket/1234-fix-something-broken + + The reason for always forcing this merge commit is that it + provides a consistent way to look up what changes & commits were + in a topic branch, whether that topic branch had one, or 500 + commits. For example, if the merge commit had an abbreviated + SHA-1 of `coffeebad`, then you could use the following `git log` + invocation to show you which commits it brought in: + + git log coffeebad^1..coffeebad^2 + + The following would show you which changes were made on the topic + branch: + + git diff coffeebad^1...coffeebad^2 + + Because we _always_ merge the topic branch into the integration + branch the first parent (`^1`) of a merge commit will be the most + recent commit on the integration branch from just before we merged + in the topic, and the second parent (`^2`) will always be the most + recent commit that was made in the topic branch. This also serves + as the record of who performed the code review, as mentioned + above. diff --git a/firewall/Gemfile b/firewall/Gemfile new file mode 100644 index 000000000..9e6eaa541 --- /dev/null +++ b/firewall/Gemfile @@ -0,0 +1,18 @@ +source ENV['GEM_SOURCE'] || "https://rubygems.org" + +group :development, :test do + gem 'puppetlabs_spec_helper', :require => false + gem 'rspec-puppet', :require => false + gem 'serverspec', :require => false + gem 'beaker-rspec', :require => false + gem 'puppet-lint', :require => false + gem 'pry', :require => false +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/firewall/LICENSE b/firewall/LICENSE new file mode 100644 index 000000000..1d196fc30 --- /dev/null +++ b/firewall/LICENSE @@ -0,0 +1,25 @@ +Puppet Firewall Module - Puppet module for managing Firewalls + +Copyright (C) 2011-2013 Puppet Labs, Inc. +Copyright (C) 2011 Jonathan Boyett +Copyright (C) 2011 Media Temple, Inc. + +Some of the iptables code was taken from puppet-iptables which was: + +Copyright (C) 2011 Bob.sh Limited +Copyright (C) 2008 Camptocamp Association +Copyright (C) 2007 Dmitri Priimak + +Puppet Labs can be contacted at: info@puppetlabs.com + +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. diff --git a/firewall/Modulefile b/firewall/Modulefile new file mode 100644 index 000000000..45831729e --- /dev/null +++ b/firewall/Modulefile @@ -0,0 +1,8 @@ +name 'puppetlabs-firewall' +version '1.0.2' +source 'git://github.com/puppetlabs/puppetlabs-firewall.git' +author 'puppetlabs' +license 'ASL 2.0' +summary 'Firewall Module' +description 'Manages Firewalls such as iptables' +project_page 'http://forge.puppetlabs.com/puppetlabs/firewall' diff --git a/firewall/README.markdown b/firewall/README.markdown new file mode 100644 index 000000000..9e44733a4 --- /dev/null +++ b/firewall/README.markdown @@ -0,0 +1,429 @@ +#firewall + +[![Build Status](https://travis-ci.org/puppetlabs/puppetlabs-firewall.png?branch=master)](https://travis-ci.org/puppetlabs/puppetlabs-firewall) + +####Table of Contents + +1. [Overview - What is the Firewall module?](#overview) +2. [Module Description - What does the module do?](#module-description) +3. [Setup - The basics of getting started with Firewall](#setup) + * [What Firewall affects](#what-firewall-affects) + * [Setup Requirements](#setup-requirements) + * [Beginning with Firewall](#beginning-with-firewall) + * [Upgrading](#upgrading) +4. [Usage - Configuration and customization options](#usage) + * [Default rules - Setting up general configurations for all firewalls](#default-rules) + * [Application-specific rules - Options for configuring and managing firewalls across applications](#application-specific-rules) + * [Other Rules](#other-rules) +5. [Reference - An under-the-hood peek at what the module is doing](#reference) +6. [Limitations - OS compatibility, etc.](#limitations) +7. [Development - Guide for contributing to the module](#development) + * [Tests - Testing your configuration](#tests) + +##Overview + +The Firewall module lets you manage firewall rules with Puppet. + +##Module Description + +PuppetLabs' Firewall introduces the resource `firewall`, which is used to manage and configure firewall rules from within the Puppet DSL. This module offers support for iptables, ip6tables, and ebtables. + +The module also introduces the resource `firewallchain`, which allows you to manage chains or firewall lists. At the moment, only iptables and ip6tables chains are supported. + +##Setup + +###What Firewall affects: + +* every node running a firewall +* system's firewall settings +* connection settings for managed nodes +* unmanaged resources (get purged) +* site.pp + +###Setup Requirements + +Firewall uses Ruby-based providers, so you must have [pluginsync enabled](http://docs.puppetlabs.com/guides/plugins_in_modules.html#enabling-pluginsync). + +###Beginning with Firewall + +To begin, you need to provide some initial top-scope configuration to ensure your firewall configurations are ordered properly and you do not lock yourself out of your box or lose any configuration. + +Persistence of rules between reboots is handled automatically, although there are known issues with ip6tables on older Debian/Ubuntu, as well as known issues with ebtables. + +In your `site.pp` (or some similarly top-scope file), set up a metatype to purge unmanaged firewall resources. This will clear any existing rules and make sure that only rules defined in Puppet exist on the machine. + + resources { "firewall": + purge => true + } + +Next, set up the default parameters for all of the firewall rules you will be establishing later. These defaults will ensure that the pre and post classes (you will be setting up in just a moment) are run in the correct order to avoid locking you out of your box during the first puppet run. + + Firewall { + before => Class['my_fw::post'], + require => Class['my_fw::pre'], + } + +You also need to declare the `my_fw::pre` & `my_fw::post` classes so that dependencies are satisfied. This can be achieved using an External Node Classifier or the following + + class { ['my_fw::pre', 'my_fw::post']: } + +Finally, you should include the `firewall` class to ensure the correct packages are installed. + + class { 'firewall': } + +Now to create the `my_fw::pre` and `my_fw::post` classes. Firewall acts on your running firewall, making immediate changes as the catalog executes. Defining default pre and post rules allows you provide global defaults for your hosts before and after any custom rules; it is also required to avoid locking yourself out of your own boxes when Puppet runs. This approach employs a whitelist setup, so you can define what rules you want and everything else is ignored rather than removed. + +The `pre` class should be located in `my_fw/manifests/pre.pp` and should contain any default rules to be applied first. + + class my_fw::pre { + Firewall { + require => undef, + } + + # Default firewall rules + firewall { '000 accept all icmp': + proto => 'icmp', + action => 'accept', + }-> + firewall { '001 accept all to lo interface': + proto => 'all', + iniface => 'lo', + action => 'accept', + }-> + firewall { '002 accept related established rules': + proto => 'all', + ctstate => ['RELATED', 'ESTABLISHED'], + action => 'accept', + } + } + +The rules in `pre` should allow basic networking (such as ICMP and TCP), as well as ensure that existing connections are not closed. + +The `post` class should be located in `my_fw/manifests/post.pp` and include any default rules to be applied last. + + class my_fw::post { + firewall { '999 drop all': + proto => 'all', + action => 'drop', + before => undef, + } + } + +To put it all together: the `require` parameter in `Firewall {}` ensures `my_fw::pre` is run before any other rules and the `before` parameter ensures `my_fw::post` is run after any other rules. So the run order is: + +* run the rules in `my_fw::pre` +* run your rules (defined in code) +* run the rules in `my_fw::post` + +###Upgrading + +####Upgrading from version 0.2.0 and newer + +Upgrade the module with the puppet module tool as normal: + + puppet module upgrade puppetlabs/firewall + +####Upgrading from version 0.1.1 and older + +Start by upgrading the module using the puppet module tool: + + puppet module upgrade puppetlabs/firewall + +Previously, you would have required the following in your `site.pp` (or some other global location): + + # Always persist firewall rules + exec { 'persist-firewall': + command => $operatingsystem ? { + 'debian' => '/sbin/iptables-save > /etc/iptables/rules.v4', + /(RedHat|CentOS)/ => '/sbin/iptables-save > /etc/sysconfig/iptables', + }, + refreshonly => true, + } + Firewall { + notify => Exec['persist-firewall'], + before => Class['my_fw::post'], + require => Class['my_fw::pre'], + } + Firewallchain { + notify => Exec['persist-firewall'], + } + resources { "firewall": + purge => true + } + +With the latest version, we now have in-built persistence, so this is no longer needed. However, you will still need some basic setup to define pre & post rules. + + resources { "firewall": + purge => true + } + Firewall { + before => Class['my_fw::post'], + require => Class['my_fw::pre'], + } + class { ['my_fw::pre', 'my_fw::post']: } + class { 'firewall': } + +Consult the the documentation below for more details around the classes `my_fw::pre` and `my_fw::post`. + +##Usage + +There are two kinds of firewall rules you can use with Firewall: default rules and application-specific rules. Default rules apply to general firewall settings, whereas application-specific rules manage firewall settings of a specific application, node, etc. + +All rules employ a numbering system in the resource's title that is used for ordering. When titling your rules, make sure you prefix the rule with a number. + + 000 this runs first + 999 this runs last + +###Default rules + +You can place default rules in either `my_fw::pre` or `my_fw::post`, depending on when you would like them to run. Rules placed in the `pre` class will run first, rules in the `post` class, last. + +Depending on the provider, the title of the rule can be stored using the comment feature of the underlying firewall subsystem. Values can match `/^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/`. + +####Examples of default rules + +Basic accept ICMP request example: + + firewall { "000 accept all icmp requests": + proto => "icmp", + action => "accept", + } + +Drop all: + + firewall { "999 drop all other requests": + action => "drop", + } + +###Application-specific rules + +Puppet doesn't care where you define rules, and this means that you can place +your firewall resources as close to the applications and services that you +manage as you wish. If you use the [roles and profiles +pattern](https://puppetlabs.com/learn/roles-profiles-introduction) then it +would make sense to create your firewall rules in the profiles, so that they +remain close to the services managed by the profile. + +An example of this might be: + +```puppet +class profile::apache { + include apache + apache::vhost { 'mysite': ensure => present } + + firewall { '100 allow http and https access': + port => [80, 443], + proto => tcp, + action => accept, + } +} +``` + + +However, if you're not using that pattern then you can place them directly into +the individual module that manages a service, such as: + +```puppet +class apache { + firewall { '100 allow http and https access': + port => [80, 443], + proto => tcp, + action => accept, + } + # ... the rest of your code ... +} +``` + +This means if someone includes either the profile: + +```puppet +include profile::apache +``` + +Or the module, if you're not using roles and profiles: + +```puppet + include ::apache +``` + +Then they would automatically get appropriate firewall rules. + +###Other rules + +You can also apply firewall rules to specific nodes. Usually, you will want to put the firewall rule in another class and apply that class to a node. But you can apply a rule to a node. + + node 'foo.bar.com' { + firewall { '111 open port 111': + dport => 111 + } + } + +You can also do more complex things with the `firewall` resource. Here we are doing some NAT configuration. + + firewall { '100 snat for network foo2': + chain => 'POSTROUTING', + jump => 'MASQUERADE', + proto => 'all', + outiface => "eth0", + source => '10.1.2.0/24', + table => 'nat', + } + +In the below example, we are creating a new chain and forwarding any port 5000 access to it. + + firewall { '100 forward to MY_CHAIN': + chain => 'INPUT', + jump => 'MY_CHAIN', + } + # The namevar here is in the format chain_name:table:protocol + firewallchain { 'MY_CHAIN:filter:IPv4': + ensure => present, + } + firewall { '100 my rule': + chain => 'MY_CHAIN', + action => 'accept', + proto => 'tcp', + dport => 5000, + } + +###Additional Information + +You can access the inline documentation: + + puppet describe firewall + +Or + + puppet doc -r type + (and search for firewall) + +##Reference + +Classes: + +* [firewall](#class-firewall) + +Types: + +* [firewall](#type-firewall) +* [firewallchain](#type-firewallchain) + +Facts: + +* [ip6tables_version](#fact-ip6tablesversion) +* [iptables_version](#fact-iptablesversion) +* [iptables_persistent_version](#fact-iptablespersistentversion) + +###Class: firewall + +This class is provided to do the basic setup tasks required for using the firewall resources. + +At the moment this takes care of: + +* iptables-persistent package installation + +You should include the class for nodes that need to use the resources in this module. For example + + class { 'firewall': } + +####`ensure` + +Indicates the state of `iptables` on your system, allowing you to disable `iptables` if desired. + +Can either be `running` or `stopped`. Default to `running`. + +###Type: firewall + +This type provides the capability to manage firewall rules within puppet. + +For more documentation on the type, access the 'Types' tab on the Puppet Labs Forge: + + + +###Type:: firewallchain + +This type provides the capability to manage rule chains for firewalls. + +For more documentation on the type, access the 'Types' tab on the Puppet Labs Forge: + + + +###Fact: ip6tables_version + +The module provides a Facter fact that can be used to determine what the default version of ip6tables is for your operating system/distribution. + +###Fact: iptables_version + +The module provides a Facter fact that can be used to determine what the default version of iptables is for your operating system/distribution. + +###Fact: iptables_persistent_version + +Retrieves the version of iptables-persistent from your OS. This is a Debian/Ubuntu specific fact. + +##Limitations + +###SLES + +The `socket` parameter is not supported on SLES. In this release it will cause +the catalog to fail with iptables failures, rather than correctly warn you that +the features are unusable. + +###Oracle Enterprise Linux + +The `socket` and `owner` parameters are unsupported on Oracle Enterprise Linux +when the "Unbreakable" kernel is used. These may function correctly when using +the stock RedHat kernel instead. Declaring either of these parameters on an +unsupported system will result in iptable rules failing to apply. + +###Other + +Bugs can be reported using Github Issues: + + + +##Development + +Puppet Labs modules on the Puppet Forge are open projects, and community contributions are essential for keeping them great. We can’t access the huge number of platforms and myriad of hardware, software, and deployment configurations that Puppet is intended to serve. + +We want to keep it as easy as possible to contribute changes so that our modules work in your environment. There are a few guidelines that we need contributors to follow so that we can have a chance of keeping on top of things. + +You can read the complete module contribution guide [on the Puppet Labs wiki.](http://projects.puppetlabs.com/projects/module-site/wiki/Module_contributing) + +For this particular module, please also read CONTRIBUTING.md before contributing. + +Currently we support: + +* iptables +* ip6tables +* ebtables (chains only) + +But plans are to support lots of other firewall implementations: + +* FreeBSD (ipf) +* Mac OS X (ipfw) +* OpenBSD (pf) +* Cisco (ASA and basic access lists) + +If you have knowledge in these technologies, know how to code, and wish to contribute to this project, we would welcome the help. + +###Testing + +Make sure you have: + +* rake +* bundler + +Install the necessary gems: + + bundle install + +And run the tests from the root of the source code: + + rake test + +If you have a copy of Vagrant 1.1.0 you can also run the system tests: + + RSPEC_SET=debian-606-x64 rake spec:system + RSPEC_SET=centos-58-x64 rake spec:system + +*Note:* system testing is fairly alpha at this point, your mileage may vary. diff --git a/firewall/Rakefile b/firewall/Rakefile new file mode 100644 index 000000000..8b1207030 --- /dev/null +++ b/firewall/Rakefile @@ -0,0 +1,14 @@ +require 'puppetlabs_spec_helper/rake_tasks' + +require 'puppet-lint/tasks/puppet-lint' +PuppetLint.configuration.ignore_paths = ['vendor/**/*.pp'] + +task :default do + sh %{rake -T} +end + +desc 'Run reasonably quick tests for CI' +task :ci => [ + :lint, + :spec, +] diff --git a/firewall/lib/facter/ip6tables_version.rb b/firewall/lib/facter/ip6tables_version.rb new file mode 100644 index 000000000..3dce27f70 --- /dev/null +++ b/firewall/lib/facter/ip6tables_version.rb @@ -0,0 +1,11 @@ +Facter.add(:ip6tables_version) do + confine :kernel => :linux + setcode do + version = Facter::Util::Resolution.exec('ip6tables --version') + if version + version.match(/\d+\.\d+\.\d+/).to_s + else + nil + end + end +end diff --git a/firewall/lib/facter/iptables_persistent_version.rb b/firewall/lib/facter/iptables_persistent_version.rb new file mode 100644 index 000000000..80bf9dea1 --- /dev/null +++ b/firewall/lib/facter/iptables_persistent_version.rb @@ -0,0 +1,15 @@ +Facter.add(:iptables_persistent_version) do + confine :operatingsystem => %w{Debian Ubuntu} + setcode do + # Throw away STDERR because dpkg >= 1.16.7 will make some noise if the + # package isn't currently installed. + cmd = "dpkg-query -Wf '${Version}' iptables-persistent 2>/dev/null" + version = Facter::Util::Resolution.exec(cmd) + + if version.nil? or !version.match(/\d+\.\d+/) + nil + else + version + end + end +end diff --git a/firewall/lib/facter/iptables_version.rb b/firewall/lib/facter/iptables_version.rb new file mode 100644 index 000000000..6f7ae5647 --- /dev/null +++ b/firewall/lib/facter/iptables_version.rb @@ -0,0 +1,11 @@ +Facter.add(:iptables_version) do + confine :kernel => :linux + setcode do + version = Facter::Util::Resolution.exec('iptables --version') + if version + version.match(/\d+\.\d+\.\d+/).to_s + else + nil + end + end +end diff --git a/firewall/lib/puppet/provider/firewall.rb b/firewall/lib/puppet/provider/firewall.rb new file mode 100644 index 000000000..c6b0b10bb --- /dev/null +++ b/firewall/lib/puppet/provider/firewall.rb @@ -0,0 +1,34 @@ +class Puppet::Provider::Firewall < Puppet::Provider + + # Prefetch our rule list. This is ran once every time before any other + # action (besides initialization of each object). + def self.prefetch(resources) + debug("[prefetch(resources)]") + instances.each do |prov| + if resource = resources[prov.name] || resources[prov.name.downcase] + resource.provider = prov + end + end + end + + # Look up the current status. This allows us to conventiently look up + # existing status with properties[:foo]. + def properties + if @property_hash.empty? + @property_hash = query || {:ensure => :absent} + @property_hash[:ensure] = :absent if @property_hash.empty? + end + @property_hash.dup + end + + # Pull the current state of the list from the full list. We're + # getting some double entendre here.... + def query + self.class.instances.each do |instance| + if instance.name == self.name or instance.name.downcase == self.name + return instance.properties + end + end + nil + end +end diff --git a/firewall/lib/puppet/provider/firewall/ip6tables.rb b/firewall/lib/puppet/provider/firewall/ip6tables.rb new file mode 100644 index 000000000..d53163371 --- /dev/null +++ b/firewall/lib/puppet/provider/firewall/ip6tables.rb @@ -0,0 +1,131 @@ +Puppet::Type.type(:firewall).provide :ip6tables, :parent => :iptables, :source => :iptables do + @doc = "Ip6tables type provider" + + has_feature :iptables + has_feature :hop_limiting + has_feature :rate_limiting + has_feature :recent_limiting + has_feature :snat + has_feature :dnat + has_feature :interface_match + has_feature :icmp_match + has_feature :owner + has_feature :state_match + has_feature :reject_type + has_feature :log_level + has_feature :log_prefix + has_feature :mark + has_feature :tcp_flags + has_feature :pkttype + has_feature :ishasmorefrags + has_feature :islastfrag + has_feature :isfirstfrag + + optional_commands({ + :ip6tables => 'ip6tables', + :ip6tables_save => 'ip6tables-save', + }) + + def initialize(*args) + if Facter.fact('ip6tables_version').value.match /1\.3\.\d/ + raise ArgumentError, 'The ip6tables provider is not supported on version 1.3 of iptables' + else + super + end + end + + def self.iptables(*args) + ip6tables(*args) + end + + def self.iptables_save(*args) + ip6tables_save(*args) + end + + @protocol = "IPv6" + + @resource_map = { + :burst => "--limit-burst", + :ctstate => "-m conntrack --ctstate", + :destination => "-d", + :dport => "-m multiport --dports", + :gid => "-m owner --gid-owner", + :icmp => "-m icmp6 --icmpv6-type", + :iniface => "-i", + :jump => "-j", + :hop_limit => "-m hl --hl-eq", + :limit => "-m limit --limit", + :log_level => "--log-level", + :log_prefix => "--log-prefix", + :name => "-m comment --comment", + :outiface => "-o", + :port => '-m multiport --ports', + :proto => "-p", + :rdest => "--rdest", + :reap => "--reap", + :recent => "-m recent", + :reject => "--reject-with", + :rhitcount => "--hitcount", + :rname => "--name", + :rseconds => "--seconds", + :rsource => "--rsource", + :rttl => "--rttl", + :source => "-s", + :state => "-m state --state", + :sport => "-m multiport --sports", + :table => "-t", + :todest => "--to-destination", + :toports => "--to-ports", + :tosource => "--to-source", + :uid => "-m owner --uid-owner", + :pkttype => "-m pkttype --pkt-type", + :ishasmorefrags => "-m frag --fragid 0 --fragmore", + :islastfrag => "-m frag --fragid 0 --fraglast", + :isfirstfrag => "-m frag --fragid 0 --fragfirst", + } + + # These are known booleans that do not take a value, but we want to munge + # to true if they exist. + @known_booleans = [:ishasmorefrags, :islastfrag, :isfirstfrag, :rsource, :rdest, :reap, :rttl] + + # Create property methods dynamically + (@resource_map.keys << :chain << :table << :action).each do |property| + if @known_booleans.include?(property) then + # The boolean properties default to '' which should be read as false + define_method "#{property}" do + @property_hash[property] = :false if @property_hash[property] == nil + @property_hash[property.to_sym] + end + else + define_method "#{property}" do + @property_hash[property.to_sym] + end + end + + if property == :chain + define_method "#{property}=" do |value| + if @property_hash[:chain] != value + raise ArgumentError, "Modifying the chain for existing rules is not supported." + end + end + else + define_method "#{property}=" do |value| + @property_hash[:needs_change] = true + end + end + end + + # This is the order of resources as they appear in iptables-save output, + # we need it to properly parse and apply rules, if the order of resource + # changes between puppet runs, the changed rules will be re-applied again. + # This order can be determined by going through iptables source code or just tweaking and trying manually + # (Note: on my CentOS 6.4 ip6tables-save returns -m frag on the place + # I put it when calling the command. So compability with manual changes + # not provided with current parser [georg.koester]) + @resource_list = [:table, :source, :destination, :iniface, :outiface, + :proto, :ishasmorefrags, :islastfrag, :isfirstfrag, :gid, :uid, :sport, :dport, + :port, :pkttype, :name, :state, :ctstate, :icmp, :hop_limit, :limit, :burst, + :recent, :rseconds, :reap, :rhitcount, :rttl, :rname, :rsource, :rdest, + :jump, :todest, :tosource, :toports, :log_level, :log_prefix, :reject] + +end diff --git a/firewall/lib/puppet/provider/firewall/iptables.rb b/firewall/lib/puppet/provider/firewall/iptables.rb new file mode 100644 index 000000000..698e731c9 --- /dev/null +++ b/firewall/lib/puppet/provider/firewall/iptables.rb @@ -0,0 +1,494 @@ +require 'puppet/provider/firewall' +require 'digest/md5' + +Puppet::Type.type(:firewall).provide :iptables, :parent => Puppet::Provider::Firewall do + include Puppet::Util::Firewall + + @doc = "Iptables type provider" + + has_feature :iptables + has_feature :rate_limiting + has_feature :recent_limiting + has_feature :snat + has_feature :dnat + has_feature :interface_match + has_feature :icmp_match + has_feature :owner + has_feature :state_match + has_feature :reject_type + has_feature :log_level + has_feature :log_prefix + has_feature :mark + has_feature :tcp_flags + has_feature :pkttype + has_feature :isfragment + has_feature :socket + has_feature :address_type + has_feature :iprange + has_feature :ipsec_dir + has_feature :ipsec_policy + + optional_commands({ + :iptables => 'iptables', + :iptables_save => 'iptables-save', + }) + + defaultfor :kernel => :linux + + iptables_version = Facter.fact('iptables_version').value + if (iptables_version and Puppet::Util::Package.versioncmp(iptables_version, '1.4.1') < 0) + mark_flag = '--set-mark' + else + mark_flag = '--set-xmark' + end + + @protocol = "IPv4" + + @resource_map = { + :burst => "--limit-burst", + :ctstate => "-m conntrack --ctstate", + :destination => "-d", + :dst_type => "-m addrtype --dst-type", + :dst_range => "-m iprange --dst-range", + :dport => ["-m multiport --dports", "--dport"], + :gid => "-m owner --gid-owner", + :icmp => "-m icmp --icmp-type", + :iniface => "-i", + :jump => "-j", + :limit => "-m limit --limit", + :log_level => "--log-level", + :log_prefix => "--log-prefix", + :name => "-m comment --comment", + :outiface => "-o", + :port => '-m multiport --ports', + :proto => "-p", + :random => "--random", + :rdest => "--rdest", + :reap => "--reap", + :recent => "-m recent", + :reject => "--reject-with", + :rhitcount => "--hitcount", + :rname => "--name", + :rseconds => "--seconds", + :rsource => "--rsource", + :rttl => "--rttl", + :set_mark => mark_flag, + :socket => "-m socket", + :source => "-s", + :src_type => "-m addrtype --src-type", + :src_range => "-m iprange --src-range", + :sport => ["-m multiport --sports", "--sport"], + :state => "-m state --state", + :table => "-t", + :tcp_flags => "-m tcp --tcp-flags", + :todest => "--to-destination", + :toports => "--to-ports", + :tosource => "--to-source", + :uid => "-m owner --uid-owner", + :pkttype => "-m pkttype --pkt-type", + :isfragment => "-f", + :ipsec_dir => "-m policy --dir", + :ipsec_policy => "--pol", + } + + # These are known booleans that do not take a value, but we want to munge + # to true if they exist. + @known_booleans = [ + :isfragment, + :random, + :rdest, + :reap, + :rsource, + :rttl, + :socket + ] + + + # Create property methods dynamically + (@resource_map.keys << :chain << :table << :action).each do |property| + if @known_booleans.include?(property) then + # The boolean properties default to '' which should be read as false + define_method "#{property}" do + @property_hash[property] = :false if @property_hash[property] == nil + @property_hash[property.to_sym] + end + else + define_method "#{property}" do + @property_hash[property.to_sym] + end + end + + if property == :chain + define_method "#{property}=" do |value| + if @property_hash[:chain] != value + raise ArgumentError, "Modifying the chain for existing rules is not supported." + end + end + else + define_method "#{property}=" do |value| + @property_hash[:needs_change] = true + end + end + end + + # This is the order of resources as they appear in iptables-save output, + # we need it to properly parse and apply rules, if the order of resource + # changes between puppet runs, the changed rules will be re-applied again. + # This order can be determined by going through iptables source code or just tweaking and trying manually + @resource_list = [ + :table, :source, :destination, :iniface, :outiface, :proto, :isfragment, + :src_range, :dst_range, :tcp_flags, :gid, :uid, :sport, :dport, :port, + :dst_type, :src_type, :socket, :pkttype, :name, :ipsec_dir, :ipsec_policy, + :state, :ctstate, :icmp, :limit, :burst, :recent, :rseconds, :reap, + :rhitcount, :rttl, :rname, :rsource, :rdest, :jump, :todest, :tosource, + :toports, :random, :log_prefix, :log_level, :reject, :set_mark + ] + + def insert + debug 'Inserting rule %s' % resource[:name] + iptables insert_args + end + + def update + debug 'Updating rule %s' % resource[:name] + iptables update_args + end + + def delete + debug 'Deleting rule %s' % resource[:name] + iptables delete_args + end + + def exists? + properties[:ensure] != :absent + end + + # Flush the property hash once done. + def flush + debug("[flush]") + if @property_hash.delete(:needs_change) + notice("Properties changed - updating rule") + update + end + persist_iptables(self.class.instance_variable_get(:@protocol)) + @property_hash.clear + end + + def self.instances + debug "[instances]" + table = nil + rules = [] + counter = 1 + + # String#lines would be nice, but we need to support Ruby 1.8.5 + iptables_save.split("\n").each do |line| + unless line =~ /^\#\s+|^\:\S+|^COMMIT|^FATAL/ + if line =~ /^\*/ + table = line.sub(/\*/, "") + else + if hash = rule_to_hash(line, table, counter) + rules << new(hash) + counter += 1 + end + end + end + end + rules + end + + def self.rule_to_hash(line, table, counter) + hash = {} + keys = [] + values = line.dup + + #################### + # PRE-PARSE CLUDGING + #################### + + # --tcp-flags takes two values; we cheat by adding " around it + # so it behaves like --comment + values = values.sub(/--tcp-flags (\S*) (\S*)/, '--tcp-flags "\1 \2"') + # we do a similar thing for negated address masks (source and destination). + values = values.sub(/(-\S+) (!)\s?(\S*)/,'\1 "\2 \3"') + # the actual rule will have the ! mark before the option. + values = values.sub(/(!)\s*(-\S+)\s*(\S*)/, '\2 "\1 \3"') + # The match extension for tcp & udp are optional and throws off the @resource_map. + values = values.sub(/-m (tcp|udp) (--(s|d)port|-m multiport)/, '\2') + + # Trick the system for booleans + @known_booleans.each do |bool| + # append "true" because all params are expected to have values + if bool == :isfragment then + # -f requires special matching: + # only replace those -f that are not followed by an l to + # distinguish between -f and the '-f' inside of --tcp-flags. + values = values.sub(/-f(?!l)(?=.*--comment)/, '-f true') + else + values = values.sub(/#{@resource_map[bool]}/, "#{@resource_map[bool]} true") + end + end + + ############ + # Populate parser_list with used value, in the correct order + ############ + map_index={} + @resource_map.each_pair do |map_k,map_v| + [map_v].flatten.each do |v| + ind=values.index(/\s#{v}/) + next unless ind + map_index[map_k]=ind + end + end + # Generate parser_list based on the index of the found option + parser_list=[] + map_index.sort_by{|k,v| v}.each{|mapi| parser_list << mapi.first } + + ############ + # MAIN PARSE + ############ + + # Here we iterate across our values to generate an array of keys + parser_list.reverse.each do |k| + resource_map_key = @resource_map[k] + [resource_map_key].flatten.each do |opt| + if values.slice!(/\s#{opt}/) + keys << k + break + end + end + end + + # Manually remove chain + values.slice!('-A') + keys << :chain + + # Here we generate the main hash + keys.zip(values.scan(/"[^"]*"|\S+/).reverse) { |f, v| hash[f] = v.gsub(/"/, '') } + + ##################### + # POST PARSE CLUDGING + ##################### + + # Normalise all rules to CIDR notation. + [:source, :destination].each do |prop| + next if hash[prop].nil? + m = hash[prop].match(/(!?)\s?(.*)/) + neg = "! " if m[1] == "!" + hash[prop] = "#{neg}#{Puppet::Util::IPCidr.new(m[2]).cidr}" + end + + [:dport, :sport, :port, :state, :ctstate].each do |prop| + hash[prop] = hash[prop].split(',') if ! hash[prop].nil? + end + + # Convert booleans removing the previous cludge we did + @known_booleans.each do |bool| + if hash[bool] != nil then + if hash[bool] != "true" then + raise "Parser error: #{bool} was meant to be a boolean but received value: #{hash[bool]}." + end + end + end + + # Our type prefers hyphens over colons for ranges so ... + # Iterate across all ports replacing colons with hyphens so that ranges match + # the types expectations. + [:dport, :sport, :port].each do |prop| + next unless hash[prop] + hash[prop] = hash[prop].collect do |elem| + elem.gsub(/:/,'-') + end + end + + # States should always be sorted. This ensures that the output from + # iptables-save and user supplied resources is consistent. + hash[:state] = hash[:state].sort unless hash[:state].nil? + hash[:ctstate] = hash[:ctstate].sort unless hash[:ctstate].nil? + + # This forces all existing, commentless rules or rules with invalid comments to be moved + # to the bottom of the stack. + # Puppet-firewall requires that all rules have comments (resource names) and match this + # regex and will fail if a rule in iptables does not have a comment. We get around this + # by appending a high level + if ! hash[:name] + num = 9000 + counter + hash[:name] = "#{num} #{Digest::MD5.hexdigest(line)}" + elsif not /^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/ =~ hash[:name] + num = 9000 + counter + hash[:name] = "#{num} #{/([[:alpha:][:digit:][:punct:][:space:]]+)/.match(hash[:name])[1]}" + end + + # Iptables defaults to log_level '4', so it is omitted from the output of iptables-save. + # If the :jump value is LOG and you don't have a log-level set, we assume it to be '4'. + if hash[:jump] == 'LOG' && ! hash[:log_level] + hash[:log_level] = '4' + end + + # Iptables defaults to burst '5', so it is ommitted from the output of iptables-save. + # If the :limit value is set and you don't have a burst set, we assume it to be '5'. + if hash[:limit] && ! hash[:burst] + hash[:burst] = '5' + end + + hash[:line] = line + hash[:provider] = self.name.to_s + hash[:table] = table + hash[:ensure] = :present + + # Munge some vars here ... + + # Proto should equal 'all' if undefined + hash[:proto] = "all" if !hash.include?(:proto) + + # If the jump parameter is set to one of: ACCEPT, REJECT or DROP then + # we should set the action parameter instead. + if ['ACCEPT','REJECT','DROP'].include?(hash[:jump]) then + hash[:action] = hash[:jump].downcase + hash.delete(:jump) + end + + hash + end + + def insert_args + args = [] + args << ["-I", resource[:chain], insert_order] + args << general_args + args + end + + def update_args + args = [] + args << ["-R", resource[:chain], insert_order] + args << general_args + args + end + + def delete_args + # Split into arguments + line = properties[:line].gsub(/\-A/, '-D').split(/\s(?=(?:[^"]|"[^"]*")*$)/).map{|v| v.gsub(/"/, '')} + line.unshift("-t", properties[:table]) + end + + # This method takes the resource, and attempts to generate the command line + # arguments for iptables. + def general_args + debug "Current resource: %s" % resource.class + + args = [] + resource_list = self.class.instance_variable_get('@resource_list') + resource_map = self.class.instance_variable_get('@resource_map') + known_booleans = self.class.instance_variable_get('@known_booleans') + + resource_list.each do |res| + resource_value = nil + if (resource[res]) then + resource_value = resource[res] + # If socket is true then do not add the value as -m socket is standalone + if known_booleans.include?(res) then + if resource[res] == :true then + resource_value = nil + else + # If the property is not :true then we don't want to add the value + # to the args list + next + end + end + elsif res == :jump and resource[:action] then + # In this case, we are substituting jump for action + resource_value = resource[:action].to_s.upcase + else + next + end + + args << [resource_map[res]].flatten.first.split(' ') + + # On negations, the '!' has to be before the option (eg: "! -d 1.2.3.4") + if resource_value.is_a?(String) and resource_value.sub!(/^!\s*/, '') then + # we do this after adding the 'dash' argument because of ones like "-m multiport --dports", where we want it before the "--dports" but after "-m multiport". + # so we insert before whatever the last argument is + args.insert(-2, '!') + end + + + # For sport and dport, convert hyphens to colons since the type + # expects hyphens for ranges of ports. + if [:sport, :dport, :port].include?(res) then + resource_value = resource_value.collect do |elem| + elem.gsub(/-/, ':') + end + end + + # our tcp_flags takes a single string with comma lists separated + # by space + # --tcp-flags expects two arguments + if res == :tcp_flags + one, two = resource_value.split(' ') + args << one + args << two + elsif resource_value.is_a?(Array) + args << resource_value.join(',') + elsif !resource_value.nil? + args << resource_value + end + end + + args + end + + def insert_order + debug("[insert_order]") + rules = [] + + # Find list of current rules based on chain and table + self.class.instances.each do |rule| + if rule.chain == resource[:chain].to_s and rule.table == resource[:table].to_s + rules << rule.name + end + end + + # No rules at all? Just bail now. + return 1 if rules.empty? + + # Add our rule to the end of the array of known rules + my_rule = resource[:name].to_s + rules << my_rule + + unmanaged_rule_regex = /^9[0-9]{3}\s[a-f0-9]{32}$/ + # Find if this is a new rule or an existing rule, then find how many + # unmanaged rules preceed it. + if rules.length == rules.uniq.length + # This is a new rule so find its ordered location. + new_rule_location = rules.sort.uniq.index(my_rule) + if new_rule_location == 0 + # The rule will be the first rule in the chain because nothing came + # before it. + offset_rule = rules[0] + else + # This rule will come after other managed rules, so find the rule + # immediately preceeding it. + offset_rule = rules.sort.uniq[new_rule_location - 1] + end + else + # This is a pre-existing rule, so find the offset from the original + # ordering. + offset_rule = my_rule + end + # Count how many unmanaged rules are ahead of the target rule so we know + # how much to add to the insert order + unnamed_offset = rules[0..rules.index(offset_rule)].inject(0) do |sum,rule| + # This regex matches the names given to unmanaged rules (a number + # 9000-9999 followed by an MD5 hash). + sum + (rule.match(unmanaged_rule_regex) ? 1 : 0) + end + + # We want our rule to come before unmanaged rules if it's not a 9-rule + if offset_rule.match(unmanaged_rule_regex) and ! my_rule.match(/^9/) + unnamed_offset -= 1 + end + + # Insert our new or updated rule in the correct order of named rules, but + # offset for unnamed rules. + rules.reject{|r|r.match(unmanaged_rule_regex)}.sort.index(my_rule) + 1 + unnamed_offset + end +end diff --git a/firewall/lib/puppet/provider/firewallchain/iptables_chain.rb b/firewall/lib/puppet/provider/firewallchain/iptables_chain.rb new file mode 100644 index 000000000..29fbc1f6b --- /dev/null +++ b/firewall/lib/puppet/provider/firewallchain/iptables_chain.rb @@ -0,0 +1,178 @@ +Puppet::Type.type(:firewallchain).provide :iptables_chain do + include Puppet::Util::Firewall + + @doc = "Iptables chain provider" + + has_feature :iptables_chain + has_feature :policy + + optional_commands({ + :iptables => 'iptables', + :iptables_save => 'iptables-save', + :ip6tables => 'ip6tables', + :ip6tables_save => 'ip6tables-save', + :ebtables => 'ebtables', + :ebtables_save => 'ebtables-save', + }) + + defaultfor :kernel => :linux + + # chain name is greedy so we anchor from the end. + # [\d+:\d+] doesn't exist on ebtables + Mapping = { + :IPv4 => { + :tables => method(:iptables), + :save => method(:iptables_save), + :re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/, + }, + :IPv6 => { + :tables => method(:ip6tables), + :save => method(:ip6tables_save), + :re => /^:(.+)\s(\S+)\s\[\d+:\d+\]$/, + }, + :ethernet => { + :tables => method(:ebtables), + :save => method(:ebtables_save), + :re => /^:(.+)\s(\S+)$/, + } + } + InternalChains = /^(PREROUTING|POSTROUTING|BROUTING|INPUT|FORWARD|OUTPUT)$/ + Tables = 'nat|mangle|filter|raw|rawpost|broute' + Nameformat = /^(.+):(#{Tables}):(IP(v[46])?|ethernet)$/ + + def create + allvalidchains do |t, chain, table, protocol| + if chain =~ InternalChains + # can't create internal chains + warning "Attempting to create internal chain #{@resource[:name]}" + end + if properties[:ensure] == protocol + debug "Skipping Inserting chain #{chain} on table #{table} (#{protocol}) already exists" + else + debug "Inserting chain #{chain} on table #{table} (#{protocol}) using #{t}" + t.call ['-t',table,'-N',chain] + unless @resource[:policy].nil? + t.call ['-t',table,'-P',chain,@resource[:policy].to_s.upcase] + end + end + end + end + + def destroy + allvalidchains do |t, chain, table| + if chain =~ InternalChains + # can't delete internal chains + warning "Attempting to destroy internal chain #{@resource[:name]}" + end + debug "Deleting chain #{chain} on table #{table}" + t.call ['-t',table,'-X',chain] + end + end + + def exists? + allvalidchains do |t, chain| + if chain =~ InternalChains + # If the chain isn't present, it's likely because the module isn't loaded. + # If this is true, then we fall into 2 cases + # 1) It'll be loaded on demand + # 2) It won't be loaded on demand, and we throw an error + # This is the intended behavior as it's not the provider's job to load kernel modules + # So we pretend it exists... + return true + end + end + properties[:ensure] == :present + end + + def policy=(value) + return if value == :empty + allvalidchains do |t, chain, table| + p = ['-t',table,'-P',chain,value.to_s.upcase] + debug "[set policy] #{t} #{p}" + t.call p + end + end + + def policy + debug "[get policy] #{@resource[:name]} =#{@property_hash[:policy].to_s.downcase}" + return @property_hash[:policy].to_s.downcase + end + + def self.prefetch(resources) + debug("[prefetch(resources)]") + instances.each do |prov| + if resource = resources[prov.name] + resource.provider = prov + end + end + end + + def flush + debug("[flush]") + persist_iptables(@resource[:name].match(Nameformat)[3]) + # Clear the property hash so we re-initialize with updated values + @property_hash.clear + end + + # Look up the current status. This allows us to conventiently look up + # existing status with properties[:foo]. + def properties + if @property_hash.empty? + @property_hash = query || {:ensure => :absent} + end + @property_hash.dup + end + + # Pull the current state of the list from the full list. + def query + self.class.instances.each do |instance| + if instance.name == self.name + debug "query found #{self.name}" % instance.properties.inspect + return instance.properties + end + end + nil + end + + def self.instances + debug "[instances]" + table = nil + chains = [] + + Mapping.each { |p, c| + begin + c[:save].call.each_line do |line| + if line =~ c[:re] then + name = $1 + ':' + (table == 'filter' ? 'filter' : table) + ':' + p.to_s + policy = $2 == '-' ? nil : $2.downcase.to_sym + + chains << new({ + :name => name, + :policy => policy, + :ensure => :present, + }) + + debug "[instance] '#{name}' #{policy}" + elsif line =~ /^\*(\S+)/ + table = $1 + else + next + end + end + rescue Puppet::Error + # ignore command not found for ebtables or anything that doesn't exist + end + } + + chains + end + + def allvalidchains + @resource[:name].match(Nameformat) + chain = $1 + table = $2 + protocol = $3 + yield Mapping[protocol.to_sym][:tables],chain,table,protocol.to_sym + end + +end diff --git a/firewall/lib/puppet/type/firewall.rb b/firewall/lib/puppet/type/firewall.rb new file mode 100644 index 000000000..75cf5863f --- /dev/null +++ b/firewall/lib/puppet/type/firewall.rb @@ -0,0 +1,1017 @@ +# See: #10295 for more details. +# +# This is a workaround for bug: #4248 whereby ruby files outside of the normal +# provider/type path do not load until pluginsync has occured on the puppetmaster +# +# In this case I'm trying the relative path first, then falling back to normal +# mechanisms. This should be fixed in future versions of puppet but it looks +# like we'll need to maintain this for some time perhaps. +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..")) +require 'puppet/util/firewall' + +Puppet::Type.newtype(:firewall) do + include Puppet::Util::Firewall + + @doc = <<-EOS + This type provides the capability to manage firewall rules within + puppet. + + **Autorequires:** + + If Puppet is managing the iptables or ip6tables chains specified in the + `chain` or `jump` parameters, the firewall resource will autorequire + those firewallchain resources. + + If Puppet is managing the iptables or iptables-persistent packages, and + the provider is iptables or ip6tables, the firewall resource will + autorequire those packages to ensure that any required binaries are + installed. + EOS + + feature :hop_limiting, "Hop limiting features." + feature :rate_limiting, "Rate limiting features." + feature :recent_limiting, "The netfilter recent module" + feature :snat, "Source NATing" + feature :dnat, "Destination NATing" + feature :interface_match, "Interface matching" + feature :icmp_match, "Matching ICMP types" + feature :owner, "Matching owners" + feature :state_match, "Matching stateful firewall states" + feature :reject_type, "The ability to control reject messages" + feature :log_level, "The ability to control the log level" + feature :log_prefix, "The ability to add prefixes to log messages" + feature :mark, "Set the netfilter mark value associated with the packet" + feature :tcp_flags, "The ability to match on particular TCP flag settings" + feature :pkttype, "Match a packet type" + feature :socket, "Match open sockets" + feature :isfragment, "Match fragments" + feature :address_type, "The ability match on source or destination address type" + feature :iprange, "The ability match on source or destination IP range " + feature :ishasmorefrags, "Match a non-last fragment of a fragmented ipv6 packet - might be first" + feature :islastfrag, "Match the last fragment of an ipv6 packet" + feature :isfirstfrag, "Match the first fragment of a fragmented ipv6 packet" + feature :ipsec_policy, "Match IPsec policy" + feature :ipsec_dir, "Match IPsec policy direction" + + # provider specific features + feature :iptables, "The provider provides iptables features." + + ensurable do + desc <<-EOS + Manage the state of this rule. The default action is *present*. + EOS + + newvalue(:present) do + provider.insert + end + + newvalue(:absent) do + provider.delete + end + + defaultto :present + end + + newparam(:name) do + desc <<-EOS + The canonical name of the rule. This name is also used for ordering + so make sure you prefix the rule with a number: + + 000 this runs first + 999 this runs last + + Depending on the provider, the name of the rule can be stored using + the comment feature of the underlying firewall subsystem. + EOS + isnamevar + + # Keep rule names simple - they must start with a number + newvalues(/^\d+[[:alpha:][:digit:][:punct:][:space:]]+$/) + end + + newproperty(:action) do + desc <<-EOS + This is the action to perform on a match. Can be one of: + + * accept - the packet is accepted + * reject - the packet is rejected with a suitable ICMP response + * drop - the packet is dropped + + If you specify no value it will simply match the rule but perform no + action unless you provide a provider specific parameter (such as *jump*). + EOS + newvalues(:accept, :reject, :drop) + end + + # Generic matching properties + newproperty(:source) do + desc <<-EOS + The source address. For example: + + source => '192.168.2.0/24' + + You can also negate a mask by putting ! in front. For example: + + source => '! 192.168.2.0/24' + + The source can also be an IPv6 address if your provider supports it. + EOS + + munge do |value| + begin + @resource.host_to_mask(value) + rescue Exception => e + self.fail("host_to_ip failed for #{value}, exception #{e}") + end + end + end + + # Source IP range + newproperty(:src_range, :required_features => :iprange) do + desc <<-EOS + The source IP range. For example: + + src_range => '192.168.1.1-192.168.1.10' + + The source IP range is must in 'IP1-IP2' format. + EOS + + newvalues(/^((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)-((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/) + end + + newproperty(:destination) do + desc <<-EOS + The destination address to match. For example: + + destination => '192.168.1.0/24' + + You can also negate a mask by putting ! in front. For example: + + destination => '! 192.168.2.0/24' + + The destination can also be an IPv6 address if your provider supports it. + EOS + + munge do |value| + begin + @resource.host_to_mask(value) + rescue Exception => e + self.fail("host_to_ip failed for #{value}, exception #{e}") + end + end + end + + # Destination IP range + newproperty(:dst_range, :required_features => :iprange) do + desc <<-EOS + The destination IP range. For example: + + dst_range => '192.168.1.1-192.168.1.10' + + The destination IP range is must in 'IP1-IP2' format. + EOS + + newvalues(/^((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)-((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)/) + end + + newproperty(:sport, :array_matching => :all) do + desc <<-EOS + The source port to match for this filter (if the protocol supports + ports). Will accept a single element or an array. + + For some firewall providers you can pass a range of ports in the format: + + - + + For example: + + 1-1024 + + This would cover ports 1 to 1024. + EOS + + munge do |value| + @resource.string_to_port(value, :proto) + end + + def is_to_s(value) + should_to_s(value) + end + + def should_to_s(value) + value = [value] unless value.is_a?(Array) + value.join(',') + end + end + + newproperty(:dport, :array_matching => :all) do + desc <<-EOS + The destination port to match for this filter (if the protocol supports + ports). Will accept a single element or an array. + + For some firewall providers you can pass a range of ports in the format: + + - + + For example: + + 1-1024 + + This would cover ports 1 to 1024. + EOS + + munge do |value| + @resource.string_to_port(value, :proto) + end + + def is_to_s(value) + should_to_s(value) + end + + def should_to_s(value) + value = [value] unless value.is_a?(Array) + value.join(',') + end + end + + newproperty(:port, :array_matching => :all) do + desc <<-EOS + The destination or source port to match for this filter (if the protocol + supports ports). Will accept a single element or an array. + + For some firewall providers you can pass a range of ports in the format: + + - + + For example: + + 1-1024 + + This would cover ports 1 to 1024. + EOS + + munge do |value| + @resource.string_to_port(value, :proto) + end + + def is_to_s(value) + should_to_s(value) + end + + def should_to_s(value) + value = [value] unless value.is_a?(Array) + value.join(',') + end + end + + newproperty(:dst_type, :required_features => :address_type) do + desc <<-EOS + The destination address type. For example: + + dst_type => 'LOCAL' + + Can be one of: + + * UNSPEC - an unspecified address + * UNICAST - a unicast address + * LOCAL - a local address + * BROADCAST - a broadcast address + * ANYCAST - an anycast packet + * MULTICAST - a multicast address + * BLACKHOLE - a blackhole address + * UNREACHABLE - an unreachable address + * PROHIBIT - a prohibited address + * THROW - undocumented + * NAT - undocumented + * XRESOLVE - undocumented + EOS + + newvalues(:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, + :BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE) + end + + newproperty(:src_type, :required_features => :address_type) do + desc <<-EOS + The source address type. For example: + + src_type => 'LOCAL' + + Can be one of: + + * UNSPEC - an unspecified address + * UNICAST - a unicast address + * LOCAL - a local address + * BROADCAST - a broadcast address + * ANYCAST - an anycast packet + * MULTICAST - a multicast address + * BLACKHOLE - a blackhole address + * UNREACHABLE - an unreachable address + * PROHIBIT - a prohibited address + * THROW - undocumented + * NAT - undocumented + * XRESOLVE - undocumented + EOS + + newvalues(:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, + :BLACKHOLE, :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE) + end + + newproperty(:proto) do + desc <<-EOS + The specific protocol to match for this rule. By default this is + *tcp*. + EOS + + newvalues(:tcp, :udp, :icmp, :"ipv6-icmp", :esp, :ah, :vrrp, :igmp, :ipencap, :ospf, :gre, :all) + defaultto "tcp" + end + + # tcp-specific + newproperty(:tcp_flags, :required_features => :tcp_flags) do + desc <<-EOS + Match when the TCP flags are as specified. + Is a string with a list of comma-separated flag names for the mask, + then a space, then a comma-separated list of flags that should be set. + The flags are: SYN ACK FIN RST URG PSH ALL NONE + Note that you specify them in the order that iptables --list-rules + would list them to avoid having puppet think you changed the flags. + Example: FIN,SYN,RST,ACK SYN matches packets with the SYN bit set and the + ACK,RST and FIN bits cleared. Such packets are used to request + TCP connection initiation. + EOS + end + + + # Iptables specific + newproperty(:chain, :required_features => :iptables) do + desc <<-EOS + Name of the chain to use. Can be one of the built-ins: + + * INPUT + * FORWARD + * OUTPUT + * PREROUTING + * POSTROUTING + + Or you can provide a user-based chain. + + The default value is 'INPUT'. + EOS + + defaultto "INPUT" + newvalue(/^[a-zA-Z0-9\-_]+$/) + end + + newproperty(:table, :required_features => :iptables) do + desc <<-EOS + Table to use. Can be one of: + + * nat + * mangle + * filter + * raw + * rawpost + + By default the setting is 'filter'. + EOS + + newvalues(:nat, :mangle, :filter, :raw, :rawpost) + defaultto "filter" + end + + newproperty(:jump, :required_features => :iptables) do + desc <<-EOS + The value for the iptables --jump parameter. Normal values are: + + * QUEUE + * RETURN + * DNAT + * SNAT + * LOG + * MASQUERADE + * REDIRECT + * MARK + + But any valid chain name is allowed. + + For the values ACCEPT, DROP and REJECT you must use the generic + 'action' parameter. This is to enfore the use of generic parameters where + possible for maximum cross-platform modelling. + + If you set both 'accept' and 'jump' parameters, you will get an error as + only one of the options should be set. + EOS + + validate do |value| + unless value =~ /^[a-zA-Z0-9\-_]+$/ + raise ArgumentError, <<-EOS + Jump destination must consist of alphanumeric characters, an + underscore or a yphen. + EOS + end + + if ["accept","reject","drop"].include?(value.downcase) + raise ArgumentError, <<-EOS + Jump destination should not be one of ACCEPT, REJECT or DROP. Use + the action property instead. + EOS + end + + end + end + + # Interface specific matching properties + newproperty(:iniface, :required_features => :interface_match) do + desc <<-EOS + Input interface to filter on. + EOS + newvalues(/^[a-zA-Z0-9\-\._\+]+$/) + end + + newproperty(:outiface, :required_features => :interface_match) do + desc <<-EOS + Output interface to filter on. + EOS + newvalues(/^[a-zA-Z0-9\-\._\+]+$/) + end + + # NAT specific properties + newproperty(:tosource, :required_features => :snat) do + desc <<-EOS + When using jump => "SNAT" you can specify the new source address using + this parameter. + EOS + end + + newproperty(:todest, :required_features => :dnat) do + desc <<-EOS + When using jump => "DNAT" you can specify the new destination address + using this paramter. + EOS + end + + newproperty(:toports, :required_features => :dnat) do + desc <<-EOS + For DNAT this is the port that will replace the destination port. + EOS + end + + newproperty(:random, :required_features => :dnat) do + desc <<-EOS + When using a jump value of "MASQUERADE", "DNAT", "REDIRECT", or "SNAT" + this boolean will enable randomized port mapping. + EOS + + newvalues(:true, :false) + end + + # Reject ICMP type + newproperty(:reject, :required_features => :reject_type) do + desc <<-EOS + When combined with jump => "REJECT" you can specify a different icmp + response to be sent back to the packet sender. + EOS + end + + # Logging properties + newproperty(:log_level, :required_features => :log_level) do + desc <<-EOS + When combined with jump => "LOG" specifies the system log level to log + to. + EOS + + munge do |value| + if value.kind_of?(String) + value = @resource.log_level_name_to_number(value) + else + value + end + + if value == nil && value != "" + self.fail("Unable to determine log level") + end + value + end + end + + newproperty(:log_prefix, :required_features => :log_prefix) do + desc <<-EOS + When combined with jump => "LOG" specifies the log prefix to use when + logging. + EOS + end + + # ICMP matching property + newproperty(:icmp, :required_features => :icmp_match) do + desc <<-EOS + When matching ICMP packets, this is the type of ICMP packet to match. + + A value of "any" is not supported. To achieve this behaviour the + parameter should simply be omitted or undefined. + EOS + + validate do |value| + if value == "any" + raise ArgumentError, + "Value 'any' is not valid. This behaviour should be achieved " \ + "by omitting or undefining the ICMP parameter." + end + end + + munge do |value| + if value.kind_of?(String) + # ICMP codes differ between IPv4 and IPv6. + case @resource[:provider] + when :iptables + protocol = 'inet' + when :ip6tables + protocol = 'inet6' + else + self.fail("cannot work out protocol family") + end + + value = @resource.icmp_name_to_number(value, protocol) + else + value + end + + if value == nil && value != "" + self.fail("cannot work out icmp type") + end + value + end + end + + newproperty(:state, :array_matching => :all, :required_features => + :state_match) do + + desc <<-EOS + Matches a packet based on its state in the firewall stateful inspection + table. Values can be: + + * INVALID + * ESTABLISHED + * NEW + * RELATED + EOS + + newvalues(:INVALID,:ESTABLISHED,:NEW,:RELATED) + + # States should always be sorted. This normalizes the resource states to + # keep it consistent with the sorted result from iptables-save. + def should=(values) + @should = super(values).sort_by {|sym| sym.to_s} + end + + def is_to_s(value) + should_to_s(value) + end + + def should_to_s(value) + value = [value] unless value.is_a?(Array) + value.join(',') + end + end + + newproperty(:ctstate, :array_matching => :all, :required_features => + :state_match) do + + desc <<-EOS + Matches a packet based on its state in the firewall stateful inspection + table, using the conntrack module. Values can be: + + * INVALID + * ESTABLISHED + * NEW + * RELATED + EOS + + newvalues(:INVALID,:ESTABLISHED,:NEW,:RELATED) + + # States should always be sorted. This normalizes the resource states to + # keep it consistent with the sorted result from iptables-save. + def should=(values) + @should = super(values).sort_by {|sym| sym.to_s} + end + + def is_to_s(value) + should_to_s(value) + end + + def should_to_s(value) + value = [value] unless value.is_a?(Array) + value.join(',') + end + end + + + # Hop limiting properties + newproperty(:hop_limit, :required_features => :hop_limiting) do + desc <<-EOS + Hop limiting value for matched packets. + EOS + newvalue(/^\d+$/) + end + + # Rate limiting properties + newproperty(:limit, :required_features => :rate_limiting) do + desc <<-EOS + Rate limiting value for matched packets. The format is: + rate/[/second/|/minute|/hour|/day]. + + Example values are: '50/sec', '40/min', '30/hour', '10/day'." + EOS + end + + newproperty(:burst, :required_features => :rate_limiting) do + desc <<-EOS + Rate limiting burst value (per second) before limit checks apply. + EOS + newvalue(/^\d+$/) + end + + newproperty(:uid, :required_features => :owner) do + desc <<-EOS + UID or Username owner matching rule. Accepts a string argument + only, as iptables does not accept multiple uid in a single + statement. + EOS + end + + newproperty(:gid, :required_features => :owner) do + desc <<-EOS + GID or Group owner matching rule. Accepts a string argument + only, as iptables does not accept multiple gid in a single + statement. + EOS + end + + newproperty(:set_mark, :required_features => :mark) do + desc <<-EOS + Set the Netfilter mark value associated with the packet. Accepts either of: + mark/mask or mark. These will be converted to hex if they are not already. + EOS + + munge do |value| + int_or_hex = '[a-fA-F0-9x]' + match = value.to_s.match("(#{int_or_hex}+)(/)?(#{int_or_hex}+)?") + mark = @resource.to_hex32(match[1]) + + # Values that can't be converted to hex. + # Or contain a trailing slash with no mask. + if mark.nil? or (mark and match[2] and match[3].nil?) + raise ArgumentError, "MARK value must be integer or hex between 0 and 0xffffffff" + end + + # Old iptables does not support a mask. New iptables will expect one. + iptables_version = Facter.fact('iptables_version').value + mask_required = (iptables_version and Puppet::Util::Package.versioncmp(iptables_version, '1.4.1') >= 0) + + if mask_required + if match[3].nil? + value = "#{mark}/0xffffffff" + else + mask = @resource.to_hex32(match[3]) + if mask.nil? + raise ArgumentError, "MARK mask must be integer or hex between 0 and 0xffffffff" + end + value = "#{mark}/#{mask}" + end + else + unless match[3].nil? + raise ArgumentError, "iptables version #{iptables_version} does not support masks on MARK rules" + end + value = mark + end + + value + end + end + + newproperty(:pkttype, :required_features => :pkttype) do + desc <<-EOS + Sets the packet type to match. + EOS + + newvalues(:unicast, :broadcast, :multicast) + end + + newproperty(:isfragment, :required_features => :isfragment) do + desc <<-EOS + Set to true to match tcp fragments (requires type to be set to tcp) + EOS + + newvalues(:true, :false) + end + + newproperty(:recent, :required_features => :recent_limiting) do + desc <<-EOS + Enable the recent module. Takes as an argument one of set, update, + rcheck or remove. For example: + + # If anyone's appeared on the 'badguy' blacklist within + # the last 60 seconds, drop their traffic, and update the timestamp. + firewall { '100 Drop badguy traffic': + recent => 'update', + rseconds => 60, + rsource => true, + rname => 'badguy', + action => 'DROP', + chain => 'FORWARD', + } + # No-one should be sending us traffic on eth0 from localhost + # Blacklist them + firewall { '101 blacklist strange traffic': + recent => 'set', + rsource => true, + rname => 'badguy', + destination => '127.0.0.0/8', + iniface => 'eth0', + action => 'DROP', + chain => 'FORWARD', + } + EOS + + newvalues(:set, :update, :rcheck, :remove) + munge do |value| + value = "--" + value + end + end + + newproperty(:rdest, :required_features => :recent_limiting) do + desc <<-EOS + Recent module; add the destination IP address to the list. + Must be boolean true. + EOS + + newvalues(:true, :false) + end + + newproperty(:rsource, :required_features => :recent_limiting) do + desc <<-EOS + Recent module; add the source IP address to the list. + Must be boolean true. + EOS + + newvalues(:true, :false) + end + + newproperty(:rname, :required_features => :recent_limiting) do + desc <<-EOS + Recent module; The name of the list. Takes a string argument. + EOS + end + + newproperty(:rseconds, :required_features => :recent_limiting) do + desc <<-EOS + Recent module; used in conjunction with one of `recent => 'rcheck'` or + `recent => 'update'`. When used, this will narrow the match to only + happen when the address is in the list and was seen within the last given + number of seconds. + EOS + end + + newproperty(:reap, :required_features => :recent_limiting) do + desc <<-EOS + Recent module; can only be used in conjunction with the `rseconds` + attribute. When used, this will cause entries older than 'seconds' to be + purged. Must be boolean true. + EOS + + newvalues(:true, :false) + end + + newproperty(:rhitcount, :required_features => :recent_limiting) do + desc <<-EOS + Recent module; used in conjunction with `recent => 'update'` or `recent + => 'rcheck'. When used, this will narrow the match to only happen when + the address is in the list and packets had been received greater than or + equal to the given value. + EOS + end + + newproperty(:rttl, :required_features => :recent_limiting) do + desc <<-EOS + Recent module; may only be used in conjunction with one of `recent => + 'rcheck'` or `recent => 'update'`. When used, this will narrow the match + to only happen when the address is in the list and the TTL of the current + packet matches that of the packet which hit the `recent => 'set'` rule. + This may be useful if you have problems with people faking their source + address in order to DoS you via this module by disallowing others access + to your site by sending bogus packets to you. Must be boolean true. + EOS + + newvalues(:true, :false) + end + + newproperty(:socket, :required_features => :socket) do + desc <<-EOS + If true, matches if an open socket can be found by doing a coket lookup + on the packet. + EOS + + newvalues(:true, :false) + end + + newproperty(:ishasmorefrags, :required_features => :ishasmorefrags) do + desc <<-EOS + If true, matches if the packet has it's 'more fragments' bit set. ipv6. + EOS + + newvalues(:true, :false) + end + + newproperty(:islastfrag, :required_features => :islastfrag) do + desc <<-EOS + If true, matches if the packet is the last fragment. ipv6. + EOS + + newvalues(:true, :false) + end + + newproperty(:isfirstfrag, :required_features => :isfirstfrag) do + desc <<-EOS + If true, matches if the packet is the first fragment. + Sadly cannot be negated. ipv6. + EOS + + newvalues(:true, :false) + end + + newproperty(:ipsec_policy, :required_features => :ipsec_policy) do + desc <<-EOS + Sets the ipsec policy type + EOS + + newvalues(:none, :ipsec) + end + + newproperty(:ipsec_dir, :required_features => :ipsec_dir) do + desc <<-EOS + Sets the ipsec policy direction + EOS + + newvalues(:in, :out) + end + + newparam(:line) do + desc <<-EOS + Read-only property for caching the rule line. + EOS + end + + autorequire(:firewallchain) do + reqs = [] + protocol = nil + + case value(:provider) + when :iptables + protocol = "IPv4" + when :ip6tables + protocol = "IPv6" + end + + unless protocol.nil? + table = value(:table) + [value(:chain), value(:jump)].each do |chain| + reqs << "#{chain}:#{table}:#{protocol}" unless ( chain.nil? || (['INPUT', 'OUTPUT', 'FORWARD'].include?(chain) && table == :filter) ) + end + end + + reqs + end + + # Classes would be a better abstraction, pending: + # http://projects.puppetlabs.com/issues/19001 + autorequire(:package) do + case value(:provider) + when :iptables, :ip6tables + %w{iptables iptables-persistent} + else + [] + end + end + + validate do + debug("[validate]") + + # TODO: this is put here to skip validation if ensure is not set. This + # is because there is a revalidation stage called later where the values + # are not set correctly. I tried tracing it - but have put in this + # workaround instead to skip. Must get to the bottom of this. + if ! value(:ensure) + return + end + + # First we make sure the chains and tables are valid combinations + if value(:table).to_s == "filter" && + value(:chain) =~ /PREROUTING|POSTROUTING/ + + self.fail "PREROUTING and POSTROUTING cannot be used in table 'filter'" + end + + if value(:table).to_s == "nat" && value(:chain) =~ /INPUT|FORWARD/ + self.fail "INPUT and FORWARD cannot be used in table 'nat'" + end + + if value(:table).to_s == "raw" && + value(:chain) =~ /INPUT|FORWARD|POSTROUTING/ + + self.fail "INPUT, FORWARD and POSTROUTING cannot be used in table raw" + end + + # Now we analyse the individual properties to make sure they apply to + # the correct combinations. + if value(:iniface) + unless value(:chain).to_s =~ /INPUT|FORWARD|PREROUTING/ + self.fail "Parameter iniface only applies to chains " \ + "INPUT,FORWARD,PREROUTING" + end + end + + if value(:outiface) + unless value(:chain).to_s =~ /OUTPUT|FORWARD|POSTROUTING/ + self.fail "Parameter outiface only applies to chains " \ + "OUTPUT,FORWARD,POSTROUTING" + end + end + + if value(:uid) + unless value(:chain).to_s =~ /OUTPUT|POSTROUTING/ + self.fail "Parameter uid only applies to chains " \ + "OUTPUT,POSTROUTING" + end + end + + if value(:gid) + unless value(:chain).to_s =~ /OUTPUT|POSTROUTING/ + self.fail "Parameter gid only applies to chains " \ + "OUTPUT,POSTROUTING" + end + end + + if value(:set_mark) + unless value(:jump).to_s =~ /MARK/ && + value(:chain).to_s =~ /PREROUTING|OUTPUT/ && + value(:table).to_s =~ /mangle/ + self.fail "Parameter set_mark only applies to " \ + "the PREROUTING or OUTPUT chain of the mangle table and when jump => MARK" + end + end + + if value(:dport) + unless value(:proto).to_s =~ /tcp|udp|sctp/ + self.fail "[%s] Parameter dport only applies to sctp, tcp and udp " \ + "protocols. Current protocol is [%s] and dport is [%s]" % + [value(:name), should(:proto), should(:dport)] + end + end + + if value(:jump).to_s == "DNAT" + unless value(:table).to_s =~ /nat/ + self.fail "Parameter jump => DNAT only applies to table => nat" + end + + unless value(:todest) + self.fail "Parameter jump => DNAT must have todest parameter" + end + end + + if value(:jump).to_s == "SNAT" + unless value(:table).to_s =~ /nat/ + self.fail "Parameter jump => SNAT only applies to table => nat" + end + + unless value(:tosource) + self.fail "Parameter jump => SNAT must have tosource parameter" + end + end + + if value(:jump).to_s == "REDIRECT" + unless value(:toports) + self.fail "Parameter jump => REDIRECT missing mandatory toports " \ + "parameter" + end + end + + if value(:jump).to_s == "MASQUERADE" + unless value(:table).to_s =~ /nat/ + self.fail "Parameter jump => MASQUERADE only applies to table => nat" + end + end + + if value(:log_prefix) || value(:log_level) + unless value(:jump).to_s == "LOG" + self.fail "Parameter log_prefix and log_level require jump => LOG" + end + end + + if value(:burst) && ! value(:limit) + self.fail "burst makes no sense without limit" + end + + if value(:action) && value(:jump) + self.fail "Only one of the parameters 'action' and 'jump' can be set" + end + end +end diff --git a/firewall/lib/puppet/type/firewallchain.rb b/firewall/lib/puppet/type/firewallchain.rb new file mode 100644 index 000000000..3e3c5d137 --- /dev/null +++ b/firewall/lib/puppet/type/firewallchain.rb @@ -0,0 +1,222 @@ +# This is a workaround for bug: #4248 whereby ruby files outside of the normal +# provider/type path do not load until pluginsync has occured on the puppetmaster +# +# In this case I'm trying the relative path first, then falling back to normal +# mechanisms. This should be fixed in future versions of puppet but it looks +# like we'll need to maintain this for some time perhaps. +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__),"..","..")) +require 'puppet/util/firewall' + +Puppet::Type.newtype(:firewallchain) do + include Puppet::Util::Firewall + + @doc = <<-EOS + This type provides the capability to manage rule chains for firewalls. + + Currently this supports only iptables, ip6tables and ebtables on Linux. And + provides support for setting the default policy on chains and tables that + allow it. + + **Autorequires:** + If Puppet is managing the iptables or iptables-persistent packages, and + the provider is iptables_chain, the firewall resource will autorequire + those packages to ensure that any required binaries are installed. + EOS + + feature :iptables_chain, "The provider provides iptables chain features." + feature :policy, "Default policy (inbuilt chains only)" + + ensurable do + defaultvalues + defaultto :present + end + + newparam(:name) do + desc <<-EOS + The canonical name of the chain. + + For iptables the format must be {chain}:{table}:{protocol}. + EOS + isnamevar + + validate do |value| + if value !~ Nameformat then + raise ArgumentError, "Inbuilt chains must be in the form {chain}:{table}:{protocol} where {table} is one of FILTER, NAT, MANGLE, RAW, RAWPOST, BROUTE or empty (alias for filter), chain can be anything without colons or one of PREROUTING, POSTROUTING, BROUTING, INPUT, FORWARD, OUTPUT for the inbuilt chains, and {protocol} being IPv4, IPv6, ethernet (ethernet bridging) got '#{value}' table:'#{$1}' chain:'#{$2}' protocol:'#{$3}'" + else + chain = $1 + table = $2 + protocol = $3 + case table + when 'filter' + if chain =~ /^(PREROUTING|POSTROUTING|BROUTING)$/ + raise ArgumentError, "INPUT, OUTPUT and FORWARD are the only inbuilt chains that can be used in table 'filter'" + end + when 'mangle' + if chain =~ InternalChains && chain == 'BROUTING' + raise ArgumentError, "PREROUTING, POSTROUTING, INPUT, FORWARD and OUTPUT are the only inbuilt chains that can be used in table 'mangle'" + end + when 'nat' + if chain =~ /^(BROUTING|FORWARD)$/ + raise ArgumentError, "PREROUTING, POSTROUTING, INPUT, and OUTPUT are the only inbuilt chains that can be used in table 'nat'" + end + if protocol =~/^(IP(v6)?)?$/ + raise ArgumentError, "table nat isn't valid in IPv6. You must specify ':IPv4' as the name suffix" + end + when 'raw' + if chain =~ /^(POSTROUTING|BROUTING|INPUT|FORWARD)$/ + raise ArgumentError,'PREROUTING and OUTPUT are the only inbuilt chains in the table \'raw\'' + end + when 'broute' + if protocol != 'ethernet' + raise ArgumentError,'BROUTE is only valid with protocol \'ethernet\'' + end + if chain =~ /^PREROUTING|POSTROUTING|INPUT|FORWARD|OUTPUT$/ + raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'broute\'' + end + end + if chain == 'BROUTING' && ( protocol != 'ethernet' || table!='broute') + raise ArgumentError,'BROUTING is the only inbuilt chain allowed on on table \'BROUTE\' with protocol \'ethernet\' i.e. \'broute:BROUTING:enternet\'' + end + end + end + end + + newproperty(:policy) do + desc <<-EOS + This is the action to when the end of the chain is reached. + It can only be set on inbuilt chains (INPUT, FORWARD, OUTPUT, + PREROUTING, POSTROUTING) and can be one of: + + * accept - the packet is accepted + * drop - the packet is dropped + * queue - the packet is passed userspace + * return - the packet is returned to calling (jump) queue + or the default of inbuilt chains + EOS + newvalues(:accept, :drop, :queue, :return) + defaultto do + # ethernet chain have an ACCEPT default while other haven't got an + # allowed value + if @resource[:name] =~ /:ethernet$/ + :accept + else + nil + end + end + end + + newparam(:purge, :boolean => true) do + desc <<-EOS + Purge unmanaged firewall rules in this chain + EOS + newvalues(:false, :true) + defaultto :false + end + + newparam(:ignore) do + desc <<-EOS + Regex to perform on firewall rules to exempt unmanaged rules from purging (when enabled). + This is matched against the output of `iptables-save`. + + This can be a single regex, or an array of them. + To support flags, use the ruby inline flag mechanism. + Meaning a regex such as + /foo/i + can be written as + '(?i)foo' or '(?i:foo)' + + Full example: + firewallchain { 'INPUT:filter:IPv4': + purge => true, + ignore => [ + '-j fail2ban-ssh', # ignore the fail2ban jump rule + '--comment "[^"]*(?i:ignore)[^"]*"', # ignore any rules with "ignore" (case insensitive) in the comment in the rule + ], + } + EOS + + validate do |value| + unless value.is_a?(Array) or value.is_a?(String) or value == false + self.devfail "Ignore must be a string or an Array" + end + end + munge do |patterns| # convert into an array of {Regex}es + patterns = [patterns] if patterns.is_a?(String) + patterns.map{|p| Regexp.new(p)} + end + end + + # Classes would be a better abstraction, pending: + # http://projects.puppetlabs.com/issues/19001 + autorequire(:package) do + case value(:provider) + when :iptables_chain + %w{iptables iptables-persistent} + else + [] + end + end + + validate do + debug("[validate]") + + value(:name).match(Nameformat) + chain = $1 + table = $2 + protocol = $3 + + # Check that we're not removing an internal chain + if chain =~ InternalChains && value(:ensure) == :absent + self.fail "Cannot remove in-built chains" + end + + if value(:policy).nil? && protocol == 'ethernet' + self.fail "you must set a non-empty policy on all ethernet table chains" + end + + # Check that we're not setting a policy on a user chain + if chain !~ InternalChains && + !value(:policy).nil? && + protocol != 'ethernet' + + self.fail "policy can only be set on in-built chains (with the exception of ethernet chains) (table:#{table} chain:#{chain} protocol:#{protocol})" + end + + # no DROP policy on nat table + if table == 'nat' && + value(:policy) == :drop + + self.fail 'The "nat" table is not intended for filtering, the use of DROP is therefore inhibited' + end + end + + def generate + return [] unless self.purge? + + value(:name).match(Nameformat) + chain = $1 + table = $2 + protocol = $3 + + provider = case protocol + when 'IPv4' + :iptables + when 'IPv6' + :ip6tables + end + + # gather a list of all rules present on the system + rules_resources = Puppet::Type.type(:firewall).instances + + # Keep only rules in this chain + rules_resources.delete_if { |res| (res[:provider] != provider or res.provider.properties[:table].to_s != table or res.provider.properties[:chain] != chain) } + + # Remove rules which match our ignore filter + rules_resources.delete_if {|res| value(:ignore).find_index{|f| res.provider.properties[:line].match(f)}} if value(:ignore) + + # We mark all remaining rules for deletion, and then let the catalog override us on rules which should be present + rules_resources.each {|res| res[:ensure] = :absent} + + rules_resources + end +end diff --git a/firewall/lib/puppet/util/firewall.rb b/firewall/lib/puppet/util/firewall.rb new file mode 100644 index 000000000..aa26d3bc7 --- /dev/null +++ b/firewall/lib/puppet/util/firewall.rb @@ -0,0 +1,225 @@ +require 'socket' +require 'resolv' +require 'puppet/util/ipcidr' + +# Util module for puppetlabs-firewall +module Puppet::Util::Firewall + # Translate the symbolic names for icmp packet types to integers + def icmp_name_to_number(value_icmp, protocol) + if value_icmp =~ /\d{1,2}$/ + value_icmp + elsif protocol == 'inet' + case value_icmp + when "echo-reply" then "0" + when "destination-unreachable" then "3" + when "source-quench" then "4" + when "redirect" then "6" + when "echo-request" then "8" + when "router-advertisement" then "9" + when "router-solicitation" then "10" + when "time-exceeded" then "11" + when "parameter-problem" then "12" + when "timestamp-request" then "13" + when "timestamp-reply" then "14" + when "address-mask-request" then "17" + when "address-mask-reply" then "18" + else nil + end + elsif protocol == 'inet6' + case value_icmp + when "destination-unreachable" then "1" + when "time-exceeded" then "3" + when "parameter-problem" then "4" + when "echo-request" then "128" + when "echo-reply" then "129" + when "router-solicitation" then "133" + when "router-advertisement" then "134" + when "redirect" then "137" + else nil + end + else + raise ArgumentError, "unsupported protocol family '#{protocol}'" + end + end + + # Convert log_level names to their respective numbers + def log_level_name_to_number(value) + #TODO make this 0-7 only + if value =~ /\d/ + value + else + case value + when "panic" then "0" + when "alert" then "1" + when "crit" then "2" + when "err" then "3" + when "error" then "3" + when "warn" then "4" + when "warning" then "4" + when "not" then "5" + when "notice" then "5" + when "info" then "6" + when "debug" then "7" + else nil + end + end + end + + # This method takes a string and a protocol and attempts to convert + # it to a port number if valid. + # + # If the string already contains a port number or perhaps a range of ports + # in the format 22:1000 for example, it simply returns the string and does + # nothing. + def string_to_port(value, proto) + proto = proto.to_s + unless proto =~ /^(tcp|udp)$/ + proto = 'tcp' + end + + if value.kind_of?(String) + if value.match(/^\d+(-\d+)?$/) + return value + else + return Socket.getservbyname(value, proto).to_s + end + else + Socket.getservbyname(value.to_s, proto).to_s + end + end + + # Takes an address and returns it in CIDR notation. + # + # If the address is: + # + # - A hostname: + # It will be resolved + # - An IPv4 address: + # It will be qualified with a /32 CIDR notation + # - An IPv6 address: + # It will be qualified with a /128 CIDR notation + # - An IP address with a CIDR notation: + # It will be normalised + # - An IP address with a dotted-quad netmask: + # It will be converted to CIDR notation + # - Any address with a resulting prefix length of zero: + # It will return nil which is equivilent to not specifying an address + # + def host_to_ip(value) + begin + value = Puppet::Util::IPCidr.new(value) + rescue + value = Puppet::Util::IPCidr.new(Resolv.getaddress(value)) + end + + return nil if value.prefixlen == 0 + value.cidr + end + + # Takes an address mask and converts the host portion to CIDR notation. + # + # This takes into account you can negate a mask but follows all rules + # defined in host_to_ip for the host/address part. + # + def host_to_mask(value) + match = value.match /(!)\s?(.*)$/ + return host_to_ip(value) unless match + + cidr = host_to_ip(match[2]) + return nil if cidr == nil + "#{match[1]} #{cidr}" + end + + # Validates the argument is int or hex, and returns valid hex + # conversion of the value or nil otherwise. + def to_hex32(value) + begin + value = Integer(value) + if value.between?(0, 0xffffffff) + return '0x' + value.to_s(16) + end + rescue ArgumentError + # pass + end + return nil + end + + def persist_iptables(proto) + debug("[persist_iptables]") + + # Basic normalisation for older Facter + os_key = Facter.value(:osfamily) + os_key ||= case Facter.value(:operatingsystem) + when 'RedHat', 'CentOS', 'Fedora', 'Scientific', 'SL', 'SLC', 'Ascendos', 'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL', 'Amazon', 'XenServer' + 'RedHat' + when 'Debian', 'Ubuntu' + 'Debian' + else + Facter.value(:operatingsystem) + end + + # Older iptables-persistent doesn't provide save action. + if os_key == 'Debian' + persist_ver = Facter.value(:iptables_persistent_version) + if (persist_ver and Puppet::Util::Package.versioncmp(persist_ver, '0.5.0') < 0) + os_key = 'Debian_manual' + end + end + + # Fedora 15 and newer use systemd to persist iptable rules + if os_key == 'RedHat' && Facter.value(:operatingsystem) == 'Fedora' && Facter.value(:operatingsystemrelease).to_i >= 15 + os_key = 'Fedora' + end + + # RHEL 7 and newer also use systemd to persist iptable rules + if os_key == 'RedHat' && Facter.value(:operatingsystem) == 'RedHat' && Facter.value(:operatingsystemrelease).to_i >= 7 + os_key = 'Fedora' + end + + cmd = case os_key.to_sym + when :RedHat + case proto.to_sym + when :IPv4 + %w{/sbin/service iptables save} + when :IPv6 + %w{/sbin/service ip6tables save} + end + when :Fedora + case proto.to_sym + when :IPv4 + %w{/usr/libexec/iptables/iptables.init save} + when :IPv6 + %w{/usr/libexec/iptables/ip6tables.init save} + end + when :Debian + case proto.to_sym + when :IPv4, :IPv6 + %w{/usr/sbin/service iptables-persistent save} + end + when :Debian_manual + case proto.to_sym + when :IPv4 + ["/bin/sh", "-c", "/sbin/iptables-save > /etc/iptables/rules"] + end + when :Archlinux + case proto.to_sym + when :IPv4 + ["/bin/sh", "-c", "/usr/sbin/iptables-save > /etc/iptables/iptables.rules"] + when :IPv6 + ["/bin/sh", "-c", "/usr/sbin/ip6tables-save > /etc/iptables/ip6tables.rules"] + end + end + + # Catch unsupported OSs from the case statement above. + if cmd.nil? + debug('firewall: Rule persistence is not supported for this type/OS') + return + end + + begin + execute(cmd) + rescue Puppet::ExecutionFailure => detail + warning("Unable to persist firewall rules: #{detail}") + end + end +end diff --git a/firewall/lib/puppet/util/ipcidr.rb b/firewall/lib/puppet/util/ipcidr.rb new file mode 100644 index 000000000..87e8d5e37 --- /dev/null +++ b/firewall/lib/puppet/util/ipcidr.rb @@ -0,0 +1,42 @@ +require 'ipaddr' + +# IPCidr object wrapper for IPAddr +module Puppet + module Util + class IPCidr < IPAddr + def initialize(ipaddr) + begin + super(ipaddr) + rescue ArgumentError => e + if e.message =~ /invalid address/ + raise ArgumentError, "Invalid address from IPAddr.new: #{ipaddr}" + else + raise e + end + end + end + + def netmask + _to_string(@mask_addr) + end + + def prefixlen + m = case @family + when Socket::AF_INET + IN4MASK + when Socket::AF_INET6 + IN6MASK + else + raise "unsupported address family" + end + return $1.length if /\A(1*)(0*)\z/ =~ (@mask_addr & m).to_s(2) + raise "bad addr_mask format" + end + + def cidr + cidr = sprintf("%s/%s", self.to_s, self.prefixlen) + cidr + end + end + end +end diff --git a/firewall/manifests/init.pp b/firewall/manifests/init.pp new file mode 100644 index 000000000..759f32823 --- /dev/null +++ b/firewall/manifests/init.pp @@ -0,0 +1,36 @@ +# = Class: firewall +# +# Manages packages and services required by the firewall type/provider. +# +# This class includes the appropriate sub-class for your operating system, +# where supported. +# +# == Parameters: +# +# [*ensure*] +# Ensure parameter passed onto Service[] resources. +# Default: running +# +class firewall ( + $ensure = running +) { + case $ensure { + /^(running|stopped)$/: { + # Do nothing. + } + default: { + fail("${title}: Ensure value '${ensure}' is not supported") + } + } + + case $::kernel { + 'Linux': { + class { "${title}::linux": + ensure => $ensure, + } + } + default: { + fail("${title}: Kernel '${::kernel}' is not currently supported") + } + } +} diff --git a/firewall/manifests/linux.pp b/firewall/manifests/linux.pp new file mode 100644 index 000000000..7c4f3a80b --- /dev/null +++ b/firewall/manifests/linux.pp @@ -0,0 +1,51 @@ +# = Class: firewall::linux +# +# Installs the `iptables` package for Linux operating systems and includes +# the appropriate sub-class for any distribution specific services and +# additional packages. +# +# == Parameters: +# +# [*ensure*] +# Ensure parameter passed onto Service[] resources. When `running` the +# service will be started on boot, and when `stopped` it will not. +# Default: running +# +class firewall::linux ( + $ensure = running +) { + $enable = $ensure ? { + running => true, + stopped => false, + } + + package { 'iptables': + ensure => present, + } + + case $::operatingsystem { + 'RedHat', 'CentOS', 'Fedora', 'Scientific', 'SL', 'SLC', 'Ascendos', + 'CloudLinux', 'PSBM', 'OracleLinux', 'OVS', 'OEL', 'Amazon', 'XenServer': { + class { "${title}::redhat": + ensure => $ensure, + enable => $enable, + require => Package['iptables'], + } + } + 'Debian', 'Ubuntu': { + class { "${title}::debian": + ensure => $ensure, + enable => $enable, + require => Package['iptables'], + } + } + 'Archlinux': { + class { "${title}::archlinux": + ensure => $ensure, + enable => $enable, + require => Package['iptables'], + } + } + default: {} + } +} diff --git a/firewall/manifests/linux/archlinux.pp b/firewall/manifests/linux/archlinux.pp new file mode 100644 index 000000000..546a5a80f --- /dev/null +++ b/firewall/manifests/linux/archlinux.pp @@ -0,0 +1,41 @@ +# = Class: firewall::linux::archlinux +# +# Manages `iptables` and `ip6tables` services, and creates files used for +# persistence, on Arch Linux systems. +# +# == Parameters: +# +# [*ensure*] +# Ensure parameter passed onto Service[] resources. +# Default: running +# +# [*enable*] +# Enable parameter passed onto Service[] resources. +# Default: true +# +class firewall::linux::archlinux ( + $ensure = 'running', + $enable = true +) { + service { 'iptables': + ensure => $ensure, + enable => $enable, + hasstatus => true, + } + + service { 'ip6tables': + ensure => $ensure, + enable => $enable, + hasstatus => true, + } + + file { '/etc/iptables/iptables.rules': + ensure => present, + before => Service['iptables'], + } + + file { '/etc/iptables/ip6tables.rules': + ensure => present, + before => Service['ip6tables'], + } +} diff --git a/firewall/manifests/linux/debian.pp b/firewall/manifests/linux/debian.pp new file mode 100644 index 000000000..4d28bc482 --- /dev/null +++ b/firewall/manifests/linux/debian.pp @@ -0,0 +1,44 @@ +# = Class: firewall::linux::debian +# +# Installs the `iptables-persistent` package for Debian-alike systems. This +# allows rules to be stored to file and restored on boot. +# +# == Parameters: +# +# [*ensure*] +# Ensure parameter passed onto Service[] resources. +# Default: running +# +# [*enable*] +# Enable parameter passed onto Service[] resources. +# Default: true +# +class firewall::linux::debian ( + $ensure = running, + $enable = true +) { + package { 'iptables-persistent': + ensure => present, + } + + if($::operatingsystemrelease =~ /^6\./ and $enable == true + and versioncmp($::iptables_persistent_version, '0.5.0') < 0 ) { + # This fixes a bug in the iptables-persistent LSB headers in 6.x, without it + # we lose idempotency + exec { 'iptables-persistent-enable': + logoutput => on_failure, + command => '/usr/sbin/update-rc.d iptables-persistent enable', + unless => '/usr/bin/test -f /etc/rcS.d/S*iptables-persistent', + require => Package['iptables-persistent'], + } + } else { + # This isn't a real service/daemon. The start action loads rules, so just + # needs to be called on system boot. + service { 'iptables-persistent': + ensure => undef, + enable => $enable, + hasstatus => true, + require => Package['iptables-persistent'], + } + } +} diff --git a/firewall/manifests/linux/redhat.pp b/firewall/manifests/linux/redhat.pp new file mode 100644 index 000000000..a4c00b647 --- /dev/null +++ b/firewall/manifests/linux/redhat.pp @@ -0,0 +1,40 @@ +# = Class: firewall::linux::redhat +# +# Manages the `iptables` service on RedHat-alike systems. +# +# == Parameters: +# +# [*ensure*] +# Ensure parameter passed onto Service[] resources. +# Default: running +# +# [*enable*] +# Enable parameter passed onto Service[] resources. +# Default: true +# +class firewall::linux::redhat ( + $ensure = running, + $enable = true +) { + + # RHEL 7 and later and Fedora 15 and later require the iptables-services + # package, which provides the /usr/libexec/iptables/iptables.init used by + # lib/puppet/util/firewall.rb. + if $::operatingsystem == RedHat and $::operatingsystemrelease >= 7 { + package { 'iptables-services': + ensure => present, + } + } + + if $::operatingsystem == Fedora and $::operatingsystemrelease >= 15 { + package { 'iptables-services': + ensure => present, + } + } + + service { 'iptables': + ensure => $ensure, + enable => $enable, + hasstatus => true, + } +} diff --git a/firewall/metadata.json b/firewall/metadata.json new file mode 100644 index 000000000..f4c45a68f --- /dev/null +++ b/firewall/metadata.json @@ -0,0 +1,64 @@ +{ + "name": "puppetlabs-firewall", + "version": "1.0.2", + "source": "https://github.com/puppetlabs/puppetlabs-firewall", + "author": "Puppet Labs", + "license": "Apache-2.0", + "project_page": "https://github.com/puppetlabs/puppetlabs-firewall", + "summary": "Manages Firewalls such as iptable", + "operatingsystem_support": [ + { + "operatingsystem": "RedHat", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "CentOS", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "OracleLinux", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "Scientific", + "operatingsystemrelease": [ + "5", + "6" + ] + }, + { + "operatingsystem": "SLES", + "operatingsystemrelease": [ + "11 SP1" + ] + }, + { + "operatingsystem": "Debian", + "operatingsystemrelease": [ + "6", + "7" + ] + }, + { + "operatingsystem": "Ubuntu", + "operatingsystemrelease": [ + "10.04", + "12.04" + ] + } + ], + "requirements": [ + { "name": "pe", "version_requirement": "3.2.x" }, + { "name": "puppet", "version_requirement": "3.x" } + ], + "dependencies": [] +} diff --git a/firewall/spec/acceptance/change_source_spec.rb b/firewall/spec/acceptance/change_source_spec.rb new file mode 100644 index 000000000..cdb4eab35 --- /dev/null +++ b/firewall/spec/acceptance/change_source_spec.rb @@ -0,0 +1,77 @@ +require 'spec_helper_acceptance' + +describe 'firewall type' do + describe 'reset' do + it 'deletes all rules' do + shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush') + end + end + + describe 'when unmanaged rules exist' do + it 'applies with 8.0.0.1 first' do + pp = <<-EOS + class { '::firewall': } + firewall { '101 test source changes': + proto => tcp, + port => '101', + action => accept, + source => '8.0.0.1', + } + firewall { '100 test source static': + proto => tcp, + port => '100', + action => accept, + source => '8.0.0.2', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'adds a unmanaged rule without a comment' do + shell('iptables -A INPUT -t filter -s 8.0.0.3/32 -p tcp -m multiport --ports 102 -j ACCEPT') + expect(shell('iptables-save').stdout).to match(/-A INPUT -s 8\.0\.0\.3(\/32)? -p tcp -m multiport --ports 102 -j ACCEPT/) + end + + it 'contains the changable 8.0.0.1 rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.1(\/32)? -p tcp -m multiport --ports 101 -m comment --comment "101 test source changes" -j ACCEPT/) + end + end + it 'contains the static 8.0.0.2 rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.2(\/32)? -p tcp -m multiport --ports 100 -m comment --comment "100 test source static" -j ACCEPT/) + end + end + + it 'changes to 8.0.0.4 second' do + pp = <<-EOS + class { '::firewall': } + firewall { '101 test source changes': + proto => tcp, + port => '101', + action => accept, + source => '8.0.0.4', + } + EOS + + expect(apply_manifest(pp, :catch_failures => true).stdout).to match(/Notice: \/Stage\[main\]\/Main\/Firewall\[101 test source changes\]\/source: source changed '8\.0\.0\.1\/32' to '8\.0\.0\.4\/32'/) + end + + it 'does not contain the old changing 8.0.0.1 rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/8\.0\.0\.1/) + end + end + it 'contains the staic 8.0.0.2 rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.2(\/32)? -p tcp -m multiport --ports 100 -m comment --comment "100 test source static" -j ACCEPT/) + end + end + it 'contains the changing new 8.0.0.4 rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -s 8\.0\.0\.4(\/32)? -p tcp -m multiport --ports 101 -m comment --comment "101 test source changes" -j ACCEPT/) + end + end + end +end diff --git a/firewall/spec/acceptance/class_spec.rb b/firewall/spec/acceptance/class_spec.rb new file mode 100644 index 000000000..aaf05a1f5 --- /dev/null +++ b/firewall/spec/acceptance/class_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper_acceptance' + +describe "firewall class:" do + it 'should run successfully' do + pp = "class { 'firewall': }" + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + it 'ensure => stopped:' do + pp = "class { 'firewall': ensure => stopped }" + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + it 'ensure => running:' do + pp = "class { 'firewall': ensure => running }" + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end +end diff --git a/firewall/spec/acceptance/firewall_spec.rb b/firewall/spec/acceptance/firewall_spec.rb new file mode 100644 index 000000000..486ce5694 --- /dev/null +++ b/firewall/spec/acceptance/firewall_spec.rb @@ -0,0 +1,1617 @@ +require 'spec_helper_acceptance' + +describe 'firewall type' do + + describe 'reset' do + it 'deletes all rules' do + shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush') + end + end + + describe 'name' do + context 'valid' do + it 'applies cleanly' do + pp = <<-EOS + class { '::firewall': } + firewall { '001 - test': ensure => present } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + end + + context 'invalid' do + it 'fails' do + pp = <<-EOS + class { '::firewall': } + firewall { 'test': ensure => present } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/Invalid value "test"./) + end + end + end + end + + describe 'ensure' do + context 'default' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '555 - test': + proto => tcp, + port => '555', + action => accept, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 555 -m comment --comment "555 - test" -j ACCEPT/) + end + end + end + + context 'present' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '555 - test': + ensure => present, + proto => tcp, + port => '555', + action => accept, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 555 -m comment --comment "555 - test" -j ACCEPT/) + end + end + end + + context 'absent' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '555 - test': + ensure => absent, + proto => tcp, + port => '555', + action => accept, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 555 -m comment --comment "555 - test" -j ACCEPT/) + end + end + end + end + + describe 'source' do + context '192.168.2.0/24' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '556 - test': + proto => tcp, + port => '556', + action => accept, + source => '192.168.2.0/24', + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -s 192.168.2.0\/(24|255\.255\.255\.0) -p tcp -m multiport --ports 556 -m comment --comment "556 - test" -j ACCEPT/) + end + end + end + + context '! 192.168.2.0/24' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '556 - test': + proto => tcp, + port => '556', + action => accept, + source => '! 192.168.2.0/24', + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT (! -s|-s !) 192.168.2.0\/(24|255\.255\.255\.0) -p tcp -m multiport --ports 556 -m comment --comment "556 - test" -j ACCEPT/) + end + end + end + + # Invalid address + context '256.168.2.0/24' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '556 - test': + proto => tcp, + port => '556', + action => accept, + source => '256.168.2.0/24', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/host_to_ip failed for 256.168.2.0\/(24|255\.255\.255\.0)/) + end + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -s 256.168.2.0\/(24|255\.255\.255\.0) -p tcp -m multiport --ports 556 -m comment --comment "556 - test" -j ACCEPT/) + end + end + end + end + + describe 'src_range' do + context '192.168.1.1-192.168.1.10' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '557 - test': + proto => tcp, + port => '557', + action => accept, + src_range => '192.168.1.1-192.168.1.10', + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m iprange --src-range 192.168.1.1-192.168.1.10 -m multiport --ports 557 -m comment --comment "557 - test" -j ACCEPT/) + end + end + end + + # Invalid IP + context '392.168.1.1-192.168.1.10' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '557 - test': + proto => tcp, + port => '557', + action => accept, + src_range => '392.168.1.1-192.168.1.10', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/Invalid value "392.168.1.1-192.168.1.10"/) + end + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m iprange --src-range 392.168.1.1-192.168.1.10 -m multiport --ports 557 -m comment --comment "557 - test" -j ACCEPT/) + end + end + end + end + + describe 'destination' do + context '192.168.2.0/24' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '558 - test': + proto => tcp, + port => '558', + action => accept, + destination => '192.168.2.0/24', + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -d 192.168.2.0\/(24|255\.255\.255\.0) -p tcp -m multiport --ports 558 -m comment --comment "558 - test" -j ACCEPT/) + end + end + end + + context '! 192.168.2.0/24' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '558 - test': + proto => tcp, + port => '558', + action => accept, + destination => '! 192.168.2.0/24', + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT (! -d|-d !) 192.168.2.0\/(24|255\.255\.255\.0) -p tcp -m multiport --ports 558 -m comment --comment "558 - test" -j ACCEPT/) + end + end + end + + # Invalid address + context '256.168.2.0/24' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '558 - test': + proto => tcp, + port => '558', + action => accept, + destination => '256.168.2.0/24', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/host_to_ip failed for 256.168.2.0\/(24|255\.255\.255\.0)/) + end + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -d 256.168.2.0\/(24|255\.255\.255\.0) -p tcp -m multiport --ports 558 -m comment --comment "558 - test" -j ACCEPT/) + end + end + end + end + + describe 'dst_range' do + context '192.168.1.1-192.168.1.10' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '559 - test': + proto => tcp, + port => '559', + action => accept, + dst_range => '192.168.1.1-192.168.1.10', + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m iprange --dst-range 192.168.1.1-192.168.1.10 -m multiport --ports 559 -m comment --comment "559 - test" -j ACCEPT/) + end + end + end + + # Invalid IP + context '392.168.1.1-192.168.1.10' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '559 - test': + proto => tcp, + port => '559', + action => accept, + dst_range => '392.168.1.1-192.168.1.10', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/Invalid value "392.168.1.1-192.168.1.10"/) + end + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m iprange --dst-range 392.168.1.1-192.168.1.10 -m multiport --ports 559 -m comment --comment "559 - test" -j ACCEPT/) + end + end + end + end + + describe 'sport' do + context 'single port' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '560 - test': + proto => tcp, + sport => '560', + action => accept, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --sports 560 -m comment --comment "560 - test" -j ACCEPT/) + end + end + end + + context 'multiple ports' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '560 - test': + proto => tcp, + sport => '560-561', + action => accept, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --sports 560:561 -m comment --comment "560 - test" -j ACCEPT/) + end + end + end + + context 'invalid ports' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '560 - test': + proto => tcp, + sport => '9999560-561', + action => accept, + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/invalid port\/service `9999560' specified/) + end + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --sports 9999560-561 -m comment --comment "560 - test" -j ACCEPT/) + end + end + end + end + + describe 'dport' do + context 'single port' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '561 - test': + proto => tcp, + dport => '561', + action => accept, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --dports 561 -m comment --comment "561 - test" -j ACCEPT/) + end + end + end + + context 'multiple ports' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '561 - test': + proto => tcp, + dport => '561-562', + action => accept, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --dports 561:562 -m comment --comment "561 - test" -j ACCEPT/) + end + end + end + + context 'invalid ports' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '561 - test': + proto => tcp, + dport => '9999561-562', + action => accept, + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/invalid port\/service `9999561' specified/) + end + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --dports 9999561-562 -m comment --comment "560 - test" -j ACCEPT/) + end + end + end + end + + describe 'port' do + context 'single port' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '562 - test': + proto => tcp, + port => '562', + action => accept, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 562 -m comment --comment "562 - test" -j ACCEPT/) + end + end + end + + context 'multiple ports' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '562 - test': + proto => tcp, + port => '562-563', + action => accept, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 562:563 -m comment --comment "562 - test" -j ACCEPT/) + end + end + end + + context 'invalid ports' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '562 - test': + proto => tcp, + port => '9999562-563', + action => accept, + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/invalid port\/service `9999562' specified/) + end + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 9999562-563 -m comment --comment "562 - test" -j ACCEPT/) + end + end + end + end + + ['dst_type', 'src_type'].each do |type| + describe "#{type}" do + context 'MULTICAST' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '563 - test': + proto => tcp, + action => accept, + #{type} => 'MULTICAST', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m addrtype\s.*\sMULTICAST -m comment --comment "563 - test" -j ACCEPT/) + end + end + end + + context 'BROKEN' do + it 'fails' do + pp = <<-EOS + class { '::firewall': } + firewall { '563 - test': + proto => tcp, + action => accept, + #{type} => 'BROKEN', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/Invalid value "BROKEN"./) + end + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m addrtype\s.*\sBROKEN -m comment --comment "563 - test" -j ACCEPT/) + end + end + end + end + end + + describe 'tcp_flags' do + context 'FIN,SYN ACK' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '564 - test': + proto => tcp, + action => accept, + tcp_flags => 'FIN,SYN ACK', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m tcp --tcp-flags FIN,SYN ACK -m comment --comment "564 - test" -j ACCEPT/) + end + end + end + end + + describe 'chain' do + context 'INPUT' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '565 - test': + proto => tcp, + action => accept, + chain => 'FORWARD', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A FORWARD -p tcp -m comment --comment "565 - test" -j ACCEPT/) + end + end + end + end + + describe 'table' do + context 'mangle' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '566 - test': + proto => tcp, + action => accept, + table => 'mangle', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save -t mangle') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m comment --comment "566 - test" -j ACCEPT/) + end + end + end + context 'nat' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '566 - test2': + proto => tcp, + action => accept, + table => 'nat', + chain => 'OUTPUT', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should not contain the rule' do + shell('iptables-save -t nat') do |r| + expect(r.stdout).to match(/-A OUTPUT -p tcp -m comment --comment "566 - test2" -j ACCEPT/) + end + end + end + end + + describe 'jump' do + after :all do + iptables_flush_all_tables + expect(shell('iptables -t filter -X TEST').stderr).to eq("") + end + + context 'MARK' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewallchain { 'TEST:filter:IPv4': + ensure => present, + } + firewall { '567 - test': + proto => tcp, + chain => 'INPUT', + jump => 'TEST', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m comment --comment "567 - test" -j TEST/) + end + end + end + + context 'jump and apply' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewallchain { 'TEST:filter:IPv4': + ensure => present, + } + firewall { '568 - test': + proto => tcp, + chain => 'INPUT', + action => 'accept', + jump => 'TEST', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/Only one of the parameters 'action' and 'jump' can be set/) + end + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m comment --comment "568 - test" -j TEST/) + end + end + end + end + + describe 'tosource' do + context '192.168.1.1' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '568 - test': + proto => tcp, + table => 'nat', + chain => 'POSTROUTING', + jump => 'SNAT', + tosource => '192.168.1.1', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save -t nat') do |r| + expect(r.stdout).to match(/A POSTROUTING -p tcp -m comment --comment "568 - test" -j SNAT --to-source 192.168.1.1/) + end + end + end + end + + describe 'todest' do + context '192.168.1.1' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '569 - test': + proto => tcp, + table => 'nat', + chain => 'PREROUTING', + jump => 'DNAT', + source => '200.200.200.200', + todest => '192.168.1.1', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save -t nat') do |r| + expect(r.stdout).to match(/-A PREROUTING -s 200.200.200.200(\/32)? -p tcp -m comment --comment "569 - test" -j DNAT --to-destination 192.168.1.1/) + end + end + end + end + + describe 'toports' do + context '192.168.1.1' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '570 - test': + proto => icmp, + table => 'nat', + chain => 'PREROUTING', + jump => 'REDIRECT', + toports => '2222', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save -t nat') do |r| + expect(r.stdout).to match(/-A PREROUTING -p icmp -m comment --comment "570 - test" -j REDIRECT --to-ports 2222/) + end + end + end + end + + # RHEL5 does not support --random + if default['platform'] !~ /el-5/ + describe 'random' do + context '192.168.1.1' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '570 - test 2': + proto => all, + table => 'nat', + chain => 'POSTROUTING', + jump => 'MASQUERADE', + source => '172.30.0.0/16', + random => true + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'should contain the rule' do + shell('iptables-save -t nat') do |r| + expect(r.stdout).to match(/-A POSTROUTING -s 172\.30\.0\.0\/16 -m comment --comment "570 - test 2" -j MASQUERADE --random/) + end + end + end + end + end + + describe 'icmp' do + context 'any' do + it 'fails' do + pp = <<-EOS + class { '::firewall': } + firewall { '571 - test': + proto => icmp, + icmp => 'any', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/This behaviour should be achieved by omitting or undefining the ICMP parameter/) + end + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p icmp -m comment --comment "570 - test" -m icmp --icmp-type 11/) + end + end + end + end + + #iptables version 1.3.5 is not suppored by the ip6tables provider + if default['platform'] !~ /el-5/ + describe 'hop_limit' do + context '5' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '571 - test': + ensure => present, + proto => tcp, + port => '571', + action => accept, + hop_limit => '5', + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 571 -m comment --comment "571 - test" -m hl --hl-eq 5 -j ACCEPT/) + end + end + end + + context 'invalid' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '571 - test': + ensure => present, + proto => tcp, + port => '571', + action => accept, + hop_limit => 'invalid', + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/Invalid value "invalid"./) + end + end + + it 'should not contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 571 -m comment --comment "571 - test" -m hl --hl-eq invalid -j ACCEPT/) + end + end + end + end + + describe 'ishasmorefrags' do + context 'true' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '587 - test': + ensure => present, + proto => tcp, + port => '587', + action => accept, + ishasmorefrags => true, + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/A INPUT -p tcp -m frag --fragid 0 --fragmore -m multiport --ports 587 -m comment --comment "587 - test" -j ACCEPT/) + end + end + end + + context 'false' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '588 - test': + ensure => present, + proto => tcp, + port => '588', + action => accept, + ishasmorefrags => false, + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 588 -m comment --comment "588 - test" -j ACCEPT/) + end + end + end + end + + describe 'islastfrag' do + context 'true' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '589 - test': + ensure => present, + proto => tcp, + port => '589', + action => accept, + islastfrag => true, + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m frag --fragid 0 --fraglast -m multiport --ports 589 -m comment --comment "589 - test" -j ACCEPT/) + end + end + end + + context 'false' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '590 - test': + ensure => present, + proto => tcp, + port => '590', + action => accept, + islastfrag => false, + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 590 -m comment --comment "590 - test" -j ACCEPT/) + end + end + end + end + + describe 'isfirstfrag' do + context 'true' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '591 - test': + ensure => present, + proto => tcp, + port => '591', + action => accept, + isfirstfrag => true, + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m frag --fragid 0 --fragfirst -m multiport --ports 591 -m comment --comment "591 - test" -j ACCEPT/) + end + end + end + + context 'false' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '592 - test': + ensure => present, + proto => tcp, + port => '592', + action => accept, + isfirstfrag => false, + provider => 'ip6tables', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 592 -m comment --comment "592 - test" -j ACCEPT/) + end + end + end + end + end + + describe 'limit' do + context '500/sec' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '572 - test': + ensure => present, + proto => tcp, + port => '572', + action => accept, + limit => '500/sec', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 572 -m comment --comment "572 - test" -m limit --limit 500\/sec -j ACCEPT/) + end + end + end + end + + describe 'burst' do + context '500' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '573 - test': + ensure => present, + proto => tcp, + port => '573', + action => accept, + limit => '500/sec', + burst => '1500', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 573 -m comment --comment "573 - test" -m limit --limit 500\/sec --limit-burst 1500 -j ACCEPT/) + end + end + end + + context 'invalid' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '571 - test': + ensure => present, + proto => tcp, + port => '571', + action => accept, + limit => '500/sec', + burst => '1500/sec', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/Invalid value "1500\/sec"./) + end + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 573 -m comment --comment "573 - test" -m limit --limit 500\/sec --limit-burst 1500\/sec -j ACCEPT/) + end + end + end + end + + describe 'uid' do + context 'nobody' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '574 - test': + ensure => present, + proto => tcp, + chain => 'OUTPUT', + port => '574', + action => accept, + uid => 'nobody', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A OUTPUT -p tcp -m owner --uid-owner (nobody|\d+) -m multiport --ports 574 -m comment --comment "574 - test" -j ACCEPT/) + end + end + end + end + + describe 'gid' do + context 'root' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '575 - test': + ensure => present, + proto => tcp, + chain => 'OUTPUT', + port => '575', + action => accept, + gid => 'root', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A OUTPUT -p tcp -m owner --gid-owner (root|\d+) -m multiport --ports 575 -m comment --comment "575 - test" -j ACCEPT/) + end + end + end + end + + #iptables version 1.3.5 does not support masks on MARK rules + if default['platform'] !~ /el-5/ + describe 'set_mark' do + context '0x3e8/0xffffffff' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '580 - test': + ensure => present, + chain => 'OUTPUT', + proto => tcp, + port => '580', + jump => 'MARK', + table => 'mangle', + set_mark => '0x3e8/0xffffffff', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save -t mangle') do |r| + expect(r.stdout).to match(/-A OUTPUT -p tcp -m multiport --ports 580 -m comment --comment "580 - test" -j MARK --set-xmark 0x3e8\/0xffffffff/) + end + end + end + end + end + + describe 'pkttype' do + context 'multicast' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '581 - test': + ensure => present, + proto => tcp, + port => '581', + action => accept, + pkttype => 'multicast', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 581 -m pkttype --pkt-type multicast -m comment --comment "581 - test" -j ACCEPT/) + end + end + end + + context 'test' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '582 - test': + ensure => present, + proto => tcp, + port => '582', + action => accept, + pkttype => 'test', + } + EOS + + apply_manifest(pp, :expect_failures => true) do |r| + expect(r.stderr).to match(/Invalid value "test"./) + end + end + + it 'should not contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/-A INPUT -p tcp -m multiport --ports 582 -m pkttype --pkt-type multicast -m comment --comment "582 - test" -j ACCEPT/) + end + end + end + end + + describe 'isfragment' do + context 'true' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '583 - test': + ensure => present, + proto => tcp, + port => '583', + action => accept, + isfragment => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -f -m multiport --ports 583 -m comment --comment "583 - test" -j ACCEPT/) + end + end + end + + context 'false' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '584 - test': + ensure => present, + proto => tcp, + port => '584', + action => accept, + isfragment => false, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -p tcp -m multiport --ports 584 -m comment --comment "584 - test" -j ACCEPT/) + end + end + end + end + + # RHEL5/SLES does not support -m socket + describe 'socket', :unless => (default['platform'] =~ /el-5/ or fact('operatingsystem') == 'SLES') do + context 'true' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '585 - test': + ensure => present, + proto => tcp, + port => '585', + action => accept, + chain => 'PREROUTING', + table => 'nat', + socket => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save -t nat') do |r| + expect(r.stdout).to match(/-A PREROUTING -p tcp -m multiport --ports 585 -m socket -m comment --comment "585 - test" -j ACCEPT/) + end + end + end + + context 'false' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '586 - test': + ensure => present, + proto => tcp, + port => '586', + action => accept, + chain => 'PREROUTING', + table => 'nat', + socket => false, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save -t nat') do |r| + expect(r.stdout).to match(/-A PREROUTING -p tcp -m multiport --ports 586 -m comment --comment "586 - test" -j ACCEPT/) + end + end + end + end + + describe 'ipsec_policy' do + context 'ipsec' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '593 - test': + ensure => 'present', + action => 'reject', + chain => 'OUTPUT', + destination => '20.0.0.0/8', + ipsec_dir => 'out', + ipsec_policy => 'ipsec', + proto => 'all', + reject => 'icmp-net-unreachable', + table => 'filter', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A OUTPUT -d 20.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "593 - test" -m policy --dir out --pol ipsec -j REJECT --reject-with icmp-net-unreachable/) + end + end + end + + context 'none' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '594 - test': + ensure => 'present', + action => 'reject', + chain => 'OUTPUT', + destination => '20.0.0.0/8', + ipsec_dir => 'out', + ipsec_policy => 'none', + proto => 'all', + reject => 'icmp-net-unreachable', + table => 'filter', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A OUTPUT -d 20.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "594 - test" -m policy --dir out --pol none -j REJECT --reject-with icmp-net-unreachable/) + end + end + end + end + + describe 'ipsec_dir' do + context 'out' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '595 - test': + ensure => 'present', + action => 'reject', + chain => 'OUTPUT', + destination => '20.0.0.0/8', + ipsec_dir => 'out', + ipsec_policy => 'ipsec', + proto => 'all', + reject => 'icmp-net-unreachable', + table => 'filter', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A OUTPUT -d 20.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "595 - test" -m policy --dir out --pol ipsec -j REJECT --reject-with icmp-net-unreachable/) + end + end + end + + context 'in' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '596 - test': + ensure => 'present', + action => 'reject', + chain => 'INPUT', + destination => '20.0.0.0/8', + ipsec_dir => 'in', + ipsec_policy => 'none', + proto => 'all', + reject => 'icmp-net-unreachable', + table => 'filter', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -d 20.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "596 - test" -m policy --dir in --pol none -j REJECT --reject-with icmp-net-unreachable/) + end + end + end + end + + describe 'recent' do + context 'set' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '597 - test': + ensure => 'present', + chain => 'INPUT', + destination => '30.0.0.0/8', + proto => 'all', + table => 'filter', + recent => 'set', + rdest => true, + rname => 'list1', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -d 30.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "597 - test" -m recent --set --name list1 --rdest/) + end + end + end + + context 'rcheck' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '598 - test': + ensure => 'present', + chain => 'INPUT', + destination => '30.0.0.0/8', + proto => 'all', + table => 'filter', + recent => 'rcheck', + rsource => true, + rname => 'list1', + rseconds => 60, + rhitcount => 5, + rttl => true, + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -d 30.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "598 - test" -m recent --rcheck --seconds 60 --hitcount 5 --rttl --name list1 --rsource/) + end + end + end + + context 'update' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '599 - test': + ensure => 'present', + chain => 'INPUT', + destination => '30.0.0.0/8', + proto => 'all', + table => 'filter', + recent => 'update', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -d 30.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "599 - test" -m recent --update/) + end + end + end + + context 'remove' do + it 'applies' do + pp = <<-EOS + class { '::firewall': } + firewall { '600 - test': + ensure => 'present', + chain => 'INPUT', + destination => '30.0.0.0/8', + proto => 'all', + table => 'filter', + recent => 'remove', + } + EOS + + apply_manifest(pp, :catch_failures => true) + end + + it 'should contain the rule' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/-A INPUT -d 30.0.0.0\/(8|255\.0\.0\.0) -m comment --comment "600 - test" -m recent --remove/) + end + end + end + end + + describe 'reset' do + it 'deletes all rules' do + shell('ip6tables --flush') + shell('iptables --flush; iptables -t nat --flush; iptables -t mangle --flush') + end + end + +end diff --git a/firewall/spec/acceptance/firewallchain_spec.rb b/firewall/spec/acceptance/firewallchain_spec.rb new file mode 100644 index 000000000..757336a75 --- /dev/null +++ b/firewall/spec/acceptance/firewallchain_spec.rb @@ -0,0 +1,125 @@ +require 'spec_helper_acceptance' + +describe 'puppet resource firewallchain command:' do + before :all do + iptables_flush_all_tables + end + describe 'ensure' do + context 'present' do + it 'applies cleanly' do + pp = <<-EOS + firewallchain { 'MY_CHAIN:filter:IPv4': + ensure => present, + } + EOS + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'finds the chain' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/MY_CHAIN/) + end + end + end + + context 'absent' do + it 'applies cleanly' do + pp = <<-EOS + firewallchain { 'MY_CHAIN:filter:IPv4': + ensure => absent, + } + EOS + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'fails to find the chain' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/MY_CHAIN/) + end + end + end + end + + # XXX purge => false is not yet implemented + #context 'adding a firewall rule to a chain:' do + # it 'applies cleanly' do + # pp = <<-EOS + # firewallchain { 'MY_CHAIN:filter:IPv4': + # ensure => present, + # } + # firewall { '100 my rule': + # chain => 'MY_CHAIN', + # action => 'accept', + # proto => 'tcp', + # dport => 5000, + # } + # EOS + # # Run it twice and test for idempotency + # apply_manifest(pp, :catch_failures => true) + # apply_manifest(pp, :catch_changes => true) + # end + #end + + #context 'not purge firewallchain chains:' do + # it 'does not purge the rule' do + # pp = <<-EOS + # firewallchain { 'MY_CHAIN:filter:IPv4': + # ensure => present, + # purge => false, + # before => Resources['firewall'], + # } + # resources { 'firewall': + # purge => true, + # } + # EOS + # # Run it twice and test for idempotency + # apply_manifest(pp, :catch_failures => true) do |r| + # expect(r.stdout).to_not match(/removed/) + # expect(r.stderr).to eq('') + # end + # apply_manifest(pp, :catch_changes => true) + # end + + # it 'still has the rule' do + # pp = <<-EOS + # firewall { '100 my rule': + # chain => 'MY_CHAIN', + # action => 'accept', + # proto => 'tcp', + # dport => 5000, + # } + # EOS + # # Run it twice and test for idempotency + # apply_manifest(pp, :catch_changes => true) + # end + #end + + describe 'policy' do + after :all do + shell('iptables -t filter -P FORWARD ACCEPT') + end + + context 'DROP' do + it 'applies cleanly' do + pp = <<-EOS + firewallchain { 'FORWARD:filter:IPv4': + policy => 'drop', + } + EOS + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'finds the chain' do + shell('iptables-save') do |r| + expect(r.stdout).to match(/FORWARD DROP/) + end + end + end + end +end diff --git a/firewall/spec/acceptance/ip6_fragment_spec.rb b/firewall/spec/acceptance/ip6_fragment_spec.rb new file mode 100644 index 000000000..bfce0e607 --- /dev/null +++ b/firewall/spec/acceptance/ip6_fragment_spec.rb @@ -0,0 +1,114 @@ +require 'spec_helper_acceptance' + +if default['platform'] =~ /el-5/ + describe "firewall ip6tables doesn't work on 1.3.5 because --comment is missing" do + before :all do + ip6tables_flush_all_tables + end + + it "can't use ip6tables" do + pp = <<-EOS + class { '::firewall': } + firewall { '599 - test': + ensure => present, + proto => 'tcp', + provider => 'ip6tables', + } + EOS + expect(apply_manifest(pp, :expect_failures => true).stderr).to match(/ip6tables provider is not supported/) + end + end +else + describe 'firewall ishasmorefrags/islastfrag/isfirstfrag properties' do + before :all do + ip6tables_flush_all_tables + end + + shared_examples "is idempotent" do |values, line_match| + it "changes the values to #{values}" do + pp = <<-EOS + class { '::firewall': } + firewall { '599 - test': + ensure => present, + proto => 'tcp', + provider => 'ip6tables', + #{values} + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/#{line_match}/) + end + end + end + shared_examples "doesn't change" do |values, line_match| + it "doesn't change the values to #{values}" do + pp = <<-EOS + class { '::firewall': } + firewall { '599 - test': + ensure => present, + proto => 'tcp', + provider => 'ip6tables', + #{values} + } + EOS + + apply_manifest(pp, :catch_changes => true) + + shell('ip6tables-save') do |r| + expect(r.stdout).to match(/#{line_match}/) + end + end + end + + describe 'adding a rule' do + context 'when unset' do + before :all do + ip6tables_flush_all_tables + end + it_behaves_like 'is idempotent', '', /-A INPUT -p tcp -m comment --comment "599 - test"/ + end + context 'when set to true' do + before :all do + ip6tables_flush_all_tables + end + it_behaves_like "is idempotent", 'ishasmorefrags => true, islastfrag => true, isfirstfrag => true', /-A INPUT -p tcp -m frag --fragid 0 --fragmore -m frag --fragid 0 --fraglast -m frag --fragid 0 --fragfirst -m comment --comment "599 - test"/ + end + context 'when set to false' do + before :all do + ip6tables_flush_all_tables + end + it_behaves_like "is idempotent", 'ishasmorefrags => false, islastfrag => false, isfirstfrag => false', /-A INPUT -p tcp -m comment --comment "599 - test"/ + end + end + describe 'editing a rule' do + context 'when unset or false' do + before :each do + ip6tables_flush_all_tables + shell('ip6tables -A INPUT -p tcp -m comment --comment "599 - test"') + end + context 'and current value is false' do + it_behaves_like "doesn't change", 'ishasmorefrags => false, islastfrag => false, isfirstfrag => false', /-A INPUT -p tcp -m comment --comment "599 - test"/ + end + context 'and current value is true' do + it_behaves_like "is idempotent", 'ishasmorefrags => true, islastfrag => true, isfirstfrag => true', /-A INPUT -p tcp -m frag --fragid 0 --fragmore -m frag --fragid 0 --fraglast -m frag --fragid 0 --fragfirst -m comment --comment "599 - test"/ + end + end + context 'when set to true' do + before :each do + ip6tables_flush_all_tables + shell('ip6tables -A INPUT -p tcp -m frag --fragid 0 --fragmore -m frag --fragid 0 --fraglast -m frag --fragid 0 --fragfirst -m comment --comment "599 - test"') + end + context 'and current value is false' do + it_behaves_like "is idempotent", 'ishasmorefrags => false, islastfrag => false, isfirstfrag => false', /-A INPUT -p tcp -m comment --comment "599 - test"/ + end + context 'and current value is true' do + it_behaves_like "doesn't change", 'ishasmorefrags => true, islastfrag => true, isfirstfrag => true', /-A INPUT -p tcp -m frag --fragid 0 --fragmore -m frag --fragid 0 --fraglast -m frag --fragid 0 --fragfirst -m comment --comment "599 - test"/ + end + end + end + end +end diff --git a/firewall/spec/acceptance/isfragment_spec.rb b/firewall/spec/acceptance/isfragment_spec.rb new file mode 100644 index 000000000..7fdedc287 --- /dev/null +++ b/firewall/spec/acceptance/isfragment_spec.rb @@ -0,0 +1,92 @@ +require 'spec_helper_acceptance' + +describe 'firewall isfragment property' do + before :all do + iptables_flush_all_tables + end + + shared_examples "is idempotent" do |value, line_match| + it "changes the value to #{value}" do + pp = <<-EOS + class { '::firewall': } + firewall { '597 - test': + ensure => present, + proto => 'tcp', + #{value} + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + + shell('iptables-save') do |r| + expect(r.stdout).to match(/#{line_match}/) + end + end + end + shared_examples "doesn't change" do |value, line_match| + it "doesn't change the value to #{value}" do + pp = <<-EOS + class { '::firewall': } + firewall { '597 - test': + ensure => present, + proto => 'tcp', + #{value} + } + EOS + + apply_manifest(pp, :catch_changes => true) + + shell('iptables-save') do |r| + expect(r.stdout).to match(/#{line_match}/) + end + end + end + + describe 'adding a rule' do + context 'when unset' do + before :all do + iptables_flush_all_tables + end + it_behaves_like 'is idempotent', '', /-A INPUT -p tcp -m comment --comment "597 - test"/ + end + context 'when set to true' do + before :all do + iptables_flush_all_tables + end + it_behaves_like 'is idempotent', 'isfragment => true,', /-A INPUT -p tcp -f -m comment --comment "597 - test"/ + end + context 'when set to false' do + before :all do + iptables_flush_all_tables + end + it_behaves_like "is idempotent", 'isfragment => false,', /-A INPUT -p tcp -m comment --comment "597 - test"/ + end + end + describe 'editing a rule' do + context 'when unset or false' do + before :each do + iptables_flush_all_tables + shell('iptables -A INPUT -p tcp -m comment --comment "597 - test"') + end + context 'and current value is false' do + it_behaves_like "doesn't change", 'isfragment => false,', /-A INPUT -p tcp -m comment --comment "597 - test"/ + end + context 'and current value is true' do + it_behaves_like "is idempotent", 'isfragment => true,', /-A INPUT -p tcp -f -m comment --comment "597 - test"/ + end + end + context 'when set to true' do + before :each do + iptables_flush_all_tables + shell('iptables -A INPUT -p tcp -f -m comment --comment "597 - test"') + end + context 'and current value is false' do + it_behaves_like "is idempotent", 'isfragment => false,', /-A INPUT -p tcp -m comment --comment "597 - test"/ + end + context 'and current value is true' do + it_behaves_like "doesn't change", 'isfragment => true,', /-A INPUT -p tcp -f -m comment --comment "597 - test"/ + end + end + end +end diff --git a/firewall/spec/acceptance/nodesets/centos-59-x64-pe.yml b/firewall/spec/acceptance/nodesets/centos-59-x64-pe.yml new file mode 100644 index 000000000..3a6470bea --- /dev/null +++ b/firewall/spec/acceptance/nodesets/centos-59-x64-pe.yml @@ -0,0 +1,12 @@ +HOSTS: + centos-59-x64: + roles: + - master + - database + - console + platform: el-5-x86_64 + box : centos-59-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: pe diff --git a/firewall/spec/acceptance/nodesets/centos-59-x64.yml b/firewall/spec/acceptance/nodesets/centos-59-x64.yml new file mode 100644 index 000000000..b41a94716 --- /dev/null +++ b/firewall/spec/acceptance/nodesets/centos-59-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-59-x64: + roles: + - master + platform: el-5-x86_64 + box : centos-59-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-59-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/firewall/spec/acceptance/nodesets/centos-64-x64-fusion.yml b/firewall/spec/acceptance/nodesets/centos-64-x64-fusion.yml new file mode 100644 index 000000000..d5166735e --- /dev/null +++ b/firewall/spec/acceptance/nodesets/centos-64-x64-fusion.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-64-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-64-x64-fusion503-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-fusion503-nocm.box + hypervisor : fusion +CONFIG: + type: foss diff --git a/firewall/spec/acceptance/nodesets/centos-64-x64-pe.yml b/firewall/spec/acceptance/nodesets/centos-64-x64-pe.yml new file mode 100644 index 000000000..7d9242f1b --- /dev/null +++ b/firewall/spec/acceptance/nodesets/centos-64-x64-pe.yml @@ -0,0 +1,12 @@ +HOSTS: + centos-64-x64: + roles: + - master + - database + - dashboard + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: pe diff --git a/firewall/spec/acceptance/nodesets/centos-64-x64.yml b/firewall/spec/acceptance/nodesets/centos-64-x64.yml new file mode 100644 index 000000000..05540ed8c --- /dev/null +++ b/firewall/spec/acceptance/nodesets/centos-64-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-64-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/firewall/spec/acceptance/nodesets/debian-607-x64.yml b/firewall/spec/acceptance/nodesets/debian-607-x64.yml new file mode 100644 index 000000000..4c8be42d0 --- /dev/null +++ b/firewall/spec/acceptance/nodesets/debian-607-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + debian-607-x64: + roles: + - master + platform: debian-6-amd64 + box : debian-607-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-607-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: git diff --git a/firewall/spec/acceptance/nodesets/debian-70rc1-x64.yml b/firewall/spec/acceptance/nodesets/debian-70rc1-x64.yml new file mode 100644 index 000000000..19181c123 --- /dev/null +++ b/firewall/spec/acceptance/nodesets/debian-70rc1-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + debian-70rc1-x64: + roles: + - master + platform: debian-7-amd64 + box : debian-70rc1-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/debian-70rc1-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: git diff --git a/firewall/spec/acceptance/nodesets/default.yml b/firewall/spec/acceptance/nodesets/default.yml new file mode 100644 index 000000000..05540ed8c --- /dev/null +++ b/firewall/spec/acceptance/nodesets/default.yml @@ -0,0 +1,10 @@ +HOSTS: + centos-64-x64: + roles: + - master + platform: el-6-x86_64 + box : centos-64-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/centos-64-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/firewall/spec/acceptance/nodesets/fedora-18-x64.yml b/firewall/spec/acceptance/nodesets/fedora-18-x64.yml new file mode 100644 index 000000000..624b53716 --- /dev/null +++ b/firewall/spec/acceptance/nodesets/fedora-18-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + fedora-18-x64: + roles: + - master + platform: fedora-18-x86_64 + box : fedora-18-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/fedora-18-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: git diff --git a/firewall/spec/acceptance/nodesets/sles-11sp1-x64.yml b/firewall/spec/acceptance/nodesets/sles-11sp1-x64.yml new file mode 100644 index 000000000..554c37a50 --- /dev/null +++ b/firewall/spec/acceptance/nodesets/sles-11sp1-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + sles-11sp1-x64: + roles: + - master + platform: sles-11-x86_64 + box : sles-11sp1-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/sles-11sp1-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: git diff --git a/firewall/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml b/firewall/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml new file mode 100644 index 000000000..5047017e6 --- /dev/null +++ b/firewall/spec/acceptance/nodesets/ubuntu-server-10044-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + ubuntu-server-10044-x64: + roles: + - master + platform: ubuntu-10.04-amd64 + box : ubuntu-server-10044-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-10044-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: git diff --git a/firewall/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml b/firewall/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml new file mode 100644 index 000000000..d065b304f --- /dev/null +++ b/firewall/spec/acceptance/nodesets/ubuntu-server-12042-x64.yml @@ -0,0 +1,10 @@ +HOSTS: + ubuntu-server-12042-x64: + roles: + - master + platform: ubuntu-12.04-amd64 + box : ubuntu-server-12042-x64-vbox4210-nocm + box_url : http://puppet-vagrant-boxes.puppetlabs.com/ubuntu-server-12042-x64-vbox4210-nocm.box + hypervisor : vagrant +CONFIG: + type: foss diff --git a/firewall/spec/acceptance/params_spec.rb b/firewall/spec/acceptance/params_spec.rb new file mode 100644 index 000000000..c0f93ad21 --- /dev/null +++ b/firewall/spec/acceptance/params_spec.rb @@ -0,0 +1,154 @@ +require 'spec_helper_acceptance' + +describe "param based tests:" do + # Takes a hash and converts it into a firewall resource + def pp(params) + name = params.delete('name') || '100 test' + pm = <<-EOS +firewall { '#{name}': + EOS + + params.each do |k,v| + pm += <<-EOS + #{k} => #{v}, + EOS + end + + pm += <<-EOS +} + EOS + pm + end + + it 'test various params', :unless => (default['platform'].match(/el-5/) || fact('operatingsystem') == 'SLES') do + iptables_flush_all_tables + + ppm = pp({ + 'table' => "'raw'", + 'socket' => 'true', + 'chain' => "'PREROUTING'", + 'jump' => 'LOG', + 'log_level' => 'debug', + }) + + expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2) + expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero + end + + it 'test log rule' do + iptables_flush_all_tables + + ppm = pp({ + 'name' => '998 log all', + 'proto' => 'all', + 'jump' => 'LOG', + 'log_level' => 'debug', + }) + expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2) + expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero + end + + it 'test log rule - changing names' do + iptables_flush_all_tables + + ppm1 = pp({ + 'name' => '004 log all INVALID packets', + 'chain' => 'INPUT', + 'proto' => 'all', + 'ctstate' => 'INVALID', + 'jump' => 'LOG', + 'log_level' => '3', + 'log_prefix' => '"IPTABLES dropped invalid: "', + }) + + ppm2 = pp({ + 'name' => '003 log all INVALID packets', + 'chain' => 'INPUT', + 'proto' => 'all', + 'ctstate' => 'INVALID', + 'jump' => 'LOG', + 'log_level' => '3', + 'log_prefix' => '"IPTABLES dropped invalid: "', + }) + + expect(apply_manifest(ppm1, :catch_failures => true).exit_code).to eq(2) + + ppm = <<-EOS + "\n" + ppm2 + resources { 'firewall': + purge => true, + } + EOS + expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2) + end + + it 'test chain - changing names' do + iptables_flush_all_tables + + ppm1 = pp({ + 'name' => '004 with a chain', + 'chain' => 'INPUT', + 'proto' => 'all', + }) + + ppm2 = pp({ + 'name' => '004 with a chain', + 'chain' => 'OUTPUT', + 'proto' => 'all', + }) + + apply_manifest(ppm1, :expect_changes => true) + + ppm = <<-EOS + "\n" + ppm2 + resources { 'firewall': + purge => true, + } + EOS + expect(apply_manifest(ppm2, :expect_failures => true).stderr).to match(/is not supported/) + end + + it 'test log rule - idempotent' do + iptables_flush_all_tables + + ppm1 = pp({ + 'name' => '004 log all INVALID packets', + 'chain' => 'INPUT', + 'proto' => 'all', + 'ctstate' => 'INVALID', + 'jump' => 'LOG', + 'log_level' => '3', + 'log_prefix' => '"IPTABLES dropped invalid: "', + }) + + expect(apply_manifest(ppm1, :catch_failures => true).exit_code).to eq(2) + expect(apply_manifest(ppm1, :catch_failures => true).exit_code).to be_zero + end + + it 'test src_range rule' do + iptables_flush_all_tables + + ppm = pp({ + 'name' => '997 block src ip range', + 'chain' => 'INPUT', + 'proto' => 'all', + 'action' => 'drop', + 'src_range' => '"10.0.0.1-10.0.0.10"', + }) + expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2) + expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero + end + + it 'test dst_range rule' do + iptables_flush_all_tables + + ppm = pp({ + 'name' => '998 block dst ip range', + 'chain' => 'INPUT', + 'proto' => 'all', + 'action' => 'drop', + 'dst_range' => '"10.0.0.2-10.0.0.20"', + }) + expect(apply_manifest(ppm, :catch_failures => true).exit_code).to eq(2) + expect(apply_manifest(ppm, :catch_failures => true).exit_code).to be_zero + end + +end diff --git a/firewall/spec/acceptance/purge_spec.rb b/firewall/spec/acceptance/purge_spec.rb new file mode 100644 index 000000000..f62b14f93 --- /dev/null +++ b/firewall/spec/acceptance/purge_spec.rb @@ -0,0 +1,124 @@ +require 'spec_helper_acceptance' + +describe "purge tests:" do + context('resources purge') do + before(:all) do + iptables_flush_all_tables + + shell('iptables -A INPUT -s 1.2.1.2') + shell('iptables -A INPUT -s 1.2.1.2') + end + + it 'make sure duplicate existing rules get purged' do + + pp = <<-EOS + class { 'firewall': } + resources { 'firewall': + purge => true, + } + EOS + + apply_manifest(pp, :expect_changes => true) + end + + it 'saves' do + shell('iptables-save') do |r| + expect(r.stdout).to_not match(/1\.2\.1\.2/) + expect(r.stderr).to eq("") + end + end + end + + context('chain purge') do + before(:each) do + iptables_flush_all_tables + + shell('iptables -A INPUT -p tcp -s 1.2.1.1') + shell('iptables -A INPUT -p udp -s 1.2.1.1') + shell('iptables -A OUTPUT -s 1.2.1.2 -m comment --comment "010 output-1.2.1.2"') + end + + it 'purges only the specified chain' do + pp = <<-EOS + class { 'firewall': } + firewallchain { 'INPUT:filter:IPv4': + purge => true, + } + EOS + + apply_manifest(pp, :expect_changes => true) + + shell('iptables-save') do |r| + expect(r.stdout).to match(/010 output-1\.2\.1\.2/) + expect(r.stdout).to_not match(/1\.2\.1\.1/) + expect(r.stderr).to eq("") + end + end + + it 'ignores managed rules' do + pp = <<-EOS + class { 'firewall': } + firewallchain { 'OUTPUT:filter:IPv4': + purge => true, + } + firewall { '010 output-1.2.1.2': + chain => 'OUTPUT', + proto => 'all', + source => '1.2.1.2', + } + EOS + + apply_manifest(pp, :catch_changes => true) + end + + it 'ignores specified rules' do + pp = <<-EOS + class { 'firewall': } + firewallchain { 'INPUT:filter:IPv4': + purge => true, + ignore => [ + '-s 1\.2\.1\.1', + ], + } + EOS + + apply_manifest(pp, :catch_changes => true) + end + + it 'adds managed rules with ignored rules' do + pp = <<-EOS + class { 'firewall': } + firewallchain { 'INPUT:filter:IPv4': + purge => true, + ignore => [ + '-s 1\.2\.1\.1', + ], + } + firewall { '014 input-1.2.1.6': + chain => 'INPUT', + proto => 'all', + source => '1.2.1.6', + } + -> firewall { '013 input-1.2.1.5': + chain => 'INPUT', + proto => 'all', + source => '1.2.1.5', + } + -> firewall { '012 input-1.2.1.4': + chain => 'INPUT', + proto => 'all', + source => '1.2.1.4', + } + -> firewall { '011 input-1.2.1.3': + chain => 'INPUT', + proto => 'all', + source => '1.2.1.3', + } + EOS + + apply_manifest(pp, :catch_failures => true) + + expect(shell('iptables-save').stdout).to match(/-A INPUT -s 1\.2\.1\.1(\/32)? -p tcp\s?\n-A INPUT -s 1\.2\.1\.1(\/32)? -p udp/) + end + end +end diff --git a/firewall/spec/acceptance/resource_cmd_spec.rb b/firewall/spec/acceptance/resource_cmd_spec.rb new file mode 100644 index 000000000..c9a852d82 --- /dev/null +++ b/firewall/spec/acceptance/resource_cmd_spec.rb @@ -0,0 +1,93 @@ +require 'spec_helper_acceptance' + +# Here we want to test the the resource commands ability to work with different +# existing ruleset scenarios. This will give the parsing capabilities of the +# code a good work out. +describe 'puppet resource firewall command:' do + context 'make sure it returns no errors when executed on a clean machine' do + it do + shell('puppet resource firewall') do |r| + r.exit_code.should be_zero + # don't check stdout, some boxes come with rules, that is normal + r.stderr.should be_empty + end + end + end + + context 'flush iptables and make sure it returns nothing afterwards' do + before(:all) do + iptables_flush_all_tables + end + + # No rules, means no output thanks. And no errors as well. + it do + shell('puppet resource firewall') do |r| + r.exit_code.should be_zero + r.stderr.should be_empty + r.stdout.should == "\n" + end + end + end + + context 'accepts rules without comments' do + before(:all) do + iptables_flush_all_tables + shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80') + end + + it do + shell('puppet resource firewall') do |r| + r.exit_code.should be_zero + # don't check stdout, testing preexisting rules, output is normal + r.stderr.should be_empty + end + end + end + + context 'accepts rules with invalid comments' do + before(:all) do + iptables_flush_all_tables + shell('iptables -A INPUT -j ACCEPT -p tcp --dport 80 -m comment --comment "http"') + end + + it do + shell('puppet resource firewall') do |r| + r.exit_code.should be_zero + # don't check stdout, testing preexisting rules, output is normal + r.stderr.should be_empty + end + end + end + + context 'accepts rules with negation' do + before :all do + iptables_flush_all_tables + shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p tcp -j MASQUERADE --to-ports 1024-65535') + shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -p udp -j MASQUERADE --to-ports 1024-65535') + shell('iptables -t nat -A POSTROUTING -s 192.168.122.0/24 ! -d 192.168.122.0/24 -j MASQUERADE') + end + + it do + shell('puppet resource firewall') do |r| + r.exit_code.should be_zero + # don't check stdout, testing preexisting rules, output is normal + r.stderr.should be_empty + end + end + end + + context 'accepts rules with match extension tcp flag' do + before :all do + iptables_flush_all_tables + shell('iptables -t mangle -A PREROUTING -d 1.2.3.4 -p tcp -m tcp -m multiport --dports 80,443,8140 -j MARK --set-mark 42') + end + + it do + shell('puppet resource firewall') do |r| + r.exit_code.should be_zero + # don't check stdout, testing preexisting rules, output is normal + r.stderr.should be_empty + end + end + end +end diff --git a/firewall/spec/acceptance/rules_spec.rb b/firewall/spec/acceptance/rules_spec.rb new file mode 100644 index 000000000..b0e66ae5b --- /dev/null +++ b/firewall/spec/acceptance/rules_spec.rb @@ -0,0 +1,252 @@ +require 'spec_helper_acceptance' + +describe 'complex ruleset 1' do + before :all do + iptables_flush_all_tables + end + + after :all do + shell('iptables -t filter -P INPUT ACCEPT') + shell('iptables -t filter -P FORWARD ACCEPT') + shell('iptables -t filter -P OUTPUT ACCEPT') + shell('iptables -t filter --flush') + end + + it 'applies cleanly' do + pp = <<-EOS + firewall { '090 forward allow local': + chain => 'FORWARD', + proto => 'all', + source => '10.0.0.0/8', + destination => '10.0.0.0/8', + action => 'accept', + } + firewall { '100 forward standard allow tcp': + chain => 'FORWARD', + source => '10.0.0.0/8', + destination => '!10.0.0.0/8', + proto => 'tcp', + state => 'NEW', + port => [80,443,21,20,22,53,123,43,873,25,465], + action => 'accept', + } + firewall { '100 forward standard allow udp': + chain => 'FORWARD', + source => '10.0.0.0/8', + destination => '!10.0.0.0/8', + proto => 'udp', + port => [53,123], + action => 'accept', + } + firewall { '100 forward standard allow icmp': + chain => 'FORWARD', + source => '10.0.0.0/8', + destination => '!10.0.0.0/8', + proto => 'icmp', + action => 'accept', + } + + firewall { '090 ignore ipsec': + table => 'nat', + chain => 'POSTROUTING', + outiface => 'eth0', + ipsec_policy => 'ipsec', + ipsec_dir => 'out', + action => 'accept', + } + firewall { '093 ignore 10.0.0.0/8': + table => 'nat', + chain => 'POSTROUTING', + outiface => 'eth0', + destination => '10.0.0.0/8', + action => 'accept', + } + firewall { '093 ignore 172.16.0.0/12': + table => 'nat', + chain => 'POSTROUTING', + outiface => 'eth0', + destination => '172.16.0.0/12', + action => 'accept', + } + firewall { '093 ignore 192.168.0.0/16': + table => 'nat', + chain => 'POSTROUTING', + outiface => 'eth0', + destination => '192.168.0.0/16', + action => 'accept', + } + firewall { '100 masq outbound': + table => 'nat', + chain => 'POSTROUTING', + outiface => 'eth0', + jump => 'MASQUERADE', + } + firewall { '101 redirect port 1': + table => 'nat', + chain => 'PREROUTING', + iniface => 'eth0', + proto => 'tcp', + dport => '1', + toports => '22', + jump => 'REDIRECT', + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end + + it 'contains appropriate rules' do + shell('iptables-save') do |r| + [ + /INPUT ACCEPT/, + /FORWARD ACCEPT/, + /OUTPUT ACCEPT/, + /-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) -d 10.0.0.0\/(8|255\.0\.0\.0) -m comment --comment \"090 forward allow local\" -j ACCEPT/, + /-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p icmp -m comment --comment \"100 forward standard allow icmp\" -j ACCEPT/, + /-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p tcp -m multiport --ports 80,443,21,20,22,53,123,43,873,25,465 -m comment --comment \"100 forward standard allow tcp\" -m state --state NEW -j ACCEPT/, + /-A FORWARD -s 10.0.0.0\/(8|255\.0\.0\.0) (! -d|-d !) 10.0.0.0\/(8|255\.0\.0\.0) -p udp -m multiport --ports 53,123 -m comment --comment \"100 forward standard allow udp\" -j ACCEPT/ + ].each do |line| + expect(r.stdout).to match(line) + end + end + end +end + +describe 'complex ruleset 2' do + after :all do + shell('iptables -t filter -P INPUT ACCEPT') + shell('iptables -t filter -P FORWARD ACCEPT') + shell('iptables -t filter -P OUTPUT ACCEPT') + shell('iptables -t filter --flush') + expect(shell('iptables -t filter -X LOCAL_INPUT').stderr).to eq("") + expect(shell('iptables -t filter -X LOCAL_INPUT_PRE').stderr).to eq("") + end + + it 'applies cleanly' do + pp = <<-EOS + class { '::firewall': } + + Firewall { + proto => 'all', + stage => 'pre', + } + Firewallchain { + stage => 'pre', + purge => 'true', + ignore => [ + '--comment "[^"]*(?i:ignore)[^"]*"', + ], + } + + firewall { '010 INPUT allow established and related': + proto => 'all', + state => ['ESTABLISHED', 'RELATED'], + action => 'accept', + before => Firewallchain['INPUT:filter:IPv4'], + } + firewall { '012 accept loopback': + iniface => 'lo', + action => 'accept', + before => Firewallchain['INPUT:filter:IPv4'], + } + firewall { '020 ssh': + proto => 'tcp', + dport => '22', + state => 'NEW', + action => 'accept', + before => Firewallchain['INPUT:filter:IPv4'], + } + + firewall { '013 icmp echo-request': + proto => 'icmp', + icmp => 'echo-request', + action => 'accept', + source => '10.0.0.0/8', + } + firewall { '013 icmp destination-unreachable': + proto => 'icmp', + icmp => 'destination-unreachable', + action => 'accept', + } + firewall { '013 icmp time-exceeded': + proto => 'icmp', + icmp => 'time-exceeded', + action => 'accept', + } + firewall { '999 reject': + action => 'reject', + reject => 'icmp-host-prohibited', + } + + + firewallchain { 'LOCAL_INPUT_PRE:filter:IPv4': } + firewall { '001 LOCAL_INPUT_PRE': + jump => 'LOCAL_INPUT_PRE', + require => Firewallchain['LOCAL_INPUT_PRE:filter:IPv4'], + } + firewallchain { 'LOCAL_INPUT:filter:IPv4': } + firewall { '900 LOCAL_INPUT': + jump => 'LOCAL_INPUT', + require => Firewallchain['LOCAL_INPUT:filter:IPv4'], + } + firewallchain { 'INPUT:filter:IPv4': + policy => 'drop', + ignore => [ + '-j fail2ban-ssh', + '--comment "[^"]*(?i:ignore)[^"]*"', + ], + } + + + firewall { '010 allow established and related': + chain => 'FORWARD', + proto => 'all', + state => ['ESTABLISHED','RELATED'], + action => 'accept', + before => Firewallchain['FORWARD:filter:IPv4'], + } + firewallchain { 'FORWARD:filter:IPv4': + policy => 'drop', + } + + firewallchain { 'OUTPUT:filter:IPv4': } + + + # purge unknown rules from mangle table + firewallchain { ['PREROUTING:mangle:IPv4', 'INPUT:mangle:IPv4', 'FORWARD:mangle:IPv4', 'OUTPUT:mangle:IPv4', 'POSTROUTING:mangle:IPv4']: } + + # and the nat table + firewallchain { ['PREROUTING:nat:IPv4', 'INPUT:nat:IPv4', 'OUTPUT:nat:IPv4', 'POSTROUTING:nat:IPv4']: } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + end + + it 'contains appropriate rules' do + shell('iptables-save') do |r| + [ + /INPUT DROP/, + /FORWARD DROP/, + /OUTPUT ACCEPT/, + /LOCAL_INPUT/, + /LOCAL_INPUT_PRE/, + /-A INPUT -m comment --comment \"001 LOCAL_INPUT_PRE\" -j LOCAL_INPUT_PRE/, + /-A INPUT -m comment --comment \"010 INPUT allow established and related\" -m state --state RELATED,ESTABLISHED -j ACCEPT/, + /-A INPUT -i lo -m comment --comment \"012 accept loopback\" -j ACCEPT/, + /-A INPUT -p icmp -m comment --comment \"013 icmp destination-unreachable\" -m icmp --icmp-type 3 -j ACCEPT/, + /-A INPUT -s 10.0.0.0\/(8|255\.0\.0\.0) -p icmp -m comment --comment \"013 icmp echo-request\" -m icmp --icmp-type 8 -j ACCEPT/, + /-A INPUT -p icmp -m comment --comment \"013 icmp time-exceeded\" -m icmp --icmp-type 11 -j ACCEPT/, + /-A INPUT -p tcp -m multiport --dports 22 -m comment --comment \"020 ssh\" -m state --state NEW -j ACCEPT/, + /-A INPUT -m comment --comment \"900 LOCAL_INPUT\" -j LOCAL_INPUT/, + /-A INPUT -m comment --comment \"999 reject\" -j REJECT --reject-with icmp-host-prohibited/, + /-A FORWARD -m comment --comment \"010 allow established and related\" -m state --state RELATED,ESTABLISHED -j ACCEPT/ + ].each do |line| + expect(r.stdout).to match(line) + end + end + end +end diff --git a/firewall/spec/acceptance/socket_spec.rb b/firewall/spec/acceptance/socket_spec.rb new file mode 100644 index 000000000..c4a05348c --- /dev/null +++ b/firewall/spec/acceptance/socket_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper_acceptance' + +# RHEL5 does not support -m socket +describe 'firewall socket property', :unless => (default['platform'] =~ /el-5/ || fact('operatingsystem') == 'SLES') do + before :all do + iptables_flush_all_tables + end + + shared_examples "is idempotent" do |value, line_match| + it "changes the value to #{value}" do + pp = <<-EOS + class { '::firewall': } + firewall { '598 - test': + ensure => present, + proto => 'tcp', + chain => 'PREROUTING', + table => 'raw', + #{value} + } + EOS + + apply_manifest(pp, :catch_failures => true) + apply_manifest(pp, :catch_changes => true) + + shell('iptables-save -t raw') do |r| + expect(r.stdout).to match(/#{line_match}/) + end + end + end + shared_examples "doesn't change" do |value, line_match| + it "doesn't change the value to #{value}" do + pp = <<-EOS + class { '::firewall': } + firewall { '598 - test': + ensure => present, + proto => 'tcp', + chain => 'PREROUTING', + table => 'raw', + #{value} + } + EOS + + apply_manifest(pp, :catch_changes => true) + + shell('iptables-save -t raw') do |r| + expect(r.stdout).to match(/#{line_match}/) + end + end + end + + describe 'adding a rule' do + context 'when unset' do + before :all do + iptables_flush_all_tables + end + it_behaves_like 'is idempotent', '', /-A PREROUTING -p tcp -m comment --comment "598 - test"/ + end + context 'when set to true' do + before :all do + iptables_flush_all_tables + end + it_behaves_like 'is idempotent', 'socket => true,', /-A PREROUTING -p tcp -m socket -m comment --comment "598 - test"/ + end + context 'when set to false' do + before :all do + iptables_flush_all_tables + end + it_behaves_like "is idempotent", 'socket => false,', /-A PREROUTING -p tcp -m comment --comment "598 - test"/ + end + end + describe 'editing a rule' do + context 'when unset or false' do + before :each do + iptables_flush_all_tables + shell('iptables -t raw -A PREROUTING -p tcp -m comment --comment "598 - test"') + end + context 'and current value is false' do + it_behaves_like "doesn't change", 'socket => false,', /-A PREROUTING -p tcp -m comment --comment "598 - test"/ + end + context 'and current value is true' do + it_behaves_like "is idempotent", 'socket => true,', /-A PREROUTING -p tcp -m socket -m comment --comment "598 - test"/ + end + end + context 'when set to true' do + before :each do + iptables_flush_all_tables + shell('iptables -t raw -A PREROUTING -p tcp -m socket -m comment --comment "598 - test"') + end + context 'and current value is false' do + it_behaves_like "is idempotent", 'socket => false,', /-A PREROUTING -p tcp -m comment --comment "598 - test"/ + end + context 'and current value is true' do + it_behaves_like "doesn't change", 'socket => true,', /-A PREROUTING -p tcp -m socket -m comment --comment "598 - test"/ + end + end + end +end diff --git a/firewall/spec/acceptance/standard_usage_spec.rb b/firewall/spec/acceptance/standard_usage_spec.rb new file mode 100644 index 000000000..f29278b97 --- /dev/null +++ b/firewall/spec/acceptance/standard_usage_spec.rb @@ -0,0 +1,55 @@ +require 'spec_helper_acceptance' + +# Some tests for the standard recommended usage +describe 'standard usage tests:' do + it 'applies twice' do + pp = <<-EOS + class my_fw::pre { + Firewall { + require => undef, + } + + # Default firewall rules + firewall { '000 accept all icmp': + proto => 'icmp', + action => 'accept', + }-> + firewall { '001 accept all to lo interface': + proto => 'all', + iniface => 'lo', + action => 'accept', + }-> + firewall { '002 accept related established rules': + proto => 'all', + ctstate => ['RELATED', 'ESTABLISHED'], + action => 'accept', + } + } + class my_fw::post { + firewall { '999 drop all': + proto => 'all', + action => 'drop', + before => undef, + } + } + resources { "firewall": + purge => true + } + Firewall { + before => Class['my_fw::post'], + require => Class['my_fw::pre'], + } + class { ['my_fw::pre', 'my_fw::post']: } + class { 'firewall': } + firewall { '500 open up port 22': + action => 'accept', + proto => 'tcp', + dport => 22, + } + EOS + + # Run it twice and test for idempotency + apply_manifest(pp, :catch_failures => true) + expect(apply_manifest(pp, :catch_failures => true).exit_code).to be_zero + end +end diff --git a/firewall/spec/fixtures/ip6tables/conversion_hash.rb b/firewall/spec/fixtures/ip6tables/conversion_hash.rb new file mode 100644 index 000000000..7c507d78b --- /dev/null +++ b/firewall/spec/fixtures/ip6tables/conversion_hash.rb @@ -0,0 +1,107 @@ +# These hashes allow us to iterate across a series of test data +# creating rspec examples for each parameter to ensure the input :line +# extrapolates to the desired value for the parameter in question. And +# vice-versa + +# This hash is for testing a line conversion to a hash of parameters +# which will be used to create a resource. +ARGS_TO_HASH6 = { + 'source_destination_ipv6_no_cidr' => { + :line => '-A INPUT -s 2001:db8:85a3::8a2e:370:7334 -d 2001:db8:85a3::8a2e:370:7334 -m comment --comment "000 source destination ipv6 no cidr"', + :table => 'filter', + :provider => 'ip6tables', + :params => { + :source => '2001:db8:85a3::8a2e:370:7334/128', + :destination => '2001:db8:85a3::8a2e:370:7334/128', + }, + }, + 'source_destination_ipv6_netmask' => { + :line => '-A INPUT -s 2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -d 2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -m comment --comment "000 source destination ipv6 netmask"', + :table => 'filter', + :provider => 'ip6tables', + :params => { + :source => '2001:db8:1234::/48', + :destination => '2001:db8:4321::/48', + }, + }, +} + +# This hash is for testing converting a hash to an argument line. +HASH_TO_ARGS6 = { + 'zero_prefixlen_ipv6' => { + :params => { + :name => '100 zero prefix length ipv6', + :table => 'filter', + :provider => 'ip6tables', + :source => '::/0', + :destination => '::/0', + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '100 zero prefix length ipv6'], + }, + 'source_destination_ipv4_no_cidr' => { + :params => { + :name => '000 source destination ipv4 no cidr', + :table => 'filter', + :provider => 'ip6tables', + :source => '1.1.1.1', + :destination => '2.2.2.2', + }, + :args => ['-t', :filter, '-s', '1.1.1.1/32', '-d', '2.2.2.2/32', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv4 no cidr'], + }, + 'source_destination_ipv6_no_cidr' => { + :params => { + :name => '000 source destination ipv6 no cidr', + :table => 'filter', + :provider => 'ip6tables', + :source => '2001:db8:1234::', + :destination => '2001:db8:4321::', + }, + :args => ['-t', :filter, '-s', '2001:db8:1234::/128', '-d', '2001:db8:4321::/128', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 no cidr'], + }, + 'source_destination_ipv6_netmask' => { + :params => { + :name => '000 source destination ipv6 netmask', + :table => 'filter', + :provider => 'ip6tables', + :source => '2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000', + :destination => '2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000', + }, + :args => ['-t', :filter, '-s', '2001:db8:1234::/48', '-d', '2001:db8:4321::/48', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 netmask'], + }, + 'frag_ishasmorefrags' => { + :params => { + :name => "100 has more fragments", + :ishasmorefrags => true, + :provider => 'ip6tables', + :table => "filter", + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "frag", "--fragid", "0", "--fragmore", "-m", "comment", "--comment", "100 has more fragments"], + }, + 'frag_islastfrag' => { + :params => { + :name => "100 last fragment", + :islastfrag => true, + :provider => 'ip6tables', + :table => "filter", + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "frag", "--fragid", "0", "--fraglast", "-m", "comment", "--comment", "100 last fragment"], + }, + 'frag_isfirstfrags' => { + :params => { + :name => "100 first fragment", + :isfirstfrag => true, + :provider => 'ip6tables', + :table => "filter", + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "frag", "--fragid", "0", "--fragfirst", "-m", "comment", "--comment", "100 first fragment"], + }, + 'hop_limit' => { + :params => { + :name => "100 hop limit", + :hop_limit => 255, + :provider => 'ip6tables', + :table => "filter", + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment", "100 hop limit", "-m", "hl", "--hl-eq", 255], + }, +} diff --git a/firewall/spec/fixtures/iptables/conversion_hash.rb b/firewall/spec/fixtures/iptables/conversion_hash.rb new file mode 100644 index 000000000..042e8bbf3 --- /dev/null +++ b/firewall/spec/fixtures/iptables/conversion_hash.rb @@ -0,0 +1,871 @@ +# These hashes allow us to iterate across a series of test data +# creating rspec examples for each parameter to ensure the input :line +# extrapolates to the desired value for the parameter in question. And +# vice-versa + +# This hash is for testing a line conversion to a hash of parameters +# which will be used to create a resource. +ARGS_TO_HASH = { + 'dport_and_sport' => { + :line => '-A nova-compute-FORWARD -s 0.0.0.0/32 -d 255.255.255.255/32 -p udp -m udp --sport 68 --dport 67 -j ACCEPT', + :table => 'filter', + :params => { + :action => 'accept', + :chain => 'nova-compute-FORWARD', + :source => '0.0.0.0/32', + :destination => '255.255.255.255/32', + :sport => ['68'], + :dport => ['67'], + :proto => 'udp', + }, + }, + 'long_rule_1' => { + :line => '-A INPUT -s 1.1.1.1/32 -d 1.1.1.1/32 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -m comment --comment "000 allow foo" -j ACCEPT', + :table => 'filter', + :compare_all => true, + :params => { + :action => "accept", + :chain => "INPUT", + :destination => "1.1.1.1/32", + :dport => ["7061","7062"], + :ensure => :present, + :line => '-A INPUT -s 1.1.1.1/32 -d 1.1.1.1/32 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -m comment --comment "000 allow foo" -j ACCEPT', + :name => "000 allow foo", + :proto => "tcp", + :provider => "iptables", + :source => "1.1.1.1/32", + :sport => ["7061","7062"], + :table => "filter", + }, + }, + 'action_drop_1' => { + :line => '-A INPUT -m comment --comment "000 allow foo" -j DROP', + :table => 'filter', + :params => { + :jump => nil, + :action => "drop", + }, + }, + 'action_reject_1' => { + :line => '-A INPUT -m comment --comment "000 allow foo" -j REJECT', + :table => 'filter', + :params => { + :jump => nil, + :action => "reject", + }, + }, + 'action_nil_1' => { + :line => '-A INPUT -m comment --comment "000 allow foo"', + :table => 'filter', + :params => { + :jump => nil, + :action => nil, + }, + }, + 'jump_custom_chain_1' => { + :line => '-A INPUT -m comment --comment "000 allow foo" -j custom_chain', + :table => 'filter', + :params => { + :jump => "custom_chain", + :action => nil, + }, + }, + 'source_destination_ipv4_no_cidr' => { + :line => '-A INPUT -s 1.1.1.1 -d 2.2.2.2 -m comment --comment "000 source destination ipv4 no cidr"', + :table => 'filter', + :params => { + :source => '1.1.1.1/32', + :destination => '2.2.2.2/32', + }, + }, + 'source_destination_ipv6_no_cidr' => { + :line => '-A INPUT -s 2001:db8:85a3::8a2e:370:7334 -d 2001:db8:85a3::8a2e:370:7334 -m comment --comment "000 source destination ipv6 no cidr"', + :table => 'filter', + :params => { + :source => '2001:db8:85a3::8a2e:370:7334/128', + :destination => '2001:db8:85a3::8a2e:370:7334/128', + }, + }, + 'source_destination_ipv4_netmask' => { + :line => '-A INPUT -s 1.1.1.0/255.255.255.0 -d 2.2.0.0/255.255.0.0 -m comment --comment "000 source destination ipv4 netmask"', + :table => 'filter', + :params => { + :source => '1.1.1.0/24', + :destination => '2.2.0.0/16', + }, + }, + 'source_destination_ipv6_netmask' => { + :line => '-A INPUT -s 2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -d 2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000 -m comment --comment "000 source destination ipv6 netmask"', + :table => 'filter', + :params => { + :source => '2001:db8:1234::/48', + :destination => '2001:db8:4321::/48', + }, + }, + 'source_destination_negate_source' => { + :line => '-A INPUT ! -s 1.1.1.1 -d 2.2.2.2 -m comment --comment "000 negated source address"', + :table => 'filter', + :params => { + :source => '! 1.1.1.1/32', + :destination => '2.2.2.2/32', + }, + }, + 'source_destination_negate_destination' => { + :line => '-A INPUT -s 1.1.1.1 ! -d 2.2.2.2 -m comment --comment "000 negated destination address"', + :table => 'filter', + :params => { + :source => '1.1.1.1/32', + :destination => '! 2.2.2.2/32', + }, + }, + 'source_destination_negate_destination_alternative' => { + :line => '-A INPUT -s 1.1.1.1 -d ! 2.2.2.2 -m comment --comment "000 negated destination address alternative"', + :table => 'filter', + :params => { + :source => '1.1.1.1/32', + :destination => '! 2.2.2.2/32', + }, + }, + 'dport_range_1' => { + :line => '-A INPUT -m multiport --dports 1:1024 -m comment --comment "000 allow foo"', + :table => 'filter', + :params => { + :dport => ["1-1024"], + }, + }, + 'dport_range_2' => { + :line => '-A INPUT -m multiport --dports 15,512:1024 -m comment --comment "000 allow foo"', + :table => 'filter', + :params => { + :dport => ["15","512-1024"], + }, + }, + 'sport_range_1' => { + :line => '-A INPUT -m multiport --sports 1:1024 -m comment --comment "000 allow foo"', + :table => 'filter', + :params => { + :sport => ["1-1024"], + }, + }, + 'sport_range_2' => { + :line => '-A INPUT -m multiport --sports 15,512:1024 -m comment --comment "000 allow foo"', + :table => 'filter', + :params => { + :sport => ["15","512-1024"], + }, + }, + 'dst_type_1' => { + :line => '-A INPUT -m addrtype --dst-type LOCAL', + :table => 'filter', + :params => { + :dst_type => 'LOCAL', + }, + }, + 'src_type_1' => { + :line => '-A INPUT -m addrtype --src-type LOCAL', + :table => 'filter', + :params => { + :src_type => 'LOCAL', + }, + }, + 'dst_range_1' => { + :line => '-A INPUT -m iprange --dst-range 10.0.0.2-10.0.0.20', + :table => 'filter', + :params => { + :dst_range => '10.0.0.2-10.0.0.20', + }, + }, + 'src_range_1' => { + :line => '-A INPUT -m iprange --src-range 10.0.0.2-10.0.0.20', + :table => 'filter', + :params => { + :src_range => '10.0.0.2-10.0.0.20', + }, + }, + 'tcp_flags_1' => { + :line => '-A INPUT -p tcp -m tcp --tcp-flags SYN,RST,ACK,FIN SYN -m comment --comment "000 initiation"', + :table => 'filter', + :compare_all => true, + :chain => 'INPUT', + :proto => 'tcp', + :params => { + :chain => "INPUT", + :ensure => :present, + :line => '-A INPUT -p tcp -m tcp --tcp-flags SYN,RST,ACK,FIN SYN -m comment --comment "000 initiation"', + :name => "000 initiation", + :proto => "tcp", + :provider => "iptables", + :table => "filter", + :tcp_flags => "SYN,RST,ACK,FIN SYN", + }, + }, + 'state_returns_sorted_values' => { + :line => '-A INPUT -m state --state INVALID,RELATED,ESTABLISHED', + :table => 'filter', + :params => { + :state => ['ESTABLISHED', 'INVALID', 'RELATED'], + :action => nil, + }, + }, + 'ctstate_returns_sorted_values' => { + :line => '-A INPUT -m conntrack --ctstate INVALID,RELATED,ESTABLISHED', + :table => 'filter', + :params => { + :ctstate => ['ESTABLISHED', 'INVALID', 'RELATED'], + :action => nil, + }, + }, + 'comment_string_character_validation' => { + :line => '-A INPUT -s 192.168.0.1/32 -m comment --comment "000 allow from 192.168.0.1, please"', + :table => 'filter', + :params => { + :source => '192.168.0.1/32', + }, + }, + 'log_level_debug' => { + :line => '-A INPUT -m comment --comment "956 INPUT log-level" -m state --state NEW -j LOG --log-level 7', + :table => 'filter', + :params => { + :state => ['NEW'], + :log_level => '7', + :jump => 'LOG' + }, + }, + 'log_level_warn' => { + :line => '-A INPUT -m comment --comment "956 INPUT log-level" -m state --state NEW -j LOG', + :table => 'filter', + :params => { + :state => ['NEW'], + :log_level => '4', + :jump => 'LOG' + }, + }, + 'load_limit_module_and_implicit_burst' => { + :line => '-A INPUT -m multiport --dports 123 -m comment --comment "057 INPUT limit NTP" -m limit --limit 15/hour', + :table => 'filter', + :params => { + :dport => ['123'], + :limit => '15/hour', + :burst => '5' + }, + }, + 'limit_with_explicit_burst' => { + :line => '-A INPUT -m multiport --dports 123 -m comment --comment "057 INPUT limit NTP" -m limit --limit 30/hour --limit-burst 10', + :table => 'filter', + :params => { + :dport => ['123'], + :limit => '30/hour', + :burst => '10' + }, + }, + 'proto_ipencap' => { + :line => '-A INPUT -p ipencap -m comment --comment "0100 INPUT accept ipencap"', + :table => 'filter', + :params => { + :proto => 'ipencap', + } + }, + 'load_uid_owner_filter_module' => { + :line => '-A OUTPUT -m owner --uid-owner root -m comment --comment "057 OUTPUT uid root only" -j ACCEPT', + :table => 'filter', + :params => { + :action => 'accept', + :uid => 'root', + :chain => 'OUTPUT', + }, + }, + 'load_uid_owner_postrouting_module' => { + :line => '-t mangle -A POSTROUTING -m owner --uid-owner root -m comment --comment "057 POSTROUTING uid root only" -j ACCEPT', + :table => 'mangle', + :params => { + :action => 'accept', + :chain => 'POSTROUTING', + :uid => 'root', + }, + }, + 'load_gid_owner_filter_module' => { + :line => '-A OUTPUT -m owner --gid-owner root -m comment --comment "057 OUTPUT gid root only" -j ACCEPT', + :table => 'filter', + :params => { + :action => 'accept', + :chain => 'OUTPUT', + :gid => 'root', + }, + }, + 'load_gid_owner_postrouting_module' => { + :line => '-t mangle -A POSTROUTING -m owner --gid-owner root -m comment --comment "057 POSTROUTING gid root only" -j ACCEPT', + :table => 'mangle', + :params => { + :action => 'accept', + :chain => 'POSTROUTING', + :gid => 'root', + }, + }, + 'mark_set-mark' => { + :line => '-t mangle -A PREROUTING -j MARK --set-xmark 0x3e8/0xffffffff', + :table => 'mangle', + :params => { + :jump => 'MARK', + :chain => 'PREROUTING', + :set_mark => '0x3e8/0xffffffff', + } + }, + 'iniface_1' => { + :line => '-A INPUT -i eth0 -m comment --comment "060 iniface" -j DROP', + :table => 'filter', + :params => { + :action => 'drop', + :chain => 'INPUT', + :iniface => 'eth0', + }, + }, + 'iniface_with_vlans_1' => { + :line => '-A INPUT -i eth0.234 -m comment --comment "060 iniface" -j DROP', + :table => 'filter', + :params => { + :action => 'drop', + :chain => 'INPUT', + :iniface => 'eth0.234', + }, + }, + 'iniface_with_plus_1' => { + :line => '-A INPUT -i eth+ -m comment --comment "060 iniface" -j DROP', + :table => 'filter', + :params => { + :action => 'drop', + :chain => 'INPUT', + :iniface => 'eth+', + }, + }, + 'outiface_1' => { + :line => '-A OUTPUT -o eth0 -m comment --comment "060 outiface" -j DROP', + :table => 'filter', + :params => { + :action => 'drop', + :chain => 'OUTPUT', + :outiface => 'eth0', + }, + }, + 'outiface_with_vlans_1' => { + :line => '-A OUTPUT -o eth0.234 -m comment --comment "060 outiface" -j DROP', + :table => 'filter', + :params => { + :action => 'drop', + :chain => 'OUTPUT', + :outiface => 'eth0.234', + }, + }, + 'outiface_with_plus_1' => { + :line => '-A OUTPUT -o eth+ -m comment --comment "060 outiface" -j DROP', + :table => 'filter', + :params => { + :action => 'drop', + :chain => 'OUTPUT', + :outiface => 'eth+', + }, + }, + 'pkttype multicast' => { + :line => '-A INPUT -m pkttype --pkt-type multicast -j ACCEPT', + :table => 'filter', + :params => { + :action => 'accept', + :pkttype => 'multicast', + }, + }, + 'socket_option' => { + :line => '-A PREROUTING -m socket -j ACCEPT', + :table => 'mangle', + :params => { + :action => 'accept', + :chain => 'PREROUTING', + :socket => true, + }, + }, + 'isfragment_option' => { + :line => '-A INPUT -f -m comment --comment "010 a-f comment with dashf" -j ACCEPT', + :table => 'filter', + :params => { + :name => '010 a-f comment with dashf', + :action => 'accept', + :isfragment => true, + }, + }, + 'single_tcp_sport' => { + :line => '-A OUTPUT -s 10.94.100.46/32 -p tcp -m tcp --sport 20443 -j ACCEPT', + :table => 'mangle', + :params => { + :action => 'accept', + :chain => 'OUTPUT', + :source => "10.94.100.46/32", + :proto => "tcp", + :sport => ["20443"], + }, + }, + 'single_udp_sport' => { + :line => '-A OUTPUT -s 10.94.100.46/32 -p udp -m udp --sport 20443 -j ACCEPT', + :table => 'mangle', + :params => { + :action => 'accept', + :chain => 'OUTPUT', + :source => "10.94.100.46/32", + :proto => "udp", + :sport => ["20443"], + }, + }, + 'single_tcp_dport' => { + :line => '-A OUTPUT -s 10.94.100.46/32 -p tcp -m tcp --dport 20443 -j ACCEPT', + :table => 'mangle', + :params => { + :action => 'accept', + :chain => 'OUTPUT', + :source => "10.94.100.46/32", + :proto => "tcp", + :dport => ["20443"], + }, + }, + 'single_udp_dport' => { + :line => '-A OUTPUT -s 10.94.100.46/32 -p udp -m udp --dport 20443 -j ACCEPT', + :table => 'mangle', + :params => { + :action => 'accept', + :chain => 'OUTPUT', + :source => "10.94.100.46/32", + :proto => "udp", + :dport => ["20443"], + }, + }, +} + +# This hash is for testing converting a hash to an argument line. +HASH_TO_ARGS = { + 'long_rule_1' => { + :params => { + :action => "accept", + :chain => "INPUT", + :destination => "1.1.1.1", + :dport => ["7061","7062"], + :ensure => :present, + :name => "000 allow foo", + :proto => "tcp", + :source => "1.1.1.1", + :sport => ["7061","7062"], + :table => "filter", + }, + :args => ["-t", :filter, "-s", "1.1.1.1/32", "-d", "1.1.1.1/32", "-p", :tcp, "-m", "multiport", "--sports", "7061,7062", "-m", "multiport", "--dports", "7061,7062", "-m", "comment", "--comment", "000 allow foo", "-j", "ACCEPT"], + }, + 'long_rule_2' => { + :params => { + :chain => "INPUT", + :destination => "2.10.13.3/24", + :dport => ["7061"], + :ensure => :present, + :jump => "my_custom_chain", + :name => "700 allow bar", + :proto => "udp", + :source => "1.1.1.1", + :sport => ["7061","7062"], + :table => "filter", + }, + :args => ["-t", :filter, "-s", "1.1.1.1/32", "-d", "2.10.13.0/24", "-p", :udp, "-m", "multiport", "--sports", "7061,7062", "-m", "multiport", "--dports", "7061", "-m", "comment", "--comment", "700 allow bar", "-j", "my_custom_chain"], + }, + 'no_action' => { + :params => { + :name => "100 no action", + :table => "filter", + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment", + "100 no action"], + }, + 'zero_prefixlen_ipv4' => { + :params => { + :name => '100 zero prefix length ipv4', + :table => 'filter', + :source => '0.0.0.0/0', + :destination => '0.0.0.0/0', + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '100 zero prefix length ipv4'], + }, + 'zero_prefixlen_ipv6' => { + :params => { + :name => '100 zero prefix length ipv6', + :table => 'filter', + :source => '::/0', + :destination => '::/0', + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '100 zero prefix length ipv6'], + }, + 'source_destination_ipv4_no_cidr' => { + :params => { + :name => '000 source destination ipv4 no cidr', + :table => 'filter', + :source => '1.1.1.1', + :destination => '2.2.2.2', + }, + :args => ['-t', :filter, '-s', '1.1.1.1/32', '-d', '2.2.2.2/32', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv4 no cidr'], + }, + 'source_destination_ipv6_no_cidr' => { + :params => { + :name => '000 source destination ipv6 no cidr', + :table => 'filter', + :source => '2001:db8:1234::', + :destination => '2001:db8:4321::', + }, + :args => ['-t', :filter, '-s', '2001:db8:1234::/128', '-d', '2001:db8:4321::/128', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 no cidr'], + }, + 'source_destination_ipv4_netmask' => { + :params => { + :name => '000 source destination ipv4 netmask', + :table => 'filter', + :source => '1.1.1.0/255.255.255.0', + :destination => '2.2.0.0/255.255.0.0', + }, + :args => ['-t', :filter, '-s', '1.1.1.0/24', '-d', '2.2.0.0/16', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv4 netmask'], + }, + 'source_destination_ipv6_netmask' => { + :params => { + :name => '000 source destination ipv6 netmask', + :table => 'filter', + :source => '2001:db8:1234::/ffff:ffff:ffff:0000:0000:0000:0000:0000', + :destination => '2001:db8:4321::/ffff:ffff:ffff:0000:0000:0000:0000:0000', + }, + :args => ['-t', :filter, '-s', '2001:db8:1234::/48', '-d', '2001:db8:4321::/48', '-p', :tcp, '-m', 'comment', '--comment', '000 source destination ipv6 netmask'], + }, + 'sport_range_1' => { + :params => { + :name => "100 sport range", + :sport => ["1-1024"], + :table => "filter", + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--sports", "1:1024", "-m", "comment", "--comment", "100 sport range"], + }, + 'sport_range_2' => { + :params => { + :name => "100 sport range", + :sport => ["15","512-1024"], + :table => "filter", + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--sports", "15,512:1024", "-m", "comment", "--comment", "100 sport range"], + }, + 'dport_range_1' => { + :params => { + :name => "100 sport range", + :dport => ["1-1024"], + :table => "filter", + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "1:1024", "-m", "comment", "--comment", "100 sport range"], + }, + 'dport_range_2' => { + :params => { + :name => "100 sport range", + :dport => ["15","512-1024"], + :table => "filter", + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "multiport", "--dports", "15,512:1024", "-m", "comment", "--comment", "100 sport range"], + }, + 'dst_type_1' => { + :params => { + :name => '000 dst_type', + :table => 'filter', + :dst_type => 'LOCAL', + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--dst-type', :LOCAL, '-m', 'comment', '--comment', '000 dst_type'], + }, + 'src_type_1' => { + :params => { + :name => '000 src_type', + :table => 'filter', + :src_type => 'LOCAL', + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'addrtype', '--src-type', :LOCAL, '-m', 'comment', '--comment', '000 src_type'], + }, + 'dst_range_1' => { + :params => { + :name => '000 dst_range', + :table => 'filter', + :dst_range => '10.0.0.1-10.0.0.10', + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'iprange', '--dst-range', '10.0.0.1-10.0.0.10', '-m', 'comment', '--comment', '000 dst_range'], + }, + 'src_range_1' => { + :params => { + :name => '000 src_range', + :table => 'filter', + :dst_range => '10.0.0.1-10.0.0.10', + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'iprange', '--dst-range', '10.0.0.1-10.0.0.10', '-m', 'comment', '--comment', '000 src_range'], + }, + 'tcp_flags_1' => { + :params => { + :name => "000 initiation", + :tcp_flags => "SYN,RST,ACK,FIN SYN", + :table => "filter", + }, + + :args => ["-t", :filter, "-p", :tcp, "-m", "tcp", "--tcp-flags", "SYN,RST,ACK,FIN", "SYN", "-m", "comment", "--comment", "000 initiation",] + }, + 'states_set_from_array' => { + :params => { + :name => "100 states_set_from_array", + :table => "filter", + :state => ['ESTABLISHED', 'INVALID'] + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment", "100 states_set_from_array", + "-m", "state", "--state", "ESTABLISHED,INVALID"], + }, + 'ctstates_set_from_array' => { + :params => { + :name => "100 ctstates_set_from_array", + :table => "filter", + :ctstate => ['ESTABLISHED', 'INVALID'] + }, + :args => ["-t", :filter, "-p", :tcp, "-m", "comment", "--comment", "100 ctstates_set_from_array", + "-m", "conntrack", "--ctstate", "ESTABLISHED,INVALID"], + }, + 'comment_string_character_validation' => { + :params => { + :name => "000 allow from 192.168.0.1, please", + :table => 'filter', + :source => '192.168.0.1' + }, + :args => ['-t', :filter, '-s', '192.168.0.1/32', '-p', :tcp, '-m', 'comment', '--comment', '000 allow from 192.168.0.1, please'], + }, + 'port_property' => { + :params => { + :name => '001 port property', + :table => 'filter', + :port => '80', + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'multiport', '--ports', '80', '-m', 'comment', '--comment', '001 port property'], + }, + 'log_level_debug' => { + :params => { + :name => '956 INPUT log-level', + :table => 'filter', + :state => 'NEW', + :jump => 'LOG', + :log_level => 'debug' + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '956 INPUT log-level', '-m', 'state', '--state', 'NEW', '-j', 'LOG', '--log-level', '7'], + }, + 'log_level_warn' => { + :params => { + :name => '956 INPUT log-level', + :table => 'filter', + :state => 'NEW', + :jump => 'LOG', + :log_level => 'warn' + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'comment', '--comment', '956 INPUT log-level', '-m', 'state', '--state', 'NEW', '-j', 'LOG', '--log-level', '4'], + }, + 'load_limit_module_and_implicit_burst' => { + :params => { + :name => '057 INPUT limit NTP', + :table => 'filter', + :dport => '123', + :limit => '15/hour' + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'multiport', '--dports', '123', '-m', 'comment', '--comment', '057 INPUT limit NTP', '-m', 'limit', '--limit', '15/hour'], + }, + 'limit_with_explicit_burst' => { + :params => { + :name => '057 INPUT limit NTP', + :table => 'filter', + :dport => '123', + :limit => '30/hour', + :burst => '10' + }, + :args => ['-t', :filter, '-p', :tcp, '-m', 'multiport', '--dports', '123', '-m', 'comment', '--comment', '057 INPUT limit NTP', '-m', 'limit', '--limit', '30/hour', '--limit-burst', '10'], + }, + 'proto_ipencap' => { + :params => { + :name => '0100 INPUT accept ipencap', + :table => 'filter', + :proto => 'ipencap', + }, + :args => ['-t', :filter, '-p', :ipencap, '-m', 'comment', '--comment', '0100 INPUT accept ipencap'], + }, + 'load_uid_owner_filter_module' => { + :params => { + :name => '057 OUTPUT uid root only', + :table => 'filter', + :uid => 'root', + :action => 'accept', + :chain => 'OUTPUT', + :proto => 'all', + }, + :args => ['-t', :filter, '-p', :all, '-m', 'owner', '--uid-owner', 'root', '-m', 'comment', '--comment', '057 OUTPUT uid root only', '-j', 'ACCEPT'], + }, + 'load_uid_owner_postrouting_module' => { + :params => { + :name => '057 POSTROUTING uid root only', + :table => 'mangle', + :uid => 'root', + :action => 'accept', + :chain => 'POSTROUTING', + :proto => 'all', + }, + :args => ['-t', :mangle, '-p', :all, '-m', 'owner', '--uid-owner', 'root', '-m', 'comment', '--comment', '057 POSTROUTING uid root only', '-j', 'ACCEPT'], + }, + 'load_gid_owner_filter_module' => { + :params => { + :name => '057 OUTPUT gid root only', + :table => 'filter', + :chain => 'OUTPUT', + :gid => 'root', + :action => 'accept', + :proto => 'all', + }, + :args => ['-t', :filter, '-p', :all, '-m', 'owner', '--gid-owner', 'root', '-m', 'comment', '--comment', '057 OUTPUT gid root only', '-j', 'ACCEPT'], + }, + 'load_gid_owner_postrouting_module' => { + :params => { + :name => '057 POSTROUTING gid root only', + :table => 'mangle', + :gid => 'root', + :action => 'accept', + :chain => 'POSTROUTING', + :proto => 'all', + }, + :args => ['-t', :mangle, '-p', :all, '-m', 'owner', '--gid-owner', 'root', '-m', 'comment', '--comment', '057 POSTROUTING gid root only', '-j', 'ACCEPT'], + }, + 'mark_set-mark_int' => { + :params => { + :name => '058 set-mark 1000', + :table => 'mangle', + :jump => 'MARK', + :chain => 'PREROUTING', + :set_mark => '1000', + }, + :args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 1000', '-j', 'MARK', '--set-xmark', '0x3e8/0xffffffff'], + }, + 'mark_set-mark_hex' => { + :params => { + :name => '058 set-mark 0x32', + :table => 'mangle', + :jump => 'MARK', + :chain => 'PREROUTING', + :set_mark => '0x32', + }, + :args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 0x32', '-j', 'MARK', '--set-xmark', '0x32/0xffffffff'], + }, + 'mark_set-mark_hex_with_hex_mask' => { + :params => { + :name => '058 set-mark 0x32/0xffffffff', + :table => 'mangle', + :jump => 'MARK', + :chain => 'PREROUTING', + :set_mark => '0x32/0xffffffff', + }, + :args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 0x32/0xffffffff', '-j', 'MARK', '--set-xmark', '0x32/0xffffffff'], + }, + 'mark_set-mark_hex_with_mask' => { + :params => { + :name => '058 set-mark 0x32/4', + :table => 'mangle', + :jump => 'MARK', + :chain => 'PREROUTING', + :set_mark => '0x32/4', + }, + :args => ['-t', :mangle, '-p', :tcp, '-m', 'comment', '--comment', '058 set-mark 0x32/4', '-j', 'MARK', '--set-xmark', '0x32/0x4'], + }, + 'iniface_1' => { + :params => { + :name => '060 iniface', + :table => 'filter', + :action => 'drop', + :chain => 'INPUT', + :iniface => 'eth0', + }, + :args => ["-t", :filter, "-i", "eth0", "-p", :tcp, "-m", "comment", "--comment", "060 iniface", "-j", "DROP"], + }, + 'iniface_with_vlans_1' => { + :params => { + :name => '060 iniface', + :table => 'filter', + :action => 'drop', + :chain => 'INPUT', + :iniface => 'eth0.234', + }, + :args => ["-t", :filter, "-i", "eth0.234", "-p", :tcp, "-m", "comment", "--comment", "060 iniface", "-j", "DROP"], + }, + 'iniface_with_plus_1' => { + :params => { + :name => '060 iniface', + :table => 'filter', + :action => 'drop', + :chain => 'INPUT', + :iniface => 'eth+', + }, + :args => ["-t", :filter, "-i", "eth+", "-p", :tcp, "-m", "comment", "--comment", "060 iniface", "-j", "DROP"], + }, + 'outiface_1' => { + :params => { + :name => '060 outiface', + :table => 'filter', + :action => 'drop', + :chain => 'OUTPUT', + :outiface => 'eth0', + }, + :args => ["-t", :filter, "-o", "eth0", "-p", :tcp, "-m", "comment", "--comment", "060 outiface", "-j", "DROP"], + }, + 'outiface_with_vlans_1' => { + :params => { + :name => '060 outiface', + :table => 'filter', + :action => 'drop', + :chain => 'OUTPUT', + :outiface => 'eth0.234', + }, + :args => ["-t", :filter, "-o", "eth0.234", "-p", :tcp, "-m", "comment", "--comment", "060 outiface", "-j", "DROP"], + }, + 'outiface_with_plus_1' => { + :params => { + :name => '060 outiface', + :table => 'filter', + :action => 'drop', + :chain => 'OUTPUT', + :outiface => 'eth+', + }, + :args => ["-t", :filter, "-o", "eth+", "-p", :tcp, "-m", "comment", "--comment", "060 outiface", "-j", "DROP"], + }, + 'pkttype multicast' => { + :params => { + :name => '062 pkttype multicast', + :table => "filter", + :action => 'accept', + :chain => 'INPUT', + :iniface => 'eth0', + :pkttype => 'multicast', + }, + :args => ["-t", :filter, "-i", "eth0", "-p", :tcp, "-m", "pkttype", "--pkt-type", :multicast, "-m", "comment", "--comment", "062 pkttype multicast", "-j", "ACCEPT"], + }, + 'socket_option' => { + :params => { + :name => '050 socket option', + :table => 'mangle', + :action => 'accept', + :chain => 'PREROUTING', + :socket => true, + }, + :args => ['-t', :mangle, '-p', :tcp, '-m', 'socket', '-m', 'comment', '--comment', '050 socket option', '-j', 'ACCEPT'], + }, + 'isfragment_option' => { + :params => { + :name => '050 isfragment option', + :table => 'filter', + :proto => :all, + :action => 'accept', + :isfragment => true, + }, + :args => ['-t', :filter, '-p', :all, '-f', '-m', 'comment', '--comment', '050 isfragment option', '-j', 'ACCEPT'], + }, + 'isfragment_option not changing -f in comment' => { + :params => { + :name => '050 testcomment-with-fdashf', + :table => 'filter', + :proto => :all, + :action => 'accept', + }, + :args => ['-t', :filter, '-p', :all, '-m', 'comment', '--comment', '050 testcomment-with-fdashf', '-j', 'ACCEPT'], + }, +} diff --git a/firewall/spec/spec_helper.rb b/firewall/spec/spec_helper.rb new file mode 100644 index 000000000..dc8bc39cb --- /dev/null +++ b/firewall/spec/spec_helper.rb @@ -0,0 +1,29 @@ +dir = File.expand_path(File.dirname(__FILE__)) +$LOAD_PATH.unshift File.join(dir, 'lib') + +# Don't want puppet getting the command line arguments for rake or autotest +ARGV.clear + +require 'rubygems' +require 'bundler/setup' +require 'rspec-puppet' + +Bundler.require :default, :test + +require 'pathname' +require 'tmpdir' + +Pathname.glob("#{dir}/shared_behaviours/**/*.rb") do |behaviour| + require behaviour.relative_path_from(Pathname.new(dir)) +end + +fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures')) + +RSpec.configure do |config| + config.tty = true + config.mock_with :rspec do |c| + c.syntax = :expect + end + config.module_path = File.join(fixture_path, 'modules') + config.manifest_dir = File.join(fixture_path, 'manifests') +end diff --git a/firewall/spec/spec_helper_acceptance.rb b/firewall/spec/spec_helper_acceptance.rb new file mode 100644 index 000000000..a0e807c54 --- /dev/null +++ b/firewall/spec/spec_helper_acceptance.rb @@ -0,0 +1,44 @@ +require 'beaker-rspec' + +def iptables_flush_all_tables + ['filter', 'nat', 'mangle', 'raw'].each do |t| + expect(shell("iptables -t #{t} -F").stderr).to eq("") + end +end + +def ip6tables_flush_all_tables + ['filter'].each do |t| + expect(shell("ip6tables -t #{t} -F").stderr).to eq("") + end +end + +unless ENV['RS_PROVISION'] == 'no' + hosts.each do |host| + # Install Puppet + if host.is_pe? + install_pe + else + install_package host, 'rubygems' + on host, 'gem install puppet --no-ri --no-rdoc' + on host, "mkdir -p #{host['distmoduledir']}" + end + end +end + +RSpec.configure do |c| + # Project root + proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) + + # Readable test descriptions + c.formatter = :documentation + + # Configure all nodes in nodeset + c.before :suite do + # Install module and dependencies + puppet_module_install(:source => proj_root, :module_name => 'firewall') + hosts.each do |host| + shell('/bin/touch /etc/puppet/hiera.yaml') + shell('puppet module install puppetlabs-stdlib --version 3.2.0', { :acceptable_exit_codes => [0,1] }) + end + end +end diff --git a/firewall/spec/unit/classes/firewall_linux_archlinux_spec.rb b/firewall/spec/unit/classes/firewall_linux_archlinux_spec.rb new file mode 100644 index 000000000..954d9ee10 --- /dev/null +++ b/firewall/spec/unit/classes/firewall_linux_archlinux_spec.rb @@ -0,0 +1,32 @@ +require 'spec_helper' + +describe 'firewall::linux::archlinux', :type => :class do + it { should contain_service('iptables').with( + :ensure => 'running', + :enable => 'true' + )} + it { should contain_service('ip6tables').with( + :ensure => 'running', + :enable => 'true' + )} + + context 'ensure => stopped' do + let(:params) {{ :ensure => 'stopped' }} + it { should contain_service('iptables').with( + :ensure => 'stopped' + )} + it { should contain_service('ip6tables').with( + :ensure => 'stopped' + )} + end + + context 'enable => false' do + let(:params) {{ :enable => 'false' }} + it { should contain_service('iptables').with( + :enable => 'false' + )} + it { should contain_service('ip6tables').with( + :enable => 'false' + )} + end +end diff --git a/firewall/spec/unit/classes/firewall_linux_debian_spec.rb b/firewall/spec/unit/classes/firewall_linux_debian_spec.rb new file mode 100644 index 000000000..98285b642 --- /dev/null +++ b/firewall/spec/unit/classes/firewall_linux_debian_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe 'firewall::linux::debian', :type => :class do + it { should contain_package('iptables-persistent').with( + :ensure => 'present' + )} + it { should contain_service('iptables-persistent').with( + :ensure => nil, + :enable => 'true', + :require => 'Package[iptables-persistent]' + )} + + context 'enable => false' do + let(:params) {{ :enable => 'false' }} + it { should contain_service('iptables-persistent').with( + :enable => 'false' + )} + end +end diff --git a/firewall/spec/unit/classes/firewall_linux_redhat_spec.rb b/firewall/spec/unit/classes/firewall_linux_redhat_spec.rb new file mode 100644 index 000000000..ea49d2b83 --- /dev/null +++ b/firewall/spec/unit/classes/firewall_linux_redhat_spec.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +describe 'firewall::linux::redhat', :type => :class do + it { should contain_service('iptables').with( + :ensure => 'running', + :enable => 'true' + )} + + context 'ensure => stopped' do + let(:params) {{ :ensure => 'stopped' }} + it { should contain_service('iptables').with( + :ensure => 'stopped' + )} + end + + context 'enable => false' do + let(:params) {{ :enable => 'false' }} + it { should contain_service('iptables').with( + :enable => 'false' + )} + end +end diff --git a/firewall/spec/unit/classes/firewall_linux_spec.rb b/firewall/spec/unit/classes/firewall_linux_spec.rb new file mode 100644 index 000000000..8245915a9 --- /dev/null +++ b/firewall/spec/unit/classes/firewall_linux_spec.rb @@ -0,0 +1,24 @@ +require 'spec_helper' + +describe 'firewall::linux', :type => :class do + let(:facts_default) {{ :kernel => 'Linux' }} + it { should contain_package('iptables').with_ensure('present') } + + context 'RedHat like' do + %w{RedHat CentOS Fedora}.each do |os| + context "operatingsystem => #{os}" do + let(:facts) { facts_default.merge({ :operatingsystem => os }) } + it { should contain_class('firewall::linux::redhat').with_require('Package[iptables]') } + end + end + end + + context 'Debian like' do + %w{Debian Ubuntu}.each do |os| + context "operatingsystem => #{os}" do + let(:facts) { facts_default.merge({ :operatingsystem => os }) } + it { should contain_class('firewall::linux::debian').with_require('Package[iptables]') } + end + end + end +end diff --git a/firewall/spec/unit/classes/firewall_spec.rb b/firewall/spec/unit/classes/firewall_spec.rb new file mode 100644 index 000000000..efc153ab2 --- /dev/null +++ b/firewall/spec/unit/classes/firewall_spec.rb @@ -0,0 +1,25 @@ +require 'spec_helper' + +describe 'firewall', :type => :class do + context 'kernel => Linux' do + let(:facts) {{ :kernel => 'Linux' }} + it { should contain_class('firewall::linux').with_ensure('running') } + end + + context 'kernel => Windows' do + let(:facts) {{ :kernel => 'Windows' }} + it { expect { should contain_class('firewall::linux') }.to raise_error(Puppet::Error) } + end + + context 'ensure => stopped' do + let(:facts) {{ :kernel => 'Linux' }} + let(:params) {{ :ensure => 'stopped' }} + it { should contain_class('firewall::linux').with_ensure('stopped') } + end + + context 'ensure => test' do + let(:facts) {{ :kernel => 'Linux' }} + let(:params) {{ :ensure => 'test' }} + it { expect { should contain_class('firewall::linux') }.to raise_error(Puppet::Error) } + end +end diff --git a/firewall/spec/unit/facter/iptables_persistent_version_spec.rb b/firewall/spec/unit/facter/iptables_persistent_version_spec.rb new file mode 100644 index 000000000..13a23a5c2 --- /dev/null +++ b/firewall/spec/unit/facter/iptables_persistent_version_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe "Facter::Util::Fact iptables_persistent_version" do + before { Facter.clear } + let(:dpkg_cmd) { "dpkg-query -Wf '${Version}' iptables-persistent 2>/dev/null" } + + { + "Debian" => "0.0.20090701", + "Ubuntu" => "0.5.3ubuntu2", + }.each do |os, ver| + describe "#{os} package installed" do + before { + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return(os) + allow(Facter::Util::Resolution).to receive(:exec).with(dpkg_cmd). + and_return(ver) + } + it { Facter.fact(:iptables_persistent_version).value.should == ver } + end + end + + describe 'Ubuntu package not installed' do + before { + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu') + allow(Facter::Util::Resolution).to receive(:exec).with(dpkg_cmd). + and_return(nil) + } + it { Facter.fact(:iptables_persistent_version).value.should be_nil } + end + + describe 'CentOS not supported' do + before { allow(Facter.fact(:operatingsystem)).to receive(:value). + and_return("CentOS") } + it { Facter.fact(:iptables_persistent_version).value.should be_nil } + end +end diff --git a/firewall/spec/unit/facter/iptables_spec.rb b/firewall/spec/unit/facter/iptables_spec.rb new file mode 100644 index 000000000..5773fdce5 --- /dev/null +++ b/firewall/spec/unit/facter/iptables_spec.rb @@ -0,0 +1,23 @@ +require 'spec_helper' + +describe "Facter::Util::Fact" do + before { + Facter.clear + allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux') + allow(Facter.fact(:kernelrelease)).to receive(:value).and_return('2.6') + } + + describe 'iptables_version' do + it { + allow(Facter::Util::Resolution).to receive(:exec).with('iptables --version'). + and_return('iptables v1.4.7') + Facter.fact(:iptables_version).value.should == '1.4.7' + } + end + + describe 'ip6tables_version' do + before { allow(Facter::Util::Resolution).to receive(:exec). + with('ip6tables --version').and_return('ip6tables v1.4.7') } + it { Facter.fact(:ip6tables_version).value.should == '1.4.7' } + end +end diff --git a/firewall/spec/unit/puppet/provider/iptables_chain_spec.rb b/firewall/spec/unit/puppet/provider/iptables_chain_spec.rb new file mode 100755 index 000000000..f350c2e3c --- /dev/null +++ b/firewall/spec/unit/puppet/provider/iptables_chain_spec.rb @@ -0,0 +1,227 @@ +#!/usr/bin/env rspec + +require 'spec_helper' +if Puppet.version < '3.4.0' + require 'puppet/provider/confine/exists' +else + require 'puppet/confine/exists' +end + +describe 'iptables chain provider detection' do + if Puppet.version < '3.4.0' + let(:exists) { + Puppet::Provider::Confine::Exists + } + else + let(:exists) { + Puppet::Confine::Exists + } + end + + before :each do + # Reset the default provider + Puppet::Type.type(:firewallchain).defaultprovider = nil + end + + it "should default to iptables provider if /sbin/(eb|ip|ip6)tables[-save] exists" do + # Stub lookup for /sbin/iptables & /sbin/iptables-save + allow(exists).to receive(:which).with("ebtables"). + and_return "/sbin/ebtables" + allow(exists).to receive(:which).with("ebtables-save"). + and_return "/sbin/ebtables-save" + + allow(exists).to receive(:which).with("iptables"). + and_return "/sbin/iptables" + allow(exists).to receive(:which).with("iptables-save"). + and_return "/sbin/iptables-save" + + allow(exists).to receive(:which).with("ip6tables"). + and_return "/sbin/ip6tables" + allow(exists).to receive(:which).with("ip6tables-save"). + and_return "/sbin/ip6tables-save" + + # Every other command should return false so we don't pick up any + # other providers + allow(exists).to receive(:which).with() { |value| + value !~ /(eb|ip|ip6)tables(-save)?$/ + }.and_return false + + # Create a resource instance and make sure the provider is iptables + resource = Puppet::Type.type(:firewallchain).new({ + :name => 'test:filter:IPv4', + }) + expect(resource.provider.class.to_s).to eq("Puppet::Type::Firewallchain::ProviderIptables_chain") + end +end + +describe 'iptables chain provider' do + let(:provider) { Puppet::Type.type(:firewallchain).provider(:iptables_chain) } + let(:resource) { + Puppet::Type.type(:firewallchain).new({ + :name => ':test:', + }) + } + + before :each do + allow(Puppet::Type::Firewallchain).to receive(:defaultprovider).and_return provider + allow(provider).to receive(:command).with(:ebtables_save).and_return "/sbin/ebtables-save" + allow(provider).to receive(:command).with(:iptables_save).and_return "/sbin/iptables-save" + allow(provider).to receive(:command).with(:ip6tables_save).and_return "/sbin/ip6tables-save" + end + + it 'should be able to get a list of existing rules' do + # Pretend to return nil from iptables + allow(provider).to receive(:execute).with(['/sbin/ip6tables-save']).and_return("") + allow(provider).to receive(:execute).with(['/sbin/ebtables-save']).and_return("") + allow(provider).to receive(:execute).with(['/sbin/iptables-save']).and_return("") + + provider.instances.each do |chain| + expect(chain).to be_instance_of(provider) + expect(chain.properties[:provider].to_s).to eq(provider.name.to_s) + end + end + +end + +describe 'iptables chain resource parsing' do + let(:provider) { Puppet::Type.type(:firewallchain).provider(:iptables_chain) } + + before :each do + ebtables = ['BROUTE:BROUTING:ethernet', + 'BROUTE:broute:ethernet', + ':INPUT:ethernet', + ':FORWARD:ethernet', + ':OUTPUT:ethernet', + ':filter:ethernet', + ':filterdrop:ethernet', + ':filterreturn:ethernet', + 'NAT:PREROUTING:ethernet', + 'NAT:OUTPUT:ethernet', + 'NAT:POSTROUTING:ethernet', + ] + allow(provider).to receive(:execute).with(['/sbin/ebtables-save']).and_return(' +*broute +:BROUTING ACCEPT +:broute ACCEPT + +*filter +:INPUT ACCEPT +:FORWARD ACCEPT +:OUTPUT ACCEPT +:filter ACCEPT +:filterdrop DROP +:filterreturn RETURN + +*nat +:PREROUTING ACCEPT +:OUTPUT ACCEPT +:POSTROUTING ACCEPT +') + + iptables = [ + 'raw:PREROUTING:IPv4', + 'raw:OUTPUT:IPv4', + 'raw:raw:IPv4', + 'mangle:PREROUTING:IPv4', + 'mangle:INPUT:IPv4', + 'mangle:FORWARD:IPv4', + 'mangle:OUTPUT:IPv4', + 'mangle:POSTROUTING:IPv4', + 'mangle:mangle:IPv4', + 'NAT:PREROUTING:IPv4', + 'NAT:OUTPUT:IPv4', + 'NAT:POSTROUTING:IPv4', + 'NAT:mangle:IPv4', + 'NAT:mangle:IPv4', + 'NAT:mangle:IPv4', + ':$5()*&%\'"^$): :IPv4', + ] + allow(provider).to receive(:execute).with(['/sbin/iptables-save']).and_return(' +# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012 +*raw +:PREROUTING ACCEPT [12:1780] +:OUTPUT ACCEPT [19:1159] +:raw - [0:0] +COMMIT +# Completed on Mon Jan 2 01:20:06 2012 +# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012 +*mangle +:PREROUTING ACCEPT [12:1780] +:INPUT ACCEPT [12:1780] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [19:1159] +:POSTROUTING ACCEPT [19:1159] +:mangle - [0:0] +COMMIT +# Completed on Mon Jan 2 01:20:06 2012 +# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012 +*nat +:PREROUTING ACCEPT [2242:639750] +:OUTPUT ACCEPT [5176:326206] +:POSTROUTING ACCEPT [5162:325382] +COMMIT +# Completed on Mon Jan 2 01:20:06 2012 +# Generated by iptables-save v1.4.9 on Mon Jan 2 01:20:06 2012 +*filter +:INPUT ACCEPT [0:0] +:FORWARD DROP [0:0] +:OUTPUT ACCEPT [5673:420879] +:$5()*&%\'"^$): - [0:0] +COMMIT +# Completed on Mon Jan 2 01:20:06 2012 +') + ip6tables = [ + 'raw:PREROUTING:IPv6', + 'raw:OUTPUT:IPv6', + 'raw:ff:IPv6', + 'mangle:PREROUTING:IPv6', + 'mangle:INPUT:IPv6', + 'mangle:FORWARD:IPv6', + 'mangle:OUTPUT:IPv6', + 'mangle:POSTROUTING:IPv6', + 'mangle:ff:IPv6', + ':INPUT:IPv6', + ':FORWARD:IPv6', + ':OUTPUT:IPv6', + ':test:IPv6', + ] + allow(provider).to receive(:execute).with(['/sbin/ip6tables-save']).and_return(' +# Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012 +*raw +:PREROUTING ACCEPT [2173:489241] +:OUTPUT ACCEPT [0:0] +:ff - [0:0] +COMMIT +# Completed on Mon Jan 2 01:31:39 2012 +# Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012 +*mangle +:PREROUTING ACCEPT [2301:518373] +:INPUT ACCEPT [0:0] +:FORWARD ACCEPT [0:0] +:OUTPUT ACCEPT [0:0] +:POSTROUTING ACCEPT [0:0] +:ff - [0:0] +COMMIT +# Completed on Mon Jan 2 01:31:39 2012 +# Generated by ip6tables-save v1.4.9 on Mon Jan 2 01:31:39 2012 +*filter +:INPUT ACCEPT [0:0] +:FORWARD DROP [0:0] +:OUTPUT ACCEPT [20:1292] +:test - [0:0] +COMMIT +# Completed on Mon Jan 2 01:31:39 2012 +') + @all = ebtables + iptables + ip6tables + # IPv4 and IPv6 names also exist as resources {table}:{chain}:IP and {table}:{chain}: + iptables.each { |name| @all += [ name[0..-3], name[0..-5] ] } + ip6tables.each { |name| @all += [ name[0..-3], name[0..-5] ] } + end + + it 'should have all in parsed resources' do + provider.instances.each do |resource| + @all.include?(resource.name) + end + end + +end diff --git a/firewall/spec/unit/puppet/provider/iptables_spec.rb b/firewall/spec/unit/puppet/provider/iptables_spec.rb new file mode 100644 index 000000000..d6f5b64cf --- /dev/null +++ b/firewall/spec/unit/puppet/provider/iptables_spec.rb @@ -0,0 +1,410 @@ +#!/usr/bin/env rspec + +require 'spec_helper' +if Puppet.version < '3.4.0' + require 'puppet/provider/confine/exists' +else + require 'puppet/confine/exists' +end + +describe 'iptables provider detection' do + if Puppet.version < '3.4.0' + let(:exists) { + Puppet::Provider::Confine::Exists + } + else + let(:exists) { + Puppet::Confine::Exists + } + end + + before :each do + # Reset the default provider + Puppet::Type.type(:firewall).defaultprovider = nil + end + + it "should default to iptables provider if /sbin/iptables[-save] exists" do + # Stub lookup for /sbin/iptables & /sbin/iptables-save + allow(exists).to receive(:which).with("iptables"). + and_return "/sbin/iptables" + allow(exists).to receive(:which).with("iptables-save"). + and_return "/sbin/iptables-save" + + # Every other command should return false so we don't pick up any + # other providers + allow(exists).to receive(:which).with() { |value| + ! ["iptables","iptables-save"].include?(value) + }.and_return false + + # Create a resource instance and make sure the provider is iptables + resource = Puppet::Type.type(:firewall).new({ + :name => '000 test foo', + }) + expect(resource.provider.class.to_s).to eq("Puppet::Type::Firewall::ProviderIptables") + end +end + +describe 'iptables provider' do + let(:provider) { Puppet::Type.type(:firewall).provider(:iptables) } + let(:resource) { + Puppet::Type.type(:firewall).new({ + :name => '000 test foo', + :action => 'accept', + }) + } + + before :each do + allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return provider + allow(provider).to receive(:command).with(:iptables_save).and_return "/sbin/iptables-save" + + # Stub iptables version + allow(Facter.fact(:iptables_version)).to receive(:value).and_return("1.4.2") + + allow(Puppet::Util::Execution).to receive(:execute).and_return "" + allow(Puppet::Util).to receive(:which).with("iptables-save"). + and_return "/sbin/iptables-save" + end + + it 'should be able to get a list of existing rules' do + provider.instances.each do |rule| + expect(rule).to be_instance_of(provider) + expect(rule.properties[:provider].to_s).to eq(provider.name.to_s) + end + end + + it 'should ignore lines with fatal errors' do + allow(Puppet::Util::Execution).to receive(:execute).with(['/sbin/iptables-save']). + and_return("FATAL: Could not load /lib/modules/2.6.18-028stab095.1/modules.dep: No such file or directory") + + expect(provider.instances.length).to be_zero + end + + describe '#insert_order' do + let(:iptables_save_output) { [ + '-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT', + '-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -m comment --comment "200 test" -j ACCEPT', + '-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT' + ] } + let(:resources) do + iptables_save_output.each_with_index.collect { |l,index| provider.rule_to_hash(l, 'filter', index) } + end + let(:providers) do + resources.collect { |r| provider.new(r) } + end + it 'understands offsets for adding rules to the beginning' do + resource = Puppet::Type.type(:firewall).new({ :name => '001 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(1) # 1-indexed + end + it 'understands offsets for editing rules at the beginning' do + resource = Puppet::Type.type(:firewall).new({ :name => '100 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(1) + end + it 'understands offsets for adding rules to the middle' do + resource = Puppet::Type.type(:firewall).new({ :name => '101 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(2) + end + it 'understands offsets for editing rules at the middle' do + resource = Puppet::Type.type(:firewall).new({ :name => '200 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(2) + end + it 'understands offsets for adding rules to the end' do + resource = Puppet::Type.type(:firewall).new({ :name => '301 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(4) + end + it 'understands offsets for editing rules at the end' do + resource = Puppet::Type.type(:firewall).new({ :name => '300 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(3) + end + + context 'with unname rules between' do + let(:iptables_save_output) { [ + '-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT', + '-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 150 -m comment --comment "150 test" -j ACCEPT', + '-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -j ACCEPT', + '-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 250 -j ACCEPT', + '-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT', + '-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 350 -m comment --comment "350 test" -j ACCEPT', + ] } + it 'understands offsets for adding rules before unnamed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '001 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(1) + end + it 'understands offsets for editing rules before unnamed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '100 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(1) + end + it 'understands offsets for adding rules between managed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '120 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(2) + end + it 'understands offsets for adding rules between unnamed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '151 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(3) + end + it 'understands offsets for adding rules after unnamed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '351 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(7) + end + end + + context 'with unname rules before and after' do + let(:iptables_save_output) { [ + '-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 050 -j ACCEPT', + '-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 090 -j ACCEPT', + '-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 100 -m comment --comment "100 test" -j ACCEPT', + '-A INPUT -s 8.0.0.2/32 -p tcp -m multiport --ports 150 -m comment --comment "150 test" -j ACCEPT', + '-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 200 -j ACCEPT', + '-A INPUT -s 8.0.0.3/32 -p tcp -m multiport --ports 250 -j ACCEPT', + '-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 300 -m comment --comment "300 test" -j ACCEPT', + '-A INPUT -s 8.0.0.4/32 -p tcp -m multiport --ports 350 -m comment --comment "350 test" -j ACCEPT', + '-A INPUT -s 8.0.0.5/32 -p tcp -m multiport --ports 400 -j ACCEPT', + '-A INPUT -s 8.0.0.5/32 -p tcp -m multiport --ports 450 -j ACCEPT', + ] } + it 'understands offsets for adding rules before unnamed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '001 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(1) + end + it 'understands offsets for editing rules before unnamed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '100 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(3) + end + it 'understands offsets for adding rules between managed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '120 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(4) + end + it 'understands offsets for adding rules between unnamed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '151 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(5) + end + it 'understands offsets for adding rules after unnamed rules' do + resource = Puppet::Type.type(:firewall).new({ :name => '351 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(9) + end + it 'understands offsets for adding rules at the end' do + resource = Puppet::Type.type(:firewall).new({ :name => '950 test', }) + allow(resource.provider.class).to receive(:instances).and_return(providers) + expect(resource.provider.insert_order).to eq(11) + end + end + end + + # Load in ruby hash for test fixtures. + load 'spec/fixtures/iptables/conversion_hash.rb' + + describe 'when converting rules to resources' do + ARGS_TO_HASH.each do |test_name,data| + describe "for test data '#{test_name}'" do + let(:resource) { provider.rule_to_hash(data[:line], data[:table], 0) } + + # If this option is enabled, make sure the parameters exactly match + if data[:compare_all] then + it "the parameter hash keys should be the same as returned by rules_to_hash" do + expect(resource.keys).to match_array(data[:params].keys) + end + end + + # Iterate across each parameter, creating an example for comparison + data[:params].each do |param_name, param_value| + it "the parameter '#{param_name.to_s}' should match #{param_value.inspect}" do + # booleans get cludged to string "true" + if param_value == true then + expect(resource[param_name]).to be_true + else + expect(resource[param_name]).to eq(data[:params][param_name]) + end + end + end + end + end + end + + describe 'when working out general_args' do + HASH_TO_ARGS.each do |test_name,data| + describe "for test data '#{test_name}'" do + let(:resource) { Puppet::Type.type(:firewall).new(data[:params]) } + let(:provider) { Puppet::Type.type(:firewall).provider(:iptables) } + let(:instance) { provider.new(resource) } + + it 'general_args should be valid' do + expect(instance.general_args.flatten).to eq(data[:args]) + end + end + end + end + + describe 'when converting rules without comments to resources' do + let(:sample_rule) { + '-A INPUT -s 1.1.1.1 -d 1.1.1.1 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -j ACCEPT' + } + let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) } + let(:instance) { provider.new(resource) } + + it 'rule name contains a MD5 sum of the line' do + expect(resource[:name]).to eq("9000 #{Digest::MD5.hexdigest(resource[:line])}") + end + + it 'parsed the rule arguments correctly' do + expect(resource[:chain]).to eq('INPUT') + expect(resource[:source]).to eq('1.1.1.1/32') + expect(resource[:destination]).to eq('1.1.1.1/32') + expect(resource[:proto]).to eq('tcp') + expect(resource[:dport]).to eq(['7061', '7062']) + expect(resource[:sport]).to eq(['7061', '7062']) + expect(resource[:action]).to eq('accept') + end + end + + describe 'when converting existing rules generates by system-config-firewall-tui to resources' do + let(:sample_rule) { + # as generated by iptables-save from rules created with system-config-firewall-tui + '-A INPUT -p tcp -m state --state NEW -m tcp --dport 22 -j ACCEPT' + } + let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) } + let(:instance) { provider.new(resource) } + + it 'rule name contains a MD5 sum of the line' do + expect(resource[:name]).to eq("9000 #{Digest::MD5.hexdigest(resource[:line])}") + end + + it 'parse arguments' do + expect(resource[:chain]).to eq('INPUT') + expect(resource[:proto]).to eq('tcp') + expect(resource[:dport]).to eq(['22']) + expect(resource[:state]).to eq(['NEW']) + expect(resource[:action]).to eq('accept') + end + end + + describe 'when creating resources' do + let(:instance) { provider.new(resource) } + + it 'insert_args should be an array' do + expect(instance.insert_args.class).to eq(Array) + end + end + + describe 'when modifying resources' do + let(:instance) { provider.new(resource) } + + it 'update_args should be an array' do + expect(instance.update_args.class).to eq(Array) + end + + it 'fails when modifying the chain' do + expect { instance.chain = "OUTPUT" }.to raise_error(/is not supported/) + end + end + + describe 'when deleting resources' do + let(:sample_rule) { + '-A INPUT -s 1.1.1.1 -d 1.1.1.1 -p tcp -m multiport --dports 7061,7062 -m multiport --sports 7061,7062 -j ACCEPT' + } + let(:resource) { provider.rule_to_hash(sample_rule, 'filter', 0) } + let(:instance) { provider.new(resource) } + + it 'resource[:line] looks like the original rule' do + resource[:line] == sample_rule + end + + it 'delete_args is an array' do + expect(instance.delete_args.class).to eq(Array) + end + + it 'delete_args is the same as the rule string when joined' do + expect(instance.delete_args.join(' ')).to eq(sample_rule.gsub(/\-A/, + '-t filter -D')) + end + end +end + +describe 'ip6tables provider' do + let(:provider6) { Puppet::Type.type(:firewall).provider(:ip6tables) } + let(:resource) { + Puppet::Type.type(:firewall).new({ + :name => '000 test foo', + :action => 'accept', + :provider => "ip6tables", + }) + } + + before :each do + allow(Puppet::Type::Firewall).to receive(:ip6tables).and_return provider6 + allow(provider6).to receive(:command).with(:ip6tables_save).and_return "/sbin/ip6tables-save" + + # Stub iptables version + allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return '1.4.7' + + allow(Puppet::Util::Execution).to receive(:execute).and_return '' + allow(Puppet::Util).to receive(:which).with("ip6tables-save"). + and_return "/sbin/ip6tables-save" + end + + it 'should be able to get a list of existing rules' do + provider6.instances.each do |rule| + rule.should be_instance_of(provider6) + rule.properties[:provider6].to_s.should == provider6.name.to_s + end + end + + it 'should ignore lines with fatal errors' do + allow(Puppet::Util::Execution).to receive(:execute).with(['/sbin/ip6tables-save']). + and_return("FATAL: Could not load /lib/modules/2.6.18-028stab095.1/modules.dep: No such file or directory") + provider6.instances.length.should == 0 + end + + # Load in ruby hash for test fixtures. + load 'spec/fixtures/ip6tables/conversion_hash.rb' + + describe 'when converting rules to resources' do + ARGS_TO_HASH6.each do |test_name,data| + describe "for test data '#{test_name}'" do + let(:resource) { provider6.rule_to_hash(data[:line], data[:table], 0) } + + # If this option is enabled, make sure the parameters exactly match + if data[:compare_all] then + it "the parameter hash keys should be the same as returned by rules_to_hash" do + resource.keys.should =~ data[:params].keys + end + end + + # Iterate across each parameter, creating an example for comparison + data[:params].each do |param_name, param_value| + it "the parameter '#{param_name.to_s}' should match #{param_value.inspect}" do + resource[param_name].should == data[:params][param_name] + end + end + end + end + end + + describe 'when working out general_args' do + HASH_TO_ARGS6.each do |test_name,data| + describe "for test data '#{test_name}'" do + let(:resource) { Puppet::Type.type(:firewall).new(data[:params]) } + let(:provider6) { Puppet::Type.type(:firewall).provider(:ip6tables) } + let(:instance) { provider6.new(resource) } + + it 'general_args should be valid' do + instance.general_args.flatten.should == data[:args] + end + end + end + end +end + diff --git a/firewall/spec/unit/puppet/type/firewall_spec.rb b/firewall/spec/unit/puppet/type/firewall_spec.rb new file mode 100755 index 000000000..afb61662b --- /dev/null +++ b/firewall/spec/unit/puppet/type/firewall_spec.rb @@ -0,0 +1,650 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +firewall = Puppet::Type.type(:firewall) + +describe firewall do + before :each do + @class = firewall + @provider = double 'provider' + allow(@provider).to receive(:name).and_return(:iptables) + allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return @provider + + @resource = @class.new({:name => '000 test foo'}) + + # Stub iptables version + allow(Facter.fact(:iptables_version)).to receive(:value).and_return('1.4.2') + allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return('1.4.2') + + # Stub confine facts + allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux') + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Debian') + end + + it 'should have :name be its namevar' do + @class.key_attributes.should == [:name] + end + + describe ':name' do + it 'should accept a name' do + @resource[:name] = '000-test-foo' + @resource[:name].should == '000-test-foo' + end + + it 'should not accept a name with non-ASCII chars' do + lambda { @resource[:name] = '%*#^(#$' }.should raise_error(Puppet::Error) + end + end + + describe ':action' do + it "should have no default" do + res = @class.new(:name => "000 test") + res.parameters[:action].should == nil + end + + [:accept, :drop, :reject].each do |action| + it "should accept value #{action}" do + @resource[:action] = action + @resource[:action].should == action + end + end + + it 'should fail when value is not recognized' do + lambda { @resource[:action] = 'not valid' }.should raise_error(Puppet::Error) + end + end + + describe ':chain' do + [:INPUT, :FORWARD, :OUTPUT, :PREROUTING, :POSTROUTING].each do |chain| + it "should accept chain value #{chain}" do + @resource[:chain] = chain + @resource[:chain].should == chain + end + end + + it 'should fail when the chain value is not recognized' do + lambda { @resource[:chain] = 'not valid' }.should raise_error(Puppet::Error) + end + end + + describe ':table' do + [:nat, :mangle, :filter, :raw].each do |table| + it "should accept table value #{table}" do + @resource[:table] = table + @resource[:table].should == table + end + end + + it "should fail when table value is not recognized" do + lambda { @resource[:table] = 'not valid' }.should raise_error(Puppet::Error) + end + end + + describe ':proto' do + [:tcp, :udp, :icmp, :esp, :ah, :vrrp, :igmp, :ipencap, :ospf, :gre, :all].each do |proto| + it "should accept proto value #{proto}" do + @resource[:proto] = proto + @resource[:proto].should == proto + end + end + + it "should fail when proto value is not recognized" do + lambda { @resource[:proto] = 'foo' }.should raise_error(Puppet::Error) + end + end + + describe ':jump' do + it "should have no default" do + res = @class.new(:name => "000 test") + res.parameters[:jump].should == nil + end + + ['QUEUE', 'RETURN', 'DNAT', 'SNAT', 'LOG', 'MASQUERADE', 'REDIRECT', 'MARK'].each do |jump| + it "should accept jump value #{jump}" do + @resource[:jump] = jump + @resource[:jump].should == jump + end + end + + ['ACCEPT', 'DROP', 'REJECT'].each do |jump| + it "should now fail when value #{jump}" do + lambda { @resource[:jump] = jump }.should raise_error(Puppet::Error) + end + end + + it "should fail when jump value is not recognized" do + lambda { @resource[:jump] = '%^&*' }.should raise_error(Puppet::Error) + end + end + + [:source, :destination].each do |addr| + describe addr do + it "should accept a #{addr} as a string" do + @resource[addr] = '127.0.0.1' + @resource[addr].should == '127.0.0.1/32' + end + ['0.0.0.0/0', '::/0'].each do |prefix| + it "should be nil for zero prefix length address #{prefix}" do + @resource[addr] = prefix + @resource[addr].should == nil + end + end + it "should accept a negated #{addr} as a string" do + @resource[addr] = '! 127.0.0.1' + @resource[addr].should == '! 127.0.0.1/32' + end + end + end + + [:dport, :sport].each do |port| + describe port do + it "should accept a #{port} as string" do + @resource[port] = '22' + @resource[port].should == ['22'] + end + + it "should accept a #{port} as an array" do + @resource[port] = ['22','23'] + @resource[port].should == ['22','23'] + end + + it "should accept a #{port} as a number" do + @resource[port] = 22 + @resource[port].should == ['22'] + end + + it "should accept a #{port} as a hyphen separated range" do + @resource[port] = ['22-1000'] + @resource[port].should == ['22-1000'] + end + + it "should accept a #{port} as a combination of arrays of single and " \ + "hyphen separated ranges" do + + @resource[port] = ['22-1000','33','3000-4000'] + @resource[port].should == ['22-1000','33','3000-4000'] + end + + it "should convert a port name for #{port} to its number" do + @resource[port] = 'ssh' + @resource[port].should == ['22'] + end + + it "should not accept something invalid for #{port}" do + expect { @resource[port] = 'something odd' }.to raise_error(Puppet::Error, /^Parameter .+ failed.+Munging failed for value ".+" in class .+: no such service/) + end + + it "should not accept something invalid in an array for #{port}" do + expect { @resource[port] = ['something odd','something even odder'] }.to raise_error(Puppet::Error, /^Parameter .+ failed.+Munging failed for value ".+" in class .+: no such service/) + end + end + end + + [:dst_type, :src_type].each do |addrtype| + describe addrtype do + it "should have no default" do + res = @class.new(:name => "000 test") + res.parameters[addrtype].should == nil + end + end + + [:UNSPEC, :UNICAST, :LOCAL, :BROADCAST, :ANYCAST, :MULTICAST, :BLACKHOLE, + :UNREACHABLE, :PROHIBIT, :THROW, :NAT, :XRESOLVE].each do |type| + it "should accept #{addrtype} value #{type}" do + @resource[addrtype] = type + @resource[addrtype].should == type + end + end + + it "should fail when #{addrtype} value is not recognized" do + lambda { @resource[addrtype] = 'foo' }.should raise_error(Puppet::Error) + end + end + + [:iniface, :outiface].each do |iface| + describe iface do + it "should accept #{iface} value as a string" do + @resource[iface] = 'eth1' + @resource[iface].should == 'eth1' + end + end + end + + [:tosource, :todest].each do |addr| + describe addr do + it "should accept #{addr} value as a string" do + @resource[addr] = '127.0.0.1' + end + end + end + + describe ':log_level' do + values = { + 'panic' => '0', + 'alert' => '1', + 'crit' => '2', + 'err' => '3', + 'warn' => '4', + 'warning' => '4', + 'not' => '5', + 'notice' => '5', + 'info' => '6', + 'debug' => '7' + } + + values.each do |k,v| + it { + @resource[:log_level] = k + @resource[:log_level].should == v + } + + it { + @resource[:log_level] = 3 + @resource[:log_level].should == 3 + } + + it { lambda { @resource[:log_level] = 'foo' }.should raise_error(Puppet::Error) } + end + end + + describe ':icmp' do + icmp_codes = { + :iptables => { + '0' => 'echo-reply', + '3' => 'destination-unreachable', + '4' => 'source-quench', + '6' => 'redirect', + '8' => 'echo-request', + '9' => 'router-advertisement', + '10' => 'router-solicitation', + '11' => 'time-exceeded', + '12' => 'parameter-problem', + '13' => 'timestamp-request', + '14' => 'timestamp-reply', + '17' => 'address-mask-request', + '18' => 'address-mask-reply' + }, + :ip6tables => { + '1' => 'destination-unreachable', + '3' => 'time-exceeded', + '4' => 'parameter-problem', + '128' => 'echo-request', + '129' => 'echo-reply', + '133' => 'router-solicitation', + '134' => 'router-advertisement', + '137' => 'redirect' + } + } + icmp_codes.each do |provider, values| + describe provider do + values.each do |k,v| + it 'should convert icmp string to number' do + @resource[:provider] = provider + @resource[:provider].should == provider + @resource[:icmp] = v + @resource[:icmp].should == k + end + end + end + end + + it 'should accept values as integers' do + @resource[:icmp] = 9 + @resource[:icmp].should == 9 + end + + it 'should fail if icmp type is "any"' do + lambda { @resource[:icmp] = 'any' }.should raise_error(Puppet::Error) + end + + it 'should fail if icmp type cannot be mapped to a numeric' do + lambda { @resource[:icmp] = 'foo' }.should raise_error(Puppet::Error) + end + end + + describe ':state' do + it 'should accept value as a string' do + @resource[:state] = :INVALID + @resource[:state].should == [:INVALID] + end + + it 'should accept value as an array' do + @resource[:state] = [:INVALID, :NEW] + @resource[:state].should == [:INVALID, :NEW] + end + + it 'should sort values alphabetically' do + @resource[:state] = [:NEW, :ESTABLISHED] + @resource[:state].should == [:ESTABLISHED, :NEW] + end + end + + describe ':ctstate' do + it 'should accept value as a string' do + @resource[:ctstate] = :INVALID + @resource[:ctstate].should == [:INVALID] + end + + it 'should accept value as an array' do + @resource[:ctstate] = [:INVALID, :NEW] + @resource[:ctstate].should == [:INVALID, :NEW] + end + + it 'should sort values alphabetically' do + @resource[:ctstate] = [:NEW, :ESTABLISHED] + @resource[:ctstate].should == [:ESTABLISHED, :NEW] + end + end + + describe ':burst' do + it 'should accept numeric values' do + @resource[:burst] = 12 + @resource[:burst].should == 12 + end + + it 'should fail if value is not numeric' do + lambda { @resource[:burst] = 'foo' }.should raise_error(Puppet::Error) + end + end + + describe ':recent' do + ['set', 'update', 'rcheck', 'remove'].each do |recent| + it "should accept recent value #{recent}" do + @resource[:recent] = recent + @resource[:recent].should == "--#{recent}" + end + end + end + + describe ':action and :jump' do + it 'should allow only 1 to be set at a time' do + expect { + @class.new( + :name => "001-test", + :action => "accept", + :jump => "custom_chain" + ) + }.to raise_error(Puppet::Error, /Only one of the parameters 'action' and 'jump' can be set$/) + end + end + describe ':gid and :uid' do + it 'should allow me to set uid' do + @resource[:uid] = 'root' + @resource[:uid].should == 'root' + end + it 'should allow me to set uid as an array, and silently hide my error' do + @resource[:uid] = ['root', 'bobby'] + @resource[:uid].should == 'root' + end + it 'should allow me to set gid' do + @resource[:gid] = 'root' + @resource[:gid].should == 'root' + end + it 'should allow me to set gid as an array, and silently hide my error' do + @resource[:gid] = ['root', 'bobby'] + @resource[:gid].should == 'root' + end + end + + describe ':set_mark' do + ['1.3.2', '1.4.2'].each do |iptables_version| + describe "with iptables #{iptables_version}" do + before { + Facter.clear + allow(Facter.fact(:iptables_version)).to receive(:value).and_return iptables_version + allow(Facter.fact(:ip6tables_version)).to receive(:value).and_return iptables_version + } + + if iptables_version == '1.3.2' + it 'should allow me to set set-mark without mask' do + @resource[:set_mark] = '0x3e8' + @resource[:set_mark].should == '0x3e8' + end + it 'should convert int to hex without mask' do + @resource[:set_mark] = '1000' + @resource[:set_mark].should == '0x3e8' + end + it 'should fail if mask is present' do + lambda { @resource[:set_mark] = '0x3e8/0xffffffff'}.should raise_error( + Puppet::Error, /iptables version #{iptables_version} does not support masks on MARK rules$/ + ) + end + end + + if iptables_version == '1.4.2' + it 'should allow me to set set-mark with mask' do + @resource[:set_mark] = '0x3e8/0xffffffff' + @resource[:set_mark].should == '0x3e8/0xffffffff' + end + it 'should convert int to hex and add a 32 bit mask' do + @resource[:set_mark] = '1000' + @resource[:set_mark].should == '0x3e8/0xffffffff' + end + it 'should add a 32 bit mask' do + @resource[:set_mark] = '0x32' + @resource[:set_mark].should == '0x32/0xffffffff' + end + it 'should use the mask provided' do + @resource[:set_mark] = '0x32/0x4' + @resource[:set_mark].should == '0x32/0x4' + end + it 'should use the mask provided and convert int to hex' do + @resource[:set_mark] = '1000/0x4' + @resource[:set_mark].should == '0x3e8/0x4' + end + it 'should fail if mask value is more than 32 bits' do + lambda { @resource[:set_mark] = '1/4294967296'}.should raise_error( + Puppet::Error, /MARK mask must be integer or hex between 0 and 0xffffffff$/ + ) + end + it 'should fail if mask is malformed' do + lambda { @resource[:set_mark] = '1000/0xq4'}.should raise_error( + Puppet::Error, /MARK mask must be integer or hex between 0 and 0xffffffff$/ + ) + end + end + + ['/', '1000/', 'pwnie'].each do |bad_mark| + it "should fail with malformed mark '#{bad_mark}'" do + lambda { @resource[:set_mark] = bad_mark}.should raise_error(Puppet::Error) + end + end + it 'should fail if mark value is more than 32 bits' do + lambda { @resource[:set_mark] = '4294967296'}.should raise_error( + Puppet::Error, /MARK value must be integer or hex between 0 and 0xffffffff$/ + ) + end + end + end + end + + [:chain, :jump].each do |param| + describe param do + it 'should autorequire fwchain when table and provider are undefined' do + @resource[param] = 'FOO' + @resource[:table].should == :filter + @resource[:provider].should == :iptables + + chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv4') + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource chain + rel = @resource.autorequire[0] + rel.source.ref.should == chain.ref + rel.target.ref.should == @resource.ref + end + + it 'should autorequire fwchain when table is undefined and provider is ip6tables' do + @resource[param] = 'FOO' + @resource[:table].should == :filter + @resource[:provider] = :ip6tables + + chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv6') + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource chain + rel = @resource.autorequire[0] + rel.source.ref.should == chain.ref + rel.target.ref.should == @resource.ref + end + + it 'should autorequire fwchain when table is raw and provider is undefined' do + @resource[param] = 'FOO' + @resource[:table] = :raw + @resource[:provider].should == :iptables + + chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:raw:IPv4') + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource chain + rel = @resource.autorequire[0] + rel.source.ref.should == chain.ref + rel.target.ref.should == @resource.ref + end + + it 'should autorequire fwchain when table is raw and provider is ip6tables' do + @resource[param] = 'FOO' + @resource[:table] = :raw + @resource[:provider] = :ip6tables + + chain = Puppet::Type.type(:firewallchain).new(:name => 'FOO:raw:IPv6') + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource chain + rel = @resource.autorequire[0] + rel.source.ref.should == chain.ref + rel.target.ref.should == @resource.ref + end + + # test where autorequire is still needed (table != filter) + ['INPUT', 'OUTPUT', 'FORWARD'].each do |test_chain| + it "should autorequire fwchain #{test_chain} when table is mangle and provider is undefined" do + @resource[param] = test_chain + @resource[:table] = :mangle + @resource[:provider].should == :iptables + + chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:mangle:IPv4") + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource chain + rel = @resource.autorequire[0] + rel.source.ref.should == chain.ref + rel.target.ref.should == @resource.ref + end + + it "should autorequire fwchain #{test_chain} when table is mangle and provider is ip6tables" do + @resource[param] = test_chain + @resource[:table] = :mangle + @resource[:provider] = :ip6tables + + chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:mangle:IPv6") + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource chain + rel = @resource.autorequire[0] + rel.source.ref.should == chain.ref + rel.target.ref.should == @resource.ref + end + end + + # test of case where autorequire should not happen + ['INPUT', 'OUTPUT', 'FORWARD'].each do |test_chain| + + it "should not autorequire fwchain #{test_chain} when table and provider are undefined" do + @resource[param] = test_chain + @resource[:table].should == :filter + @resource[:provider].should == :iptables + + chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:filter:IPv4") + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource chain + rel = @resource.autorequire[0] + rel.should == nil + end + + it "should not autorequire fwchain #{test_chain} when table is undefined and provider is ip6tables" do + @resource[param] = test_chain + @resource[:table].should == :filter + @resource[:provider] = :ip6tables + + chain = Puppet::Type.type(:firewallchain).new(:name => "#{test_chain}:filter:IPv6") + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource chain + rel = @resource.autorequire[0] + rel.should == nil + end + end + end + end + + describe ":chain and :jump" do + it 'should autorequire independent fwchains' do + @resource[:chain] = 'FOO' + @resource[:jump] = 'BAR' + @resource[:table].should == :filter + @resource[:provider].should == :iptables + + chain_foo = Puppet::Type.type(:firewallchain).new(:name => 'FOO:filter:IPv4') + chain_bar = Puppet::Type.type(:firewallchain).new(:name => 'BAR:filter:IPv4') + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource chain_foo + catalog.add_resource chain_bar + rel = @resource.autorequire + rel[0].source.ref.should == chain_foo.ref + rel[0].target.ref.should == @resource.ref + rel[1].source.ref.should == chain_bar.ref + rel[1].target.ref.should == @resource.ref + end + end + + describe ':pkttype' do + [:multicast, :broadcast, :unicast].each do |pkttype| + it "should accept pkttype value #{pkttype}" do + @resource[:pkttype] = pkttype + @resource[:pkttype].should == pkttype + end + end + + it 'should fail when the pkttype value is not recognized' do + lambda { @resource[:pkttype] = 'not valid' }.should raise_error(Puppet::Error) + end + end + + describe 'autorequire packages' do + [:iptables, :ip6tables].each do |provider| + it "provider #{provider} should autorequire package iptables" do + @resource[:provider] = provider + @resource[:provider].should == provider + package = Puppet::Type.type(:package).new(:name => 'iptables') + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + catalog.add_resource package + rel = @resource.autorequire[0] + rel.source.ref.should == package.ref + rel.target.ref.should == @resource.ref + end + + it "provider #{provider} should autorequire packages iptables and iptables-persistent" do + @resource[:provider] = provider + @resource[:provider].should == provider + packages = [ + Puppet::Type.type(:package).new(:name => 'iptables'), + Puppet::Type.type(:package).new(:name => 'iptables-persistent') + ] + catalog = Puppet::Resource::Catalog.new + catalog.add_resource @resource + packages.each do |package| + catalog.add_resource package + end + packages.zip(@resource.autorequire) do |package, rel| + rel.source.ref.should == package.ref + rel.target.ref.should == @resource.ref + end + end + end + end +end diff --git a/firewall/spec/unit/puppet/type/firewallchain_spec.rb b/firewall/spec/unit/puppet/type/firewallchain_spec.rb new file mode 100755 index 000000000..88ca99dc5 --- /dev/null +++ b/firewall/spec/unit/puppet/type/firewallchain_spec.rb @@ -0,0 +1,185 @@ +#!/usr/bin/env rspec + +require 'spec_helper' + +firewallchain = Puppet::Type.type(:firewallchain) + +describe firewallchain do + before(:each) do + # Stub confine facts + allow(Facter.fact(:kernel)).to receive(:value).and_return('Linux') + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Debian') + end + let(:klass) { firewallchain } + let(:provider) { + prov = double 'provider' + allow(prov).to receive(:name).and_return(:iptables_chain) + prov + } + let(:resource) { + allow(Puppet::Type::Firewallchain).to receive(:defaultprovider).and_return provider + klass.new({:name => 'INPUT:filter:IPv4', :policy => :accept }) + } + + it 'should have :name be its namevar' do + klass.key_attributes.should == [:name] + end + + describe ':name' do + {'nat' => ['PREROUTING', 'POSTROUTING', 'INPUT', 'OUTPUT'], + 'mangle' => [ 'PREROUTING', 'POSTROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ], + 'filter' => ['INPUT','OUTPUT','FORWARD'], + 'raw' => [ 'PREROUTING', 'OUTPUT'], + 'broute' => ['BROUTING'] + }.each_pair do |table, allowedinternalchains| + ['IPv4', 'IPv6', 'ethernet'].each do |protocol| + [ 'test', '$5()*&%\'"^$09):' ].each do |chainname| + name = "#{chainname}:#{table}:#{protocol}" + if table == 'nat' && protocol == 'IPv6' + it "should fail #{name}" do + expect { resource[:name] = name }.to raise_error(Puppet::Error) + end + elsif protocol != 'ethernet' && table == 'broute' + it "should fail #{name}" do + expect { resource[:name] = name }.to raise_error(Puppet::Error) + end + else + it "should accept name #{name}" do + resource[:name] = name + resource[:name].should == name + end + end + end # chainname + end # protocol + + [ 'PREROUTING', 'POSTROUTING', 'BROUTING', 'INPUT', 'FORWARD', 'OUTPUT' ].each do |internalchain| + name = internalchain + ':' + table + ':' + if internalchain == 'BROUTING' + name += 'ethernet' + elsif table == 'nat' + name += 'IPv4' + else + name += 'IPv4' + end + if allowedinternalchains.include? internalchain + it "should allow #{name}" do + resource[:name] = name + resource[:name].should == name + end + else + it "should fail #{name}" do + expect { resource[:name] = name }.to raise_error(Puppet::Error) + end + end + end # internalchain + + end # table, allowedinternalchainnames + + it 'should fail with invalid table names' do + expect { resource[:name] = 'wrongtablename:test:IPv4' }.to raise_error(Puppet::Error) + end + + it 'should fail with invalid protocols names' do + expect { resource[:name] = 'test:filter:IPv5' }.to raise_error(Puppet::Error) + end + + end + + describe ':policy' do + + [:accept, :drop, :queue, :return].each do |policy| + it "should accept policy #{policy}" do + resource[:policy] = policy + resource[:policy].should == policy + end + end + + it 'should fail when value is not recognized' do + expect { resource[:policy] = 'not valid' }.to raise_error(Puppet::Error) + end + + [:accept, :drop, :queue, :return].each do |policy| + it "non-inbuilt chains should not accept policy #{policy}" do + expect { klass.new({:name => 'testchain:filter:IPv4', :policy => policy }) }.to raise_error(Puppet::Error) + end + it "non-inbuilt chains can accept policies on protocol = ethernet (policy #{policy})" do + klass.new({:name => 'testchain:filter:ethernet', :policy => policy }) + end + end + + end + + describe 'autorequire packages' do + it "provider iptables_chain should autorequire package iptables" do + resource[:provider].should == :iptables_chain + package = Puppet::Type.type(:package).new(:name => 'iptables') + catalog = Puppet::Resource::Catalog.new + catalog.add_resource resource + catalog.add_resource package + rel = resource.autorequire[0] + rel.source.ref.should == package.ref + rel.target.ref.should == resource.ref + end + + it "provider iptables_chain should autorequire packages iptables and iptables-persistent" do + resource[:provider].should == :iptables_chain + packages = [ + Puppet::Type.type(:package).new(:name => 'iptables'), + Puppet::Type.type(:package).new(:name => 'iptables-persistent') + ] + catalog = Puppet::Resource::Catalog.new + catalog.add_resource resource + packages.each do |package| + catalog.add_resource package + end + packages.zip(resource.autorequire) do |package, rel| + rel.source.ref.should == package.ref + rel.target.ref.should == resource.ref + end + end + end + + describe 'purge iptables rules' do + before(:each) do + allow(Puppet::Type.type(:firewall).provider(:iptables)).to receive(:iptables_save).and_return(< 'INPUT:filter:IPv4', :purge => true) + + expect(resource.generate.size).to eq(3) + end + + it 'should not generate ignored iptables rules' do + resource = Puppet::Type::Firewallchain.new(:name => 'INPUT:filter:IPv4', :purge => true, :ignore => ['-j fail2ban-ssh']) + + expect(resource.generate.size).to eq(2) + end + + it 'should not generate iptables resources when not enabled' do + resource = Puppet::Type::Firewallchain.new(:name => 'INPUT:filter:IPv4') + + expect(resource.generate.size).to eq(0) + end + end +end diff --git a/firewall/spec/unit/puppet/util/firewall_spec.rb b/firewall/spec/unit/puppet/util/firewall_spec.rb new file mode 100644 index 000000000..8c33c34f0 --- /dev/null +++ b/firewall/spec/unit/puppet/util/firewall_spec.rb @@ -0,0 +1,196 @@ +require 'spec_helper' + +describe 'Puppet::Util::Firewall' do + let(:resource) { + type = Puppet::Type.type(:firewall) + provider = double 'provider' + allow(provider).to receive(:name).and_return(:iptables) + allow(Puppet::Type::Firewall).to receive(:defaultprovider).and_return(provider) + type.new({:name => '000 test foo'}) + } + + before(:each) { resource } + + describe '#host_to_ip' do + subject { resource } + specify { + expect(Resolv).to receive(:getaddress).with('puppetlabs.com').and_return('96.126.112.51') + subject.host_to_ip('puppetlabs.com').should == '96.126.112.51/32' + } + specify { subject.host_to_ip('96.126.112.51').should == '96.126.112.51/32' } + specify { subject.host_to_ip('96.126.112.51/32').should == '96.126.112.51/32' } + specify { subject.host_to_ip('2001:db8:85a3:0:0:8a2e:370:7334').should == '2001:db8:85a3::8a2e:370:7334/128' } + specify { subject.host_to_ip('2001:db8:1234::/48').should == '2001:db8:1234::/48' } + specify { subject.host_to_ip('0.0.0.0/0').should == nil } + specify { subject.host_to_ip('::/0').should == nil } + end + + describe '#host_to_mask' do + subject { resource } + specify { + expect(Resolv).to receive(:getaddress).at_least(:once).with('puppetlabs.com').and_return('96.126.112.51') + subject.host_to_mask('puppetlabs.com').should == '96.126.112.51/32' + subject.host_to_mask('!puppetlabs.com').should == '! 96.126.112.51/32' + } + specify { subject.host_to_mask('96.126.112.51').should == '96.126.112.51/32' } + specify { subject.host_to_mask('!96.126.112.51').should == '! 96.126.112.51/32' } + specify { subject.host_to_mask('96.126.112.51/32').should == '96.126.112.51/32' } + specify { subject.host_to_mask('! 96.126.112.51/32').should == '! 96.126.112.51/32' } + specify { subject.host_to_mask('2001:db8:85a3:0:0:8a2e:370:7334').should == '2001:db8:85a3::8a2e:370:7334/128' } + specify { subject.host_to_mask('!2001:db8:85a3:0:0:8a2e:370:7334').should == '! 2001:db8:85a3::8a2e:370:7334/128' } + specify { subject.host_to_mask('2001:db8:1234::/48').should == '2001:db8:1234::/48' } + specify { subject.host_to_mask('! 2001:db8:1234::/48').should == '! 2001:db8:1234::/48' } + specify { subject.host_to_mask('0.0.0.0/0').should == nil } + specify { subject.host_to_mask('!0.0.0.0/0').should == nil } + specify { subject.host_to_mask('::/0').should == nil } + specify { subject.host_to_mask('! ::/0').should == nil } + end + + describe '#icmp_name_to_number' do + describe 'proto unsupported' do + subject { resource } + + %w{inet5 inet8 foo}.each do |proto| + it "should reject invalid proto #{proto}" do + expect { subject.icmp_name_to_number('echo-reply', proto) }. + to raise_error(ArgumentError, "unsupported protocol family '#{proto}'") + end + end + end + + describe 'proto IPv4' do + proto = 'inet' + subject { resource } + specify { subject.icmp_name_to_number('echo-reply', proto).should == '0' } + specify { subject.icmp_name_to_number('destination-unreachable', proto).should == '3' } + specify { subject.icmp_name_to_number('source-quench', proto).should == '4' } + specify { subject.icmp_name_to_number('redirect', proto).should == '6' } + specify { subject.icmp_name_to_number('echo-request', proto).should == '8' } + specify { subject.icmp_name_to_number('router-advertisement', proto).should == '9' } + specify { subject.icmp_name_to_number('router-solicitation', proto).should == '10' } + specify { subject.icmp_name_to_number('time-exceeded', proto).should == '11' } + specify { subject.icmp_name_to_number('parameter-problem', proto).should == '12' } + specify { subject.icmp_name_to_number('timestamp-request', proto).should == '13' } + specify { subject.icmp_name_to_number('timestamp-reply', proto).should == '14' } + specify { subject.icmp_name_to_number('address-mask-request', proto).should == '17' } + specify { subject.icmp_name_to_number('address-mask-reply', proto).should == '18' } + end + + describe 'proto IPv6' do + proto = 'inet6' + subject { resource } + specify { subject.icmp_name_to_number('destination-unreachable', proto).should == '1' } + specify { subject.icmp_name_to_number('time-exceeded', proto).should == '3' } + specify { subject.icmp_name_to_number('parameter-problem', proto).should == '4' } + specify { subject.icmp_name_to_number('echo-request', proto).should == '128' } + specify { subject.icmp_name_to_number('echo-reply', proto).should == '129' } + specify { subject.icmp_name_to_number('router-solicitation', proto).should == '133' } + specify { subject.icmp_name_to_number('router-advertisement', proto).should == '134' } + specify { subject.icmp_name_to_number('redirect', proto).should == '137' } + end + end + + describe '#string_to_port' do + subject { resource } + specify { subject.string_to_port('80','tcp').should == '80' } + specify { subject.string_to_port(80,'tcp').should == '80' } + specify { subject.string_to_port('http','tcp').should == '80' } + specify { subject.string_to_port('domain','udp').should == '53' } + end + + describe '#to_hex32' do + subject { resource } + specify { subject.to_hex32('0').should == '0x0' } + specify { subject.to_hex32('0x32').should == '0x32' } + specify { subject.to_hex32('42').should == '0x2a' } + specify { subject.to_hex32('4294967295').should == '0xffffffff' } + specify { subject.to_hex32('4294967296').should == nil } + specify { subject.to_hex32('-1').should == nil } + specify { subject.to_hex32('bananas').should == nil } + end + + describe '#persist_iptables' do + before { Facter.clear } + subject { resource } + + describe 'when proto is IPv4' do + let(:proto) { 'IPv4' } + + it 'should exec /sbin/service if running RHEL 6 or earlier' do + allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat') + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat') + allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('6') + + expect(subject).to receive(:execute).with(%w{/sbin/service iptables save}) + subject.persist_iptables(proto) + end + + it 'should exec for systemd if running RHEL 7 or greater' do + allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat') + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat') + allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('7') + + expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables/iptables.init save}) + subject.persist_iptables(proto) + end + + it 'should exec for systemd if running Fedora 15 or greater' do + allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat') + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Fedora') + allow(Facter.fact(:operatingsystemrelease)).to receive(:value).and_return('15') + + expect(subject).to receive(:execute).with(%w{/usr/libexec/iptables/iptables.init save}) + subject.persist_iptables(proto) + end + + it 'should exec for CentOS identified from operatingsystem' do + allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil) + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('CentOS') + expect(subject).to receive(:execute).with(%w{/sbin/service iptables save}) + subject.persist_iptables(proto) + end + + it 'should exec for Archlinux identified from osfamily' do + allow(Facter.fact(:osfamily)).to receive(:value).and_return('Archlinux') + expect(subject).to receive(:execute).with(['/bin/sh', '-c', '/usr/sbin/iptables-save > /etc/iptables/iptables.rules']) + subject.persist_iptables(proto) + end + + it 'should raise a warning when exec fails' do + allow(Facter.fact(:osfamily)).to receive(:value).and_return('RedHat') + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('RedHat') + + expect(subject).to receive(:execute).with(%w{/sbin/service iptables save}). + and_raise(Puppet::ExecutionFailure, 'some error') + expect(subject).to receive(:warning).with('Unable to persist firewall rules: some error') + subject.persist_iptables(proto) + end + end + + describe 'when proto is IPv6' do + let(:proto) { 'IPv6' } + + it 'should exec for newer Ubuntu' do + allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil) + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu') + allow(Facter.fact(:iptables_persistent_version)).to receive(:value).and_return('0.5.3ubuntu2') + expect(subject).to receive(:execute).with(%w{/usr/sbin/service iptables-persistent save}) + subject.persist_iptables(proto) + end + + it 'should not exec for older Ubuntu which does not support IPv6' do + allow(Facter.fact(:osfamily)).to receive(:value).and_return(nil) + allow(Facter.fact(:operatingsystem)).to receive(:value).and_return('Ubuntu') + allow(Facter.fact(:iptables_persistent_version)).to receive(:value).and_return('0.0.20090701') + expect(subject).to receive(:execute).never + subject.persist_iptables(proto) + end + + it 'should not exec for Suse which is not supported' do + allow(Facter.fact(:osfamily)).to receive(:value).and_return('Suse') + expect(subject).to receive(:execute).never + subject.persist_iptables(proto) + end + end + end +end diff --git a/firewall/spec/unit/puppet/util/ipcidr_spec.rb b/firewall/spec/unit/puppet/util/ipcidr_spec.rb new file mode 100644 index 000000000..916f74a35 --- /dev/null +++ b/firewall/spec/unit/puppet/util/ipcidr_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe 'Puppet::Util::IPCidr' do + describe 'ipv4 address' do + before { @ipaddr = Puppet::Util::IPCidr.new('96.126.112.51') } + subject { @ipaddr } + specify { subject.cidr.should == '96.126.112.51/32' } + specify { subject.prefixlen.should == 32 } + specify { subject.netmask.should == '255.255.255.255' } + end + + describe 'single ipv4 address with cidr' do + before { @ipcidr = Puppet::Util::IPCidr.new('96.126.112.51/32') } + subject { @ipcidr } + specify { subject.cidr.should == '96.126.112.51/32' } + specify { subject.prefixlen.should == 32 } + specify { subject.netmask.should == '255.255.255.255' } + end + + describe 'ipv4 address range with cidr' do + before { @ipcidr = Puppet::Util::IPCidr.new('96.126.112.0/24') } + subject { @ipcidr } + specify { subject.cidr.should == '96.126.112.0/24' } + specify { subject.prefixlen.should == 24 } + specify { subject.netmask.should == '255.255.255.0' } + end + + describe 'ipv4 open range with cidr' do + before { @ipcidr = Puppet::Util::IPCidr.new('0.0.0.0/0') } + subject { @ipcidr } + specify { subject.cidr.should == '0.0.0.0/0' } + specify { subject.prefixlen.should == 0 } + specify { subject.netmask.should == '0.0.0.0' } + end + + describe 'ipv6 address' do + before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:85a3:0:0:8a2e:370:7334') } + subject { @ipaddr } + specify { subject.cidr.should == '2001:db8:85a3::8a2e:370:7334/128' } + specify { subject.prefixlen.should == 128 } + specify { subject.netmask.should == 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' } + end + + describe 'single ipv6 addr with cidr' do + before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:85a3:0:0:8a2e:370:7334/128') } + subject { @ipaddr } + specify { subject.cidr.should == '2001:db8:85a3::8a2e:370:7334/128' } + specify { subject.prefixlen.should == 128 } + specify { subject.netmask.should == 'ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff' } + end + + describe 'ipv6 addr range with cidr' do + before { @ipaddr = Puppet::Util::IPCidr.new('2001:db8:1234::/48') } + subject { @ipaddr } + specify { subject.cidr.should == '2001:db8:1234::/48' } + specify { subject.prefixlen.should == 48 } + specify { subject.netmask.should == 'ffff:ffff:ffff:0000:0000:0000:0000:0000' } + end + + describe 'ipv6 open range with cidr' do + before { @ipaddr = Puppet::Util::IPCidr.new('::/0') } + subject { @ipaddr } + specify { subject.cidr.should == '::/0' } + specify { subject.prefixlen.should == 0 } + specify { subject.netmask.should == '0000:0000:0000:0000:0000:0000:0000:0000' } + end +end diff --git a/glance/.fixtures.yml b/glance/.fixtures.yml new file mode 100644 index 000000000..c05855f00 --- /dev/null +++ b/glance/.fixtures.yml @@ -0,0 +1,19 @@ +fixtures: + repositories: + "apt": "git://github.com/puppetlabs/puppetlabs-apt.git" + "keystone": + repo: "git://github.com/stackforge/puppet-keystone.git" + ref: 'origin/stable/havana' + "mysql": + repo: "git://github.com/puppetlabs/puppetlabs-mysql.git" + ref: 'origin/0.x' + "stdlib": "git://github.com/puppetlabs/puppetlabs-stdlib.git" + "rabbitmq": + repo: "git://github.com/puppetlabs/puppetlabs-rabbitmq" + ref: 'origin/2.x' + 'inifile': 'git://github.com/puppetlabs/puppetlabs-inifile' + "postgresql": + repo: "git://github.com/puppetlabs/puppet-postgresql.git" + ref: "2.5.0" + symlinks: + "glance": "#{source_dir}" diff --git a/glance/.gitignore b/glance/.gitignore new file mode 100644 index 000000000..d875d4b87 --- /dev/null +++ b/glance/.gitignore @@ -0,0 +1,4 @@ +spec/fixtures/modules/* +spec/fixtures/manifests/* +*swp +pkg diff --git a/glance/.gitreview b/glance/.gitreview new file mode 100644 index 000000000..7723979b7 --- /dev/null +++ b/glance/.gitreview @@ -0,0 +1,5 @@ +[gerrit] +host=review.openstack.org +port=29418 +project=stackforge/puppet-glance.git +defaultbranch=stable/havana diff --git a/glance/Gemfile b/glance/Gemfile new file mode 100644 index 000000000..d965fa900 --- /dev/null +++ b/glance/Gemfile @@ -0,0 +1,18 @@ +source 'https://rubygems.org' + +group :development, :test do + gem 'puppetlabs_spec_helper', :require => false + gem 'puppet-lint', '~> 0.3.2' + gem 'rake', '10.1.1' + gem 'rspec', '< 2.99' + gem 'json' + gem 'webmock' +end + +if puppetversion = ENV['PUPPET_GEM_VERSION'] + gem 'puppet', puppetversion, :require => false +else + gem 'puppet', :require => false +end + +# vim:ft=ruby diff --git a/glance/LICENSE b/glance/LICENSE new file mode 100644 index 000000000..8d968b6cb --- /dev/null +++ b/glance/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + 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. + 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. diff --git a/glance/Modulefile b/glance/Modulefile new file mode 100644 index 000000000..647f1033e --- /dev/null +++ b/glance/Modulefile @@ -0,0 +1,13 @@ +name 'puppetlabs-glance' +version '3.1.0' +source 'https://github.com/stackforge/puppet-glance' +author 'Puppet Labs' +license 'Apache License 2.0' +summary 'Puppet Labs Glance Module' +description 'Puppet module to install and configure the Openstack Glance image service' +project_page 'https://launchpad.net/puppet-openstack' + +dependency 'puppetlabs/inifile', '>=1.0.0 <2.0.0' +dependency 'puppetlabs/keystone', '>=3.0.0 <4.0.0' +dependency 'puppetlabs/mysql', '>=0.9.0 <1.0.0' +dependency 'puppetlabs/stdlib', '>= 3.2.0' diff --git a/glance/README.md b/glance/README.md new file mode 100644 index 000000000..3ae132b80 --- /dev/null +++ b/glance/README.md @@ -0,0 +1,163 @@ +glance +======= + +#### Table of Contents + +1. [Overview - What is the glance module?](#overview) +2. [Module Description - What does the module do?](#module-description) +3. [Setup - The basics of getting started with glance](#setup) +4. [Implementation - An under-the-hood peek at what the module is doing](#implementation) +5. [Limitations - OS compatibility, etc.](#limitations) +6. [Development - Guide for contributing to the module](#development) +7. [Contributors - Those with commits](#contributors) +8. [Release Notes - Notes on the most recent updates to the module](#release-notes) + +Overview +-------- + +The glance module is a part of [Stackforge](https://github.com/stackfoge), an effort by the Openstack infrastructure team to provide continuous integration testing and code review for Openstack and Openstack community projects not part of the core software. The module its self is used to flexibly configure and manage the image service for Openstack. + +Module Description +------------------ + +The glance module is a thorough attempt to make Puppet capable of managing the entirety of glance. This includes manifests to provision such things as keystone endpoints, RPC configurations specific to glance, and database connections. Types are shipped as part of the glance module to assist in manipulation of configuration files. + +This module is tested in combination with other modules needed to build and leverage an entire Openstack software stack. These modules can be found, all pulled together in the [openstack module](https://github.com/stackfoge/puppet-openstack). + +Setup +----- + +**What the glance module affects** + +* glance, the image service for Openstack. + +### Installing glance + + example% puppet module install puppetlabs/glance + +### Beginning with glance + +To utilize the glance module's functionality you will need to declare multiple resources. The following is a modified excerpt from the [openstack module](https://github.com/stackfoge/puppet-openstack). This is not an exhaustive list of all the components needed, we recommend you consult and understand the [openstack module](https://github.com/stackforge/puppet-openstack) and the [core openstack](http://docs.openstack.org) documentation. + +**Define a glance node** + +```puppet +class { 'glance::api': + verbose => true, + keystone_tenant => 'services', + keystone_user => 'glance', + keystone_password => '12345', + sql_connection => 'mysql://glance:12345@127.0.0.1/glance', +} + +class { 'glance::registry': + verbose => true, + keystone_tenant => 'services', + keystone_user => 'glance', + keystone_password => '12345', + sql_connection => 'mysql://glance:12345@127.0.0.1/glance', +} + +class { 'glance::backend::file': } +``` + +**Setup postgres node glance** + +```puppet +class { 'glance::db::postgresql': + password => '12345', +} +``` + +**Setup mysql node for glance** + +```puppet +class { 'glance::db::mysql': + password => '12345', + allowed_hosts => '%', +} +``` + +**Setup up keystone endpoints for glance on keystone node** + +```puppet +class { 'glance::keystone::auth': + password => '12345' + email => 'glance@example.com', + public_address => '172.17.0.3', + admin_address => '172.17.0.3', + internal_address => '172.17.1.3', + region => 'example-west-1', +} +``` + +Implementation +-------------- + +### glance + +glance is a combination of Puppet manifest and ruby code to deliver configuration and extra functionality through types and providers. + +Limitations +------------ + +* Only supports configuring the file, swift and rbd storage backends. + +Development +----------- + +Developer documentation for the entire puppet-openstack project. + +* https://wiki.openstack.org/wiki/Puppet-openstack#Developer_documentation + +Contributors +------------ + +* https://github.com/stackforge/puppet-glance/graphs/contributors + +Release Notes +------------- + +**3.1.0** + +* Added availability to configure show_image_direct_url. +* Removed Keystone client warnings. +* Added support for https authentication endpoints. +* Enabled ssl configuration for glance-registry. +* Explicitly sets default notifier strategy. + +**3.0.0** + +* Major release for OpenStack Havana. +* Fixed bug to ensure keystone endpoint is set before service starts. +* Added Cinder backend to image storage. +* Fixed qpid_hostname bug. + +**2.2.0** + +* Added syslog support. +* Added support for iso disk format. +* Fixed bug to allow support for rdb options in glance-api.conf. +* Fixed bug for rabbitmq options in notify::rabbitmq. +* Removed non-implemented glance::scrubber class. +* Various lint and bug fixes. + +**2.1.0** + +* Added glance-cache-cleaner and glance-cache-pruner. +* Added ceph/rdb support. +* Added retry for glance provider to account for service startup time. +* Added support for both file and swift backends. +* Fixed allowed_hosts/database access bug. +* Fixed glance_image type example. +* Removed unnecessary mysql::server dependency. +* Removed --silent-upload option. +* Removed glance-manage version_control. +* Pinned rabbit and mysql module versions. +* Various lint and bug fixes. + +**2.0.0** + +* Upstream is now part of stackfoge. +* Added postgresql support. +* Various cleanups and bug fixes. diff --git a/glance/Rakefile b/glance/Rakefile new file mode 100644 index 000000000..4c2b2ed07 --- /dev/null +++ b/glance/Rakefile @@ -0,0 +1,6 @@ +require 'puppetlabs_spec_helper/rake_tasks' +require 'puppet-lint/tasks/puppet-lint' + +PuppetLint.configuration.fail_on_warnings = true +PuppetLint.configuration.send('disable_80chars') +PuppetLint.configuration.send('disable_class_parameter_defaults') diff --git a/glance/ext/glance.rb b/glance/ext/glance.rb new file mode 100644 index 000000000..6510397c0 --- /dev/null +++ b/glance/ext/glance.rb @@ -0,0 +1,86 @@ +#!/usr/bin/env ruby +# +# test that we can upload and download files +# +require 'open3' +require 'fileutils' + +keystone_public = '127.0.0.1' +image_dir='/tmp/images' + +ENV['OS_USERNAME']='admin' +ENV['OS_TENANT_NAME']='admin' +ENV['OS_PASSWORD']='ChangeMe' +ENV['OS_AUTH_URL']='http://127.0.0.1:5000/v2.0/' +ENV['OS_REGION_NAME']='RegionOne' + +FileUtils.mkdir_p(image_dir) +Dir.chdir(image_dir) do |dir| + + kernel_id = nil + initrd_id = nil + + remote_image_url='http://smoser.brickies.net/ubuntu/ttylinux-uec/ttylinux-uec-amd64-12.1_2.6.35-22_1.tar.gz; tar -zxvf ttylinux-uec-amd64-12.1_2.6.35-22_1.tar.gz' + + wget_command = "wget #{remote_image_url}" + + Open3.popen3(wget_command) do |stdin, stdout, stderr| + puts "wget stdout: #{stdout.read}" + puts "wget stderr: #{stderr.read}" + end + + add_kernel='disk_format=aki container_format=aki < ttylinux-uec-amd64-12.1_2.6.35-22_1-vmlinuz' + kernel_name='tty-linux-kernel' + kernel_format='aki' + + add_kernel_command="glance add name='#{kernel_name}' disk_format='#{kernel_format}' container_format=#{kernel_format} < ttylinux-uec-amd64-12.1_2.6.35-22_1-vmlinuz" + + Open3.popen3(add_kernel_command) do |stdin, stdout, stderr| + stdout = stdout.read.split("\n") + stdout.each do |line| + if line =~ /Added new image with ID: (\w+)/ + kernel_id = $1 + end + end + puts stderr.read + puts stdout + end + + raise(Exception, 'Did not add kernel successfully') unless kernel_id + + initrd_id = nil + add_initrd_command="glance add name='tty-linux-ramdisk' disk_format=ari container_format=ari < ttylinux-uec-amd64-12.1_2.6.35-22_1-loader" + + Open3.popen3(add_initrd_command) do |stdin, stdout, stderr| + stdout = stdout.read.split("\n") + stdout.each do |line| + if line =~ /Added new image with ID: (\w+)/ + initrd_id = $1 + end + end + puts stderr.read + puts stdout + end + + raise(Exception, 'Did not add initrd successfully') unless initrd_id + + add_image_command="glance add name='tty-linux' disk_format=ami container_format=ami kernel_id=#{kernel_id} ramdisk_id=#{initrd_id} < ttylinux-uec-amd64-12.1_2.6.35-22_1.img" + + Open3.popen3(add_image_command) do |stdin, stdout, stderr| + stdout = stdout.read.split("\n") + stdout.each do |line| + if line =~ /Added new image with ID: (\w+)/ + kernel_id = $1 + end + end + puts stderr.read + puts stdout + end + + get_index='glance index' + + Open3.popen3(get_index) do |stdin, stdout, stderr| + puts stdout.read + puts stderr.read + end +end diff --git a/glance/ext/glance.sh b/glance/ext/glance.sh new file mode 100755 index 000000000..174749981 --- /dev/null +++ b/glance/ext/glance.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# +# assumes that resonable credentials have been stored at +# /root/auth +source /root/auth +wget http://uec-images.ubuntu.com/releases/11.10/release/ubuntu-11.10-server-cloudimg-amd64-disk1.img +glance add name="Ubuntu 11.10 cloudimg amd64" is_public=true container_format=ovf disk_format=qcow2 < ubuntu-11.10-server-cloudimg-amd64-disk1.img +glance index diff --git a/glance/lib/puppet/provider/glance.rb b/glance/lib/puppet/provider/glance.rb new file mode 100644 index 000000000..ef4eb23c1 --- /dev/null +++ b/glance/lib/puppet/provider/glance.rb @@ -0,0 +1,158 @@ +# Since there's only one glance type for now, +# this probably could have all gone in the provider file. +# But maybe this is good long-term. +require 'puppet/util/inifile' +class Puppet::Provider::Glance < Puppet::Provider + + def self.glance_credentials + @glance_credentials ||= get_glance_credentials + end + + def self.get_glance_credentials + if glance_file and glance_file['keystone_authtoken'] and + glance_file['keystone_authtoken']['auth_host'] and + glance_file['keystone_authtoken']['auth_port'] and + glance_file['keystone_authtoken']['auth_protocol'] and + glance_file['keystone_authtoken']['admin_tenant_name'] and + glance_file['keystone_authtoken']['admin_user'] and + glance_file['keystone_authtoken']['admin_password'] + + g = {} + g['auth_host'] = glance_file['keystone_authtoken']['auth_host'].strip + g['auth_port'] = glance_file['keystone_authtoken']['auth_port'].strip + g['auth_protocol'] = glance_file['keystone_authtoken']['auth_protocol'].strip + g['admin_tenant_name'] = glance_file['keystone_authtoken']['admin_tenant_name'].strip + g['admin_user'] = glance_file['keystone_authtoken']['admin_user'].strip + g['admin_password'] = glance_file['keystone_authtoken']['admin_password'].strip + + # auth_admin_prefix not required to be set. + g['auth_admin_prefix'] = (glance_file['keystone_authtoken']['auth_admin_prefix'] || '').strip + + return g + else + raise(Puppet::Error, 'File: /etc/glance/glance-api.conf does not contain all required sections.') + end + end + + def glance_credentials + self.class.glance_credentials + end + + def self.auth_endpoint + @auth_endpoint ||= get_auth_endpoint + end + + def self.get_auth_endpoint + g = glance_credentials + "#{g['auth_protocol']}://#{g['auth_host']}:#{g['auth_port']}#{g['auth_admin_prefix']}/v2.0/" + end + + def self.glance_file + return @glance_file if @glance_file + @glance_file = Puppet::Util::IniConfig::File.new + @glance_file.read('/etc/glance/glance-api.conf') + @glance_file + end + + def self.glance_hash + @glance_hash ||= build_glance_hash + end + + def self.reset + @glance_hash = nil + @glance_file = nil + @glance_credentials = nil + @auth_endpoint = nil + end + + def glance_hash + self.class.glance_hash + end + + def self.auth_glance(*args) + begin + g = glance_credentials + remove_warnings(glance('-T', g['admin_tenant_name'], '-I', g['admin_user'], '-K', g['admin_password'], '-N', auth_endpoint, args)) + rescue Exception => e + if (e.message =~ /\[Errno 111\] Connection refused/) or (e.message =~ /\(HTTP 400\)/) or (e.message =~ /HTTP Unable to establish connection/) + sleep 10 + remove_warnings(glance('-T', g['admin_tenant_name'], '-I', g['admin_user'], '-K', g['admin_password'], '-N', auth_endpoint, args)) + else + raise(e) + end + end + end + + def auth_glance(*args) + self.class.auth_glance(args) + end + + def self.auth_glance_stdin(*args) + begin + g = glance_credentials + command = "glance -T #{g['admin_tenant_name']} -I #{g['admin_user']} -K #{g['admin_password']} -N #{auth_endpoint} #{args.join(' ')}" + + # This is a horrible, horrible hack + # Redirect stderr to stdout in order to report errors + # Ignore good output + err = `#{command} 3>&1 1>/dev/null 2>&3` + if $? != 0 + raise(Puppet::Error, err) + end + end + end + + def auth_glance_stdin(*args) + self.class.auth_glance_stdin(args) + end + + + private + def self.list_glance_images + ids = [] + (auth_glance('index').split("\n")[2..-1] || []).collect do |line| + ids << line.split[0] + end + return ids + end + + def self.get_glance_image_attr(id, attr) + (auth_glance('show', id).split("\n") || []).collect do |line| + if line =~ /^#{attr}:/ + return line.split(': ')[1..-1] + end + end + end + + def self.get_glance_image_attrs(id) + attrs = {} + (auth_glance('show', id).split("\n") || []).collect do |line| + attrs[line.split(': ').first.downcase] = line.split(': ')[1..-1].pop + end + return attrs + end + + # Remove warning from the output. This is a temporary hack until + # things will be refactored to use the REST API + def self.remove_warnings(results) + found_header = false + in_warning = false + results.split("\n").collect do |line| + unless found_header + if line =~ /^\+[-\+]+\+$/ + in_warning = false + found_header = true + line + elsif line =~ /^WARNING/ or line =~ /UserWarning/ or in_warning + # warnings can be multi line, we have to skip all of them + in_warning = true + nil + else + line + end + else + line + end + end.compact.join("\n") + end +end diff --git a/glance/lib/puppet/provider/glance_api_config/ini_setting.rb b/glance/lib/puppet/provider/glance_api_config/ini_setting.rb new file mode 100644 index 000000000..4323ae8cd --- /dev/null +++ b/glance/lib/puppet/provider/glance_api_config/ini_setting.rb @@ -0,0 +1,27 @@ +Puppet::Type.type(:glance_api_config).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def self.file_path + '/etc/glance/glance-api.conf' + end + + # this needs to be removed. This has been replaced with the class method + def file_path + self.class.file_path + end + +end diff --git a/glance/lib/puppet/provider/glance_api_paste_ini/ini_setting.rb b/glance/lib/puppet/provider/glance_api_paste_ini/ini_setting.rb new file mode 100644 index 000000000..d624d64bc --- /dev/null +++ b/glance/lib/puppet/provider/glance_api_paste_ini/ini_setting.rb @@ -0,0 +1,27 @@ +Puppet::Type.type(:glance_api_paste_ini).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def self.file_path + '/etc/glance/glance-api-paste.ini' + end + + # added for backwards compatibility with older versions of inifile + def file_path + self.class.file_path + end + +end diff --git a/glance/lib/puppet/provider/glance_cache_config/ini_setting.rb b/glance/lib/puppet/provider/glance_cache_config/ini_setting.rb new file mode 100644 index 000000000..579dfb469 --- /dev/null +++ b/glance/lib/puppet/provider/glance_cache_config/ini_setting.rb @@ -0,0 +1,27 @@ +Puppet::Type.type(:glance_cache_config).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def self.file_path + '/etc/glance/glance-cache.conf' + end + + # added for backwards compatibility with older versions of inifile + def file_path + self.class.file_path + end + +end diff --git a/glance/lib/puppet/provider/glance_image/glance.rb b/glance/lib/puppet/provider/glance_image/glance.rb new file mode 100644 index 000000000..f5b6ddfbe --- /dev/null +++ b/glance/lib/puppet/provider/glance_image/glance.rb @@ -0,0 +1,106 @@ +# Load the Glance provider library to help +require File.join(File.dirname(__FILE__), '..','..','..', 'puppet/provider/glance') + +Puppet::Type.type(:glance_image).provide( + :glance, + :parent => Puppet::Provider::Glance +) do + desc <<-EOT + Glance provider to manage glance_image type. + + Assumes that the glance-api service is on the same host and is working. + EOT + + commands :glance => 'glance' + + mk_resource_methods + + def self.instances + list_glance_images.collect do |image| + attrs = get_glance_image_attrs(image) + new( + :ensure => :present, + :name => attrs['name'], + :is_public => attrs['public'], + :container_format => attrs['container format'], + :id => attrs['id'], + :disk_format => attrs['disk format'] + ) + end + end + + def self.prefetch(resources) + images = instances + resources.keys.each do |name| + if provider = images.find{ |pkg| pkg.name == name } + resources[name].provider = provider + end + end + end + + def exists? + @property_hash[:ensure] == :present + end + + def create + stdin = nil + if resource[:source] + # copy_from cannot handle file:// + if resource[:source] =~ /^\// # local file + location = "< #{resource[:source]}" + stdin = true + else + location = "copy_from=#{resource[:source]}" + end + # location cannot handle file:// + # location does not import, so no sense in doing anything more than this + elsif resource[:location] + location = "location=#{resource[:location]}" + else + raise(Puppet::Error, "Must specify either source or location") + end + if stdin + result = auth_glance_stdin('add', "name=#{resource[:name]}", "is_public=#{resource[:is_public]}", "container_format=#{resource[:container_format]}", "disk_format=#{resource[:disk_format]}", location) + else + results = auth_glance('add', "name=#{resource[:name]}", "is_public=#{resource[:is_public]}", "container_format=#{resource[:container_format]}", "disk_format=#{resource[:disk_format]}", location) + end + if results =~ /Added new image with ID: (\S+)/ + @property_hash = { + :ensure => :present, + :name => resource[:name], + :is_public => resource[:is_public], + :container_format => resource[:container_format], + :disk_format => resource[:disk_format], + :id => $1 + } + else + fail("did not get expected message from image creation, got #{results}") + end + end + + def destroy + auth_glance('delete', id) + @property_hash[:ensure] = :absent + end + + def location=(value) + auth_glance('update', id, "location=#{value}") + end + + def is_public=(value) + auth_glance('update', id, "is_public=#{value}") + end + + def disk_format=(value) + auth_glance('update', id, "disk_format=#{value}") + end + + def container_format=(value) + auth_glance('update', id, "container_format=#{value}") + end + + def id=(id) + fail('id is read only') + end + +end diff --git a/glance/lib/puppet/provider/glance_registry_config/ini_setting.rb b/glance/lib/puppet/provider/glance_registry_config/ini_setting.rb new file mode 100644 index 000000000..5f843b5e9 --- /dev/null +++ b/glance/lib/puppet/provider/glance_registry_config/ini_setting.rb @@ -0,0 +1,27 @@ +Puppet::Type.type(:glance_registry_config).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def self.file_path + '/etc/glance/glance-registry.conf' + end + + # added for backwards compatibility with older versions of inifile + def file_path + self.class.file_path + end + +end diff --git a/glance/lib/puppet/provider/glance_registry_paste_ini/ini_setting.rb b/glance/lib/puppet/provider/glance_registry_paste_ini/ini_setting.rb new file mode 100644 index 000000000..36cb7e12c --- /dev/null +++ b/glance/lib/puppet/provider/glance_registry_paste_ini/ini_setting.rb @@ -0,0 +1,27 @@ +Puppet::Type.type(:glance_registry_paste_ini).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def self.file_path + '/etc/glance/glance-registry-paste.ini' + end + + # added for backwards compatibility with older versions of inifile + def file_path + self.class.file_path + end + +end diff --git a/glance/lib/puppet/type/glance_api_config.rb b/glance/lib/puppet/type/glance_api_config.rb new file mode 100644 index 000000000..d7bd10f92 --- /dev/null +++ b/glance/lib/puppet/type/glance_api_config.rb @@ -0,0 +1,19 @@ +Puppet::Type.newtype(:glance_api_config) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from glance-api.conf' + newvalues(/\S+\/\S+/) + end + + newproperty(:value) do + desc 'The value of the setting to be defined.' + munge do |value| + value = value.to_s.strip + value.capitalize! if value =~ /^(true|false)$/i + value + end + end + +end diff --git a/glance/lib/puppet/type/glance_api_paste_ini.rb b/glance/lib/puppet/type/glance_api_paste_ini.rb new file mode 100644 index 000000000..740f2bbc8 --- /dev/null +++ b/glance/lib/puppet/type/glance_api_paste_ini.rb @@ -0,0 +1,19 @@ +Puppet::Type.newtype(:glance_api_paste_ini) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from glance-api-paste.ini' + newvalues(/\S+\/\S+/) + end + + newproperty(:value) do + desc 'The value of the setting to be defined.' + munge do |value| + value = value.to_s.strip + value.capitalize! if value =~ /^(true|false)$/i + value + end + end + +end diff --git a/glance/lib/puppet/type/glance_cache_config.rb b/glance/lib/puppet/type/glance_cache_config.rb new file mode 100644 index 000000000..ae0254e19 --- /dev/null +++ b/glance/lib/puppet/type/glance_cache_config.rb @@ -0,0 +1,19 @@ +Puppet::Type.newtype(:glance_cache_config) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from glance-cache.conf' + newvalues(/\S+\/\S+/) + end + + newproperty(:value) do + desc 'The value of the setting to be defined.' + munge do |value| + value = value.to_s.strip + value.capitalize! if value =~ /^(true|false)$/i + value + end + end + +end diff --git a/glance/lib/puppet/type/glance_image.rb b/glance/lib/puppet/type/glance_image.rb new file mode 100644 index 000000000..d63590d9c --- /dev/null +++ b/glance/lib/puppet/type/glance_image.rb @@ -0,0 +1,75 @@ +Puppet::Type.newtype(:glance_image) do + desc <<-EOT + This allows manifests to declare an image to be + stored in glance. + + glance_image { "Ubuntu 12.04 cloudimg amd64": + ensure => present, + name => "Ubuntu 12.04 cloudimg amd64" + is_public => yes, + container_format => ovf, + disk_format => 'qcow2', + source => 'http://uec-images.ubuntu.com/releases/precise/release/ubuntu-12.04-server-cloudimg-amd64-disk1.img' + } + + Known problems / limitations: + * All images are managed by the glance service. + This means that since users are unable to manage their own images via this type, + is_public is really of no use. You can probably hide images this way but that's all. + * As glance image names do not have to be unique, you must ensure that your glance + repository does not have any duplicate names prior to using this. + * Ensure this is run on the same server as the glance-api service. + + EOT + + ensurable + + newparam(:name, :namevar => true) do + desc 'The image name' + newvalues(/.*/) + end + + newproperty(:id) do + desc 'The unique id of the image' + validate do |v| + raise(Puppet::Error, 'This is a read only property') + end + end + + newproperty(:location) do + desc "The permanent location of the image. Optional" + newvalues(/\S+/) + end + + newproperty(:is_public) do + desc "Whether the image is public or not. Default true" + newvalues(/(y|Y)es/, /(n|N)o/) + defaultto('Yes') + munge do |v| + v.to_s.capitalize + end + end + + newproperty(:container_format) do + desc "The format of the container" + newvalues(:ami, :ari, :aki, :bare, :ovf) + end + + newproperty(:disk_format) do + desc "The format of the disk" + newvalues(:ami, :ari, :aki, :vhd, :vmd, :raw, :qcow2, :vdi, :iso) + end + + newparam(:source) do + desc "The source of the image to import from" + newvalues(/\S+/) + end + + # Require the Glance service to be running + autorequire(:service) do + ['glance'] + end + +end + + diff --git a/glance/lib/puppet/type/glance_registry_config.rb b/glance/lib/puppet/type/glance_registry_config.rb new file mode 100644 index 000000000..3b08e6a60 --- /dev/null +++ b/glance/lib/puppet/type/glance_registry_config.rb @@ -0,0 +1,19 @@ +Puppet::Type.newtype(:glance_registry_config) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from glance-registry.conf' + newvalues(/\S+\/\S+/) + end + + newproperty(:value) do + desc 'The value of the setting to be defined.' + munge do |value| + value = value.to_s.strip + value.capitalize! if value =~ /^(true|false)$/i + value + end + end + +end diff --git a/glance/lib/puppet/type/glance_registry_paste_ini.rb b/glance/lib/puppet/type/glance_registry_paste_ini.rb new file mode 100644 index 000000000..8c9b956bc --- /dev/null +++ b/glance/lib/puppet/type/glance_registry_paste_ini.rb @@ -0,0 +1,19 @@ +Puppet::Type.newtype(:glance_registry_paste_ini) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from glance-registry-paste.ini' + newvalues(/\S+\/\S+/) + end + + newproperty(:value) do + desc 'The value of the setting to be defined.' + munge do |value| + value = value.to_s.strip + value.capitalize! if value =~ /^(true|false)$/i + value + end + end + +end diff --git a/glance/manifests/api.pp b/glance/manifests/api.pp new file mode 100644 index 000000000..1bfe8bc57 --- /dev/null +++ b/glance/manifests/api.pp @@ -0,0 +1,370 @@ +# == Class glance::api +# +# Configure API service in glance +# +# == Parameters +# +# [*keystone_password*] +# (required) Password used to authentication. +# +# [*verbose*] +# (optional) Rather to log the glance api service at verbose level. +# Default: false +# +# [*debug*] +# (optional) Rather to log the glance api service at debug level. +# Default: false +# +# [*bind_host*] +# (optional) The address of the host to bind to. +# Default: 0.0.0.0 +# +# [*bind_port*] +# (optional) The port the server should bind to. +# Default: 9292 +# +# [*backlog*] +# (optional) Backlog requests when creating socket +# Default: 4096 +# +# [*workers*] +# (optional) Number of Glance API worker processes to start +# Default: $::processorcount +# +# [*log_file*] +# (optional) The path of file used for logging +# If set to boolean false, it will not log to any file. +# Default: /var/log/glance/api.log +# +# [*log_dir*] +# (optional) directory to which glance logs are sent. +# If set to boolean false, it will not log to any directory. +# Defaults to '/var/log/glance' +# +# [*registry_host*] +# (optional) The address used to connect to the registry service. +# Default: 0.0.0.0 +# +# [*registry_port*] +# (optional) The port of the Glance registry service. +# Default: 9191 +# +# [*auth_type*] +# (optional) Type is authorization being used. +# Defaults to 'keystone' +# +# [* auth_host*] +# (optional) Host running auth service. +# Defaults to '127.0.0.1'. +# +# [*auth_url*] +# (optional) Authentication URL. +# Defaults to 'http://localhost:5000/v2.0'. +# +# [* auth_port*] +# (optional) Port to use for auth service on auth_host. +# Defaults to '35357'. +# +# [* auth_uri*] +# (optional) Complete public Identity API endpoint. +# Defaults to false. +# +# [*auth_admin_prefix*] +# (optional) Path part of the auth url. +# This allow admin auth URIs like http://auth_host:35357/keystone/admin. +# (where '/keystone/admin' is auth_admin_prefix) +# Defaults to false for empty. If defined, should be a string with a leading '/' and no trailing '/'. +# +# [* auth_protocol*] +# (optional) Protocol to use for auth. +# Defaults to 'http'. +# +# [*pipeline*] +# (optional) Partial name of a pipeline in your paste configuration file with the +# service name removed. +# Defaults to 'keystone+cachemanagement'. +# +# [*keystone_tenant*] +# (optional) Tenant to authenticate to. +# Defaults to services. +# +# [*keystone_user*] +# (optional) User to authenticate as with keystone. +# Defaults to 'glance'. +# +# [*enabled*] +# (optional) Whether to enable services. +# Defaults to true. +# +# [*sql_idle_timeout*] +# (optional) Period in seconds after which SQLAlchemy should reestablish its connection +# to the database. +# Defaults to '3600'. +# +# [*sql_connection*] +# (optional) Database connection. +# Defaults to 'sqlite:///var/lib/glance/glance.sqlite'. +# +# [*use_syslog*] +# (optional) Use syslog for logging. +# Defaults to false. +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines. +# Defaults to 'LOG_USER'. +# +# [*show_image_direct_url*] +# (optional) Expose image location to trusted clients. +# Defaults to false. +# +# [*cert_file*] +# (optinal) Certificate file to use when starting API server securely +# Defaults to false, not set +# +# [*key_file*] +# (optional) Private key file to use when starting API server securely +# Defaults to false, not set +# +# [*ca_file*] +# (optional) CA certificate file to use to verify connecting clients +# Defaults to false, not set +# +class glance::api( + $keystone_password, + $verbose = false, + $debug = false, + $bind_host = '0.0.0.0', + $bind_port = '9292', + $backlog = '4096', + $workers = $::processorcount, + $log_file = '/var/log/glance/api.log', + $log_dir = '/var/log/glance', + $registry_host = '0.0.0.0', + $registry_port = '9191', + $auth_type = 'keystone', + $auth_host = '127.0.0.1', + $auth_url = 'http://localhost:5000/v2.0', + $auth_port = '35357', + $auth_uri = false, + $auth_admin_prefix = false, + $auth_protocol = 'http', + $pipeline = 'keystone+cachemanagement', + $keystone_tenant = 'services', + $keystone_user = 'glance', + $enabled = true, + $sql_idle_timeout = '3600', + $sql_connection = 'sqlite:///var/lib/glance/glance.sqlite', + $use_syslog = false, + $log_facility = 'LOG_USER', + $show_image_direct_url = false, + $cert_file = false, + $key_file = false, + $ca_file = false, + $notifier_strategy = 'noop', +) inherits glance { + + require keystone::python + + validate_re($sql_connection, '(sqlite|mysql|postgresql):\/\/(\S+:\S+@\S+\/\S+)?') + + Package['glance'] -> Glance_api_config<||> + Package['glance'] -> Glance_cache_config<||> + # adding all of this stuff b/c it devstack says glance-api uses the + # db now + Glance_api_config<||> ~> Exec<| title == 'glance-manage db_sync' |> + Glance_cache_config<||> ~> Exec<| title == 'glance-manage db_sync' |> + Exec<| title == 'glance-manage db_sync' |> ~> Service['glance-api'] + Glance_api_config<||> ~> Service['glance-api'] + Glance_cache_config<||> ~> Service['glance-api'] + + File { + ensure => present, + owner => 'glance', + group => 'glance', + mode => '0640', + notify => Service['glance-api'], + require => Class['glance'], + } + + @glance_api_config { + 'DEFAULT/notifier_strategy': value => $notifier_strategy; + } + + if($sql_connection =~ /mysql:\/\/\S+:\S+@\S+\/\S+/) { + require 'mysql::python' + } elsif($sql_connection =~ /postgresql:\/\/\S+:\S+@\S+\/\S+/) { + + } elsif($sql_connection =~ /sqlite:\/\//) { + + } else { + fail("Invalid db connection ${sql_connection}") + } + + # basic service config + glance_api_config { + 'DEFAULT/verbose': value => $verbose; + 'DEFAULT/debug': value => $debug; + 'DEFAULT/bind_host': value => $bind_host; + 'DEFAULT/bind_port': value => $bind_port; + 'DEFAULT/backlog': value => $backlog; + 'DEFAULT/workers': value => $workers; + 'DEFAULT/show_image_direct_url': value => $show_image_direct_url; + } + + glance_cache_config { + 'DEFAULT/verbose': value => $verbose; + 'DEFAULT/debug': value => $debug; + } + + # configure api service to connect registry service + glance_api_config { + 'DEFAULT/registry_host': value => $registry_host; + 'DEFAULT/registry_port': value => $registry_port; + } + + glance_cache_config { + 'DEFAULT/registry_host': value => $registry_host; + 'DEFAULT/registry_port': value => $registry_port; + } + + # db connection config + # I do not believe this was required in Essex. + # Does the API server now need to connect to the DB? + # TODO figure out if I need this... + glance_api_config { + 'DEFAULT/sql_connection': value => $sql_connection; + 'DEFAULT/sql_idle_timeout': value => $sql_idle_timeout; + } + + if $auth_uri { + glance_api_config { 'keystone_authtoken/auth_uri': value => $auth_uri; } + } else { + glance_api_config { 'keystone_authtoken/auth_uri': value => "${auth_protocol}://${auth_host}:5000/"; } + } + + # auth config + glance_api_config { + 'keystone_authtoken/auth_host': value => $auth_host; + 'keystone_authtoken/auth_port': value => $auth_port; + 'keystone_authtoken/auth_protocol': value => $auth_protocol; + } + + if $auth_admin_prefix { + validate_re($auth_admin_prefix, '^(/.+[^/])?$') + glance_api_config { + 'keystone_authtoken/auth_admin_prefix': value => $auth_admin_prefix; + } + } else { + glance_api_config { + 'keystone_authtoken/auth_admin_prefix': ensure => absent; + } + } + + # Set the pipeline, it is allowed to be blank + if $pipeline != '' { + validate_re($pipeline, '^(\w+([+]\w+)*)*$') + glance_api_config { + 'paste_deploy/flavor': + ensure => present, + value => $pipeline, + } + } else { + glance_api_config { 'paste_deploy/flavor': ensure => absent } + } + + # keystone config + if $auth_type == 'keystone' { + glance_api_config { + 'keystone_authtoken/admin_tenant_name': value => $keystone_tenant; + 'keystone_authtoken/admin_user' : value => $keystone_user; + 'keystone_authtoken/admin_password' : value => $keystone_password; + } + glance_cache_config { + 'DEFAULT/auth_url' : value => $auth_url; + 'DEFAULT/admin_tenant_name': value => $keystone_tenant; + 'DEFAULT/admin_user' : value => $keystone_user; + 'DEFAULT/admin_password' : value => $keystone_password; + } + } + + # SSL Options + if $cert_file { + glance_api_config { + 'DEFAULT/cert_file' : value => $cert_file; + } + } else { + glance_api_config { + 'DEFAULT/cert_file': ensure => absent; + } + } + if $key_file { + glance_api_config { + 'DEFAULT/key_file' : value => $key_file; + } + } else { + glance_api_config { + 'DEFAULT/key_file': ensure => absent; + } + } + if $ca_file { + glance_api_config { + 'DEFAULT/ca_file' : value => $ca_file; + } + } else { + glance_api_config { + 'DEFAULT/ca_file': ensure => absent; + } + } + + # Logging + if $log_file { + glance_api_config { + 'DEFAULT/log_file': value => $log_file; + } + } else { + glance_api_config { + 'DEFAULT/log_file': ensure => absent; + } + } + + if $log_dir { + glance_api_config { + 'DEFAULT/log_dir': value => $log_dir; + } + } else { + glance_api_config { + 'DEFAULT/log_dir': ensure => absent; + } + } + + # Syslog + if $use_syslog { + glance_api_config { + 'DEFAULT/use_syslog' : value => true; + 'DEFAULT/syslog_log_facility' : value => $log_facility; + } + } else { + glance_api_config { + 'DEFAULT/use_syslog': value => false; + } + } + + file { ['/etc/glance/glance-api.conf', + '/etc/glance/glance-api-paste.ini', + '/etc/glance/glance-cache.conf']: + } + + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + service { 'glance-api': + ensure => $service_ensure, + name => $::glance::params::api_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + } +} diff --git a/glance/manifests/backend/cinder.pp b/glance/manifests/backend/cinder.pp new file mode 100644 index 000000000..db9593338 --- /dev/null +++ b/glance/manifests/backend/cinder.pp @@ -0,0 +1,97 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# == Class: glance::backend::cinder +# +# Setup Glance to backend images into Cinder +# +# === Parameters +# +# [*cinder_catalog_info*] +# (optional) Info to match when looking for cinder in the service catalog. +# Format is : separated values of the form: +# :: (string value) +# Defaults to 'volume:cinder:publicURL' +# +# [*cinder_endpoint_template*] +# (optional) Override service catalog lookup with template for cinder endpoint. +# Should be a valid URL. Example: 'http://localhost:8776/v1/%(project_id)s' +# Defaults to 'undef' +# +# [*os_region_name*] +# (optional) Region name of this node. +# Should be a valid region name +# Defaults to 'RegionOne' +# +# [*cinder_ca_certificates_file*] +# (optional) Location of ca certicate file to use for cinder client requests. +# Should be a valid ca certicate file +# Defaults to undef +# +# [*cinder_http_retries*] +# (optional) Number of cinderclient retries on failed http calls. +# Should be a valid integer +# Defaults to '3' +# +# [*cinder_api_insecure*] +# (optional) Allow to perform insecure SSL requests to cinder. +# Should be a valid boolean value +# Defaults to false +# + +class glance::backend::cinder( + $os_region_name = 'RegionOne', + $cinder_ca_certificates_file = undef, + $cinder_api_insecure = false, + $cinder_catalog_info = 'volume:cinder:publicURL', + $cinder_endpoint_template = undef, + $cinder_http_retries = '3' + +) { + + glance_api_config { + 'DEFAULT/cinder_api_insecure': value => $cinder_api_insecure; + 'DEFAULT/cinder_catalog_info': value => $cinder_catalog_info; + 'DEFAULT/cinder_http_retries': value => $cinder_http_retries; + 'DEFAULT/default_store': value => 'cinder'; + 'DEFAULT/os_region_name': value => $os_region_name; + } + + glance_cache_config { + 'DEFAULT/cinder_api_insecure': value => $cinder_api_insecure; + 'DEFAULT/cinder_catalog_info': value => $cinder_catalog_info; + 'DEFAULT/cinder_http_retries': value => $cinder_http_retries; + 'DEFAULT/os_region_name': value => $os_region_name; + } + + if $cinder_endpoint_template { + glance_api_config { 'DEFAULT/cinder_endpoint_template': value => $cinder_endpoint_template; } + glance_cache_config { 'DEFAULT/cinder_endpoint_template': value => $cinder_endpoint_template; } + } else { + glance_api_config { 'DEFAULT/cinder_endpoint_template': ensure => absent; } + glance_cache_config { 'DEFAULT/cinder_endpoint_template': ensure => absent; } + } + + if $cinder_ca_certificates_file { + glance_api_config { 'DEFAULT/cinder_ca_certificates_file': value => $cinder_ca_certificates_file; } + glance_cache_config { 'DEFAULT/cinder_ca_certificates_file': value => $cinder_ca_certificates_file; } + } else { + glance_api_config { 'DEFAULT/cinder_ca_certificates_file': ensure => absent; } + glance_cache_config { 'DEFAULT/cinder_ca_certificates_file': ensure => absent; } + } + +} diff --git a/glance/manifests/backend/file.pp b/glance/manifests/backend/file.pp new file mode 100644 index 000000000..9c27719ba --- /dev/null +++ b/glance/manifests/backend/file.pp @@ -0,0 +1,19 @@ +# +# used to configure file backends for glance +# +# $filesystem_store_datadir - Location where dist images are stored when +# default_store == file. +# Optional. Default: /var/lib/glance/images/ +class glance::backend::file( + $filesystem_store_datadir = '/var/lib/glance/images/' +) inherits glance::api { + + glance_api_config { + 'DEFAULT/default_store': value => 'file'; + 'DEFAULT/filesystem_store_datadir': value => $filesystem_store_datadir; + } + + glance_cache_config { + 'DEFAULT/filesystem_store_datadir': value => $filesystem_store_datadir; + } +} diff --git a/glance/manifests/backend/rbd.pp b/glance/manifests/backend/rbd.pp new file mode 100644 index 000000000..6dad65250 --- /dev/null +++ b/glance/manifests/backend/rbd.pp @@ -0,0 +1,34 @@ +# +# configures the storage backend for glance +# as a rbd instance +# +# $rbd_store_user - Optional. +# +# $rbd_store_pool - Optional. Default:'images' +# +# $rbd_store_ceph_conf - Optional. Default:'/etc/ceph/ceph.conf' +# +# $rbd_store_chunk_size - Optional. Default:'8' + +class glance::backend::rbd( + $rbd_store_user = undef, + $rbd_store_ceph_conf = '/etc/ceph/ceph.conf', + $rbd_store_pool = 'images', + $rbd_store_chunk_size = '8', +) { + include glance::params + + glance_api_config { + 'DEFAULT/default_store': value => 'rbd'; + 'DEFAULT/rbd_store_ceph_conf': value => $rbd_store_ceph_conf; + 'DEFAULT/rbd_store_user': value => $rbd_store_user; + 'DEFAULT/rbd_store_pool': value => $rbd_store_pool; + 'DEFAULT/rbd_store_chunk_size': value => $rbd_store_chunk_size; + } + + package { 'python-ceph': + ensure => 'present', + name => $::glance::params::pyceph_package_name, + } + +} diff --git a/glance/manifests/backend/swift.pp b/glance/manifests/backend/swift.pp new file mode 100644 index 000000000..158940a49 --- /dev/null +++ b/glance/manifests/backend/swift.pp @@ -0,0 +1,44 @@ +# +# configures the storage backend for glance +# as a swift instance +# +# $swift_store_auth_address - Optional. Default: '127.0.0.1:8080/v1.0/', +# +# $swift_store_user - Optional. Default:'jdoe', +# +# $swift_store_key - Optional. Default: 'a86850deb2742ec3cb41518e26aa2d89', +# +# $swift_store_container - 'glance', +# +# $swift_store_create_container_on_put - 'False' +class glance::backend::swift( + $swift_store_user, + $swift_store_key, + $swift_store_auth_address = '127.0.0.1:5000/v2.0/', + $swift_store_container = 'glance', + $swift_store_auth_version = '2', + $swift_store_create_container_on_put = false +) { + + glance_api_config { + 'DEFAULT/default_store': value => 'swift'; + 'DEFAULT/swift_store_user': value => $swift_store_user; + 'DEFAULT/swift_store_key': value => $swift_store_key; + 'DEFAULT/swift_store_auth_address': value => $swift_store_auth_address; + 'DEFAULT/swift_store_container': value => $swift_store_container; + 'DEFAULT/swift_store_auth_version': value => $swift_store_auth_version; + 'DEFAULT/swift_store_create_container_on_put': + value => $swift_store_create_container_on_put; + } + + glance_cache_config { + 'DEFAULT/swift_store_user': value => $swift_store_user; + 'DEFAULT/swift_store_key': value => $swift_store_key; + 'DEFAULT/swift_store_auth_address': value => $swift_store_auth_address; + 'DEFAULT/swift_store_container': value => $swift_store_container; + 'DEFAULT/swift_store_auth_version': value => $swift_store_auth_version; + 'DEFAULT/swift_store_create_container_on_put': + value => $swift_store_create_container_on_put; + } + +} diff --git a/glance/manifests/cache/cleaner.pp b/glance/manifests/cache/cleaner.pp new file mode 100644 index 000000000..181c627e6 --- /dev/null +++ b/glance/manifests/cache/cleaner.pp @@ -0,0 +1,42 @@ +# == Class: glance::cache::cleaner +# +# Installs a cron job to run glance-cache-cleaner. +# +# === Parameters +# +# [*minute*] +# (optional) Defaults to '1'. +# +# [*hour*] +# (optional) Defaults to '0'. +# +# [*monthday*] +# (optional) Defaults to '*'. +# +# [*month*] +# (optional) Defaults to '*'. +# +# [*weekday*] +# (optional) Defaults to '*'. +# +class glance::cache::cleaner ( + $minute = 1, + $hour = 0, + $monthday = '*', + $month = '*', + $weekday = '*', +) { + + include glance::params + + cron { 'glance-cache-cleaner': + command => $glance::params::cache_cleaner_command, + environment => 'PATH=/bin:/usr/bin:/usr/sbin', + user => 'glance', + minute => $minute, + hour => $hour, + monthday => $monthday, + month => $month, + weekday => $weekday + } +} diff --git a/glance/manifests/cache/pruner.pp b/glance/manifests/cache/pruner.pp new file mode 100644 index 000000000..9c3811a91 --- /dev/null +++ b/glance/manifests/cache/pruner.pp @@ -0,0 +1,42 @@ +# == Class: glance::cache::pruner +# +# Installs a cron job to run glance-cache-pruner. +# +# === Parameters +# +# [*minute*] +# (optional) Defaults to '*/30'. +# +# [*hour*] +# (optional) Defaults to '*'. +# +# [*monthday*] +# (optional) Defaults to '*'. +# +# [*month*] +# (optional) Defaults to '*'. +# +# [*weekday*] +# (optional) Defaults to '*'. +# +class glance::cache::pruner ( + $minute = '*/30', + $hour = '*', + $monthday = '*', + $month = '*', + $weekday = '*', +) { + + include glance::params + + cron { 'glance-cache-pruner': + command => $glance::params::cache_pruner_command, + environment => 'PATH=/bin:/usr/bin:/usr/sbin', + user => 'glance', + minute => $minute, + hour => $hour, + monthday => $monthday, + month => $month, + weekday => $weekday + } +} diff --git a/glance/manifests/client.pp b/glance/manifests/client.pp new file mode 100644 index 000000000..cbb0f1c0c --- /dev/null +++ b/glance/manifests/client.pp @@ -0,0 +1,18 @@ +# +# Installs the glance python library. +# +# == parameters +# * ensure - ensure state for pachage. +# +class glance::client ( + $ensure = 'present' +) { + + include glance::params + + package { 'python-glanceclient': + ensure => $ensure, + name => $::glance::params::client_package_name, + } + +} diff --git a/glance/manifests/config.pp b/glance/manifests/config.pp new file mode 100644 index 000000000..d9c6a4371 --- /dev/null +++ b/glance/manifests/config.pp @@ -0,0 +1,56 @@ +# == Class: glance::config +# +# This class is used to manage arbitrary glance configurations. +# +# === Parameters +# +# [*xxx_config*] +# (optional) Allow configuration of arbitrary glance configurations. +# The value is an hash of glance_config resources. Example: +# { 'DEFAULT/foo' => { value => 'fooValue'}, +# 'DEFAULT/bar' => { value => 'barValue'} +# } +# In yaml format, Example: +# glance_config: +# DEFAULT/foo: +# value: fooValue +# DEFAULT/bar: +# value: barValue +# +# [**api_config**] +# (optional) Allow configuration of glance-api.conf configurations. +# +# [**api_paste_ini_config**] +# (optional) Allow configuration of glance-api-paste.ini configurations. +# +# [**registry_config**] +# (optional) Allow configuration of glance-registry.conf configurations. +# +# [**registry_paste_ini_config**] +# (optional) Allow configuration of glance-registry-paste.ini configurations. +# +# [**cache_config**] +# (optional) Allow configuration of glance-cache.conf configurations. +# +# NOTE: The configuration MUST NOT be already handled by this module +# or Puppet catalog compilation will fail with duplicate resources. +# +class glance::config ( + $api_config = {}, + $api_paste_ini_config = {}, + $registry_config = {}, + $registry_paste_ini_config = {}, + $cache_config = {}, +) { + validate_hash($api_config) + validate_hash($api_paste_ini_config) + validate_hash($registry_config) + validate_hash($registry_paste_ini_config) + validate_hash($cache_config) + + create_resources('glance_api_config', $api_config) + create_resources('glance_api_paste_ini', $api_paste_ini_config) + create_resources('glance_registry_config', $registry_config) + create_resources('glance_registry_paste_ini', $registry_paste_ini_config) + create_resources('glance_cache_config', $cache_config) +} diff --git a/glance/manifests/db/mysql.pp b/glance/manifests/db/mysql.pp new file mode 100644 index 000000000..c190912e7 --- /dev/null +++ b/glance/manifests/db/mysql.pp @@ -0,0 +1,45 @@ +# +# I should change this to mysql +# for consistency +# +class glance::db::mysql( + $password, + $dbname = 'glance', + $user = 'glance', + $host = '127.0.0.1', + $allowed_hosts = undef, + $charset = 'latin1', + $cluster_id = 'localzone' +) { + + Class['glance::db::mysql'] -> Exec<| title == 'glance-manage db_sync' |> + Database[$dbname] ~> Exec<| title == 'glance-manage db_sync' |> + + require mysql::python + + mysql::db { $dbname: + user => $user, + password => $password, + host => $host, + charset => $charset, + require => Class['mysql::config'], + } + + # Check allowed_hosts to avoid duplicate resource declarations + # If $host in $allowed_hosts, then remove it + if is_array($allowed_hosts) and delete($allowed_hosts,$host) != [] { + $real_allowed_hosts = delete($allowed_hosts,$host) + # If $host = $allowed_hosts, then set it to undef + } elsif is_string($allowed_hosts) and ($allowed_hosts != $host) { + $real_allowed_hosts = $allowed_hosts + } + + if $real_allowed_hosts { + # TODO this class should be in the mysql namespace + glance::db::mysql::host_access { $real_allowed_hosts: + user => $user, + password => $password, + database => $dbname, + } + } +} diff --git a/glance/manifests/db/mysql/host_access.pp b/glance/manifests/db/mysql/host_access.pp new file mode 100644 index 000000000..6c18a0414 --- /dev/null +++ b/glance/manifests/db/mysql/host_access.pp @@ -0,0 +1,16 @@ +# +# Used to grant access to the glance mysql DB +# +define glance::db::mysql::host_access ($user, $password, $database) { + database_user { "${user}@${name}": + password_hash => mysql_password($password), + provider => 'mysql', + require => Database[$database], + } + database_grant { "${user}@${name}/${database}": + # TODO figure out which privileges to grant. + privileges => 'all', + provider => 'mysql', + require => Database_user["${user}@${name}"] + } +} diff --git a/glance/manifests/db/postgresql.pp b/glance/manifests/db/postgresql.pp new file mode 100644 index 000000000..a1eb54c63 --- /dev/null +++ b/glance/manifests/db/postgresql.pp @@ -0,0 +1,21 @@ +# +# Class that configures postgresql for glance +# +# Requires the Puppetlabs postgresql module. +class glance::db::postgresql( + $password, + $dbname = 'glance', + $user = 'glance' +) { + + require postgresql::python + + Postgresql::Db[$dbname] ~> Exec<| title == 'glance-manage db_sync' |> + Package['python-psycopg2'] -> Exec<| title == 'glance-manage db_sync' |> + + postgresql::db { $dbname: + user => $user, + password => $password, + } + +} diff --git a/glance/manifests/init.pp b/glance/manifests/init.pp new file mode 100644 index 000000000..1bbdba2f3 --- /dev/null +++ b/glance/manifests/init.pp @@ -0,0 +1,24 @@ +# +# base glacne config. +# +# == parameters +# * package_ensure - ensure state for package. +# +class glance( + $package_ensure = 'present' +) { + + include glance::params + + file { '/etc/glance/': + ensure => directory, + owner => 'glance', + group => 'root', + mode => '0770', + require => Package['glance'] + } + package { 'glance': + ensure => $package_ensure, + name => $::glance::params::package_name, + } +} diff --git a/glance/manifests/keystone/auth.pp b/glance/manifests/keystone/auth.pp new file mode 100644 index 000000000..cdd6e8628 --- /dev/null +++ b/glance/manifests/keystone/auth.pp @@ -0,0 +1,67 @@ +# +# Sets up glance users, service and endpoint +# +# == Parameters: +# +# $auth_name :: identifier used for all keystone objects related to glance. +# Optional. Defaults to glance. +# $password :: password for glance user. Optional. Defaults to glance_password. +# $service_type :: type of service to create. Optional. Defaults to image. +# $public_address :: Public address for endpoint. Optional. Defaults to 127.0.0.1. +# $admin_address :: Admin address for endpoint. Optional. Defaults to 127.0.0.1. +# $inernal_address :: Internal address for endpoint. Optional. Defaults to 127.0.0.1. +# $port :: Port for endpoint. Needs to match glance api service port. Optional. +# Defaults to 9292. +# $region :: Region where endpoint is set. +# $public_protocol :: Protocol for public endpoint. Optional. Defaults to http. +# $admin_protocol :: Protocol for admin endpoint. Optional. Defaults to http. +# $internal_protocol :: Protocol for internal endpoint. Optional. Defaults to http. +# +class glance::keystone::auth( + $password, + $email = 'glance@localhost', + $auth_name = 'glance', + $configure_endpoint = true, + $service_type = 'image', + $public_address = '127.0.0.1', + $admin_address = '127.0.0.1', + $internal_address = '127.0.0.1', + $port = '9292', + $region = 'RegionOne', + $tenant = 'services', + $public_protocol = 'http', + $admin_protocol = 'http', + $internal_protocol = 'http' +) { + + Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'glance-registry' |> + Keystone_user_role["${auth_name}@${tenant}"] ~> Service <| name == 'glance-api' |> + Keystone_endpoint["${region}/${auth_name}"] ~> Service <| name == 'glance-api' |> + + keystone_user { $auth_name: + ensure => present, + password => $password, + email => $email, + tenant => $tenant, + } + + keystone_user_role { "${auth_name}@${tenant}": + ensure => present, + roles => 'admin', + } + + keystone_service { $auth_name: + ensure => present, + type => $service_type, + description => 'Openstack Image Service', + } + + if $configure_endpoint { + keystone_endpoint { "${region}/${auth_name}": + ensure => present, + public_url => "${public_protocol}://${public_address}:${port}", + admin_url => "${admin_protocol}://${admin_address}:${port}", + internal_url => "${internal_protocol}://${internal_address}:${port}", + } + } +} diff --git a/glance/manifests/notify/qpid.pp b/glance/manifests/notify/qpid.pp new file mode 100644 index 000000000..f8a29734a --- /dev/null +++ b/glance/manifests/notify/qpid.pp @@ -0,0 +1,22 @@ +# +# used to configure qpid notifications for glance +# +class glance::notify::qpid( + $qpid_password, + $qpid_username = 'guest', + $qpid_hostname = 'localhost', + $qpid_port = '5672', +) inherits glance::api { + + Glance_api_config <| title == 'DEFAULT/notifier_strategy' |> { + value => 'qpid' + } + + glance_api_config { + 'DEFAULT/qpid_hostname': value => $qpid_hostname; + 'DEFAULT/qpid_port': value => $qpid_port; + 'DEFAULT/qpid_username': value => $qpid_username; + 'DEFAULT/qpid_password': value => $qpid_password; + } + +} diff --git a/glance/manifests/notify/rabbitmq.pp b/glance/manifests/notify/rabbitmq.pp new file mode 100644 index 000000000..39de193dc --- /dev/null +++ b/glance/manifests/notify/rabbitmq.pp @@ -0,0 +1,31 @@ +# +# used to configure rabbitmq notifications for glance +# +class glance::notify::rabbitmq( + $rabbit_password, + $rabbit_userid = 'guest', + $rabbit_host = 'localhost', + $rabbit_port = '5672', + $rabbit_virtual_host = '/', + $rabbit_use_ssl = false, + $rabbit_notification_exchange = 'glance', + $rabbit_notification_topic = 'notifications', + $rabbit_durable_queues = false +) { + + Glance_api_config <| title == 'DEFAULT/notifier_strategy' |> { + value => 'rabbit' + } + + glance_api_config { + 'DEFAULT/rabbit_host': value => $rabbit_host; + 'DEFAULT/rabbit_port': value => $rabbit_port; + 'DEFAULT/rabbit_virtual_host': value => $rabbit_virtual_host; + 'DEFAULT/rabbit_password': value => $rabbit_password; + 'DEFAULT/rabbit_userid': value => $rabbit_userid; + 'DEFAULT/rabbit_notification_exchange': value => $rabbit_notification_exchange; + 'DEFAULT/rabbit_notification_topic': value => $rabbit_notification_topic; + 'DEFAULT/rabbit_use_ssl': value => $rabbit_use_ssl; + 'DEFAULT/rabbit_durable_queues': value => $rabbit_durable_queues; + } +} diff --git a/glance/manifests/params.pp b/glance/manifests/params.pp new file mode 100644 index 000000000..5ba0e9272 --- /dev/null +++ b/glance/manifests/params.pp @@ -0,0 +1,29 @@ +# these parameters need to be accessed from several locations and +# should be considered to be constant +class glance::params { + + $client_package_name = 'python-glanceclient' + $pyceph_package_name = 'python-ceph' + + $cache_cleaner_command = 'glance-cache-cleaner' + $cache_pruner_command = 'glance-cache-pruner' + + case $::osfamily { + 'RedHat': { + $package_name = 'openstack-glance' + $api_service_name = 'openstack-glance-api' + $registry_service_name = 'openstack-glance-registry' + $db_sync_command = 'glance-manage db_sync' + } + 'Debian': { + $package_name = 'glance' + $api_service_name = 'glance-api' + $registry_service_name = 'glance-registry' + $db_sync_command = 'glance-manage db_sync' + } + default: { + fail("Unsupported osfamily: ${::osfamily} operatingsystem: ${::operatingsystem}, module ${module_name} only support osfamily RedHat and Debian") + } + } + +} diff --git a/glance/manifests/registry.pp b/glance/manifests/registry.pp new file mode 100644 index 000000000..4777a9e3b --- /dev/null +++ b/glance/manifests/registry.pp @@ -0,0 +1,297 @@ +# == Class: glance::registry +# +# Installs and configures glance-registry +# +# === Parameters +# +# [*keystone_password*] +# (required) The keystone password for administrative user +# +# [*verbose*] +# (optional) Enable verbose logs (true|false). Defaults to false. +# +# [*debug*] +# (optional) Enable debug logs (true|false). Defaults to false. +# +# [*bind_host*] +# (optional) The address of the host to bind to. Defaults to '0.0.0.0'. +# +# [*bind_port*] +# (optional) The port the server should bind to. Defaults to '9191'. +# +# [*log_file*] +# (optional) Log file for glance-registry. +# If set to boolean false, it will not log to any file. +# Defaults to '/var/log/glance/registry.log'. +# +# [*log_dir*] +# (optional) directory to which glance logs are sent. +# If set to boolean false, it will not log to any directory. +# Defaults to '/var/log/glance' +# +# [*sql_connection*] +# (optional) SQL connection string. +# Defaults to 'sqlite:///var/lib/glance/glance.sqlite'. +# +# [*sql_idle_timeout*] +# (optional) SQL connections idle timeout. Defaults to '3600'. +# +# [*auth_type*] +# (optional) Authentication type. Defaults to 'keystone'. +# +# [*auth_host*] +# (optional) Address of the admin authentication endpoint. +# Defaults to '127.0.0.1'. +# +# [*auth_port*] +# (optional) Port of the admin authentication endpoint. Defaults to '35357'. +# +# [*auth_admin_prefix*] +# (optional) path part of the auth url. +# This allow admin auth URIs like http://auth_host:35357/keystone/admin. +# (where '/keystone/admin' is auth_admin_prefix) +# Defaults to false for empty. If defined, should be a string with a leading '/' and no trailing '/'. +# +# [*auth_protocol*] +# (optional) Protocol to communicate with the admin authentication endpoint. +# Defaults to 'http'. Should be 'http' or 'https'. +# +# [*auth_uri*] +# (optional) Complete public Identity API endpoint. +# +# [*keystone_tenant*] +# (optional) administrative tenant name to connect to keystone. +# Defaults to 'services'. +# +# [*keystone_user*] +# (optional) administrative user name to connect to keystone. +# Defaults to 'glance'. +# +# [*use_syslog*] +# (optional) Use syslog for logging. +# Defaults to false. +# +# [*log_facility*] +# (optional) Syslog facility to receive log lines. +# Defaults to LOG_USER. +# +# [*enabled*] +# (optional) Should the service be enabled. Defaults to true. +# +# [*cert_file*] +# (optinal) Certificate file to use when starting registry server securely +# Defaults to false, not set +# +# [*key_file*] +# (optional) Private key file to use when starting registry server securely +# Defaults to false, not set +# +# [*ca_file*] +# (optional) CA certificate file to use to verify connecting clients +# Defaults to false, not set +# +class glance::registry( + $keystone_password, + $verbose = false, + $debug = false, + $bind_host = '0.0.0.0', + $bind_port = '9191', + $log_file = '/var/log/glance/registry.log', + $log_dir = '/var/log/glance', + $sql_connection = 'sqlite:///var/lib/glance/glance.sqlite', + $sql_idle_timeout = '3600', + $auth_type = 'keystone', + $auth_host = '127.0.0.1', + $auth_port = '35357', + $auth_admin_prefix = false, + $auth_uri = false, + $auth_protocol = 'http', + $keystone_tenant = 'services', + $keystone_user = 'glance', + $pipeline = 'keystone', + $use_syslog = false, + $log_facility = 'LOG_USER', + $enabled = true, + $cert_file = false, + $key_file = false, + $ca_file = false +) inherits glance { + + require keystone::python + + validate_re($sql_connection, '(sqlite|mysql|postgresql):\/\/(\S+:\S+@\S+\/\S+)?') + + Package['glance'] -> Glance_registry_config<||> + Glance_registry_config<||> ~> Exec<| title == 'glance-manage db_sync' |> + Glance_registry_config<||> ~> Service['glance-registry'] + + File { + ensure => present, + owner => 'glance', + group => 'glance', + mode => '0640', + notify => Service['glance-registry'], + require => Class['glance'] + } + + if($sql_connection =~ /mysql:\/\/\S+:\S+@\S+\/\S+/) { + require mysql::python + } elsif($sql_connection =~ /postgresql:\/\/\S+:\S+@\S+\/\S+/) { + + } elsif($sql_connection =~ /sqlite:\/\//) { + + } else { + fail("Invalid db connection ${sql_connection}") + } + + glance_registry_config { + 'DEFAULT/verbose': value => $verbose; + 'DEFAULT/debug': value => $debug; + 'DEFAULT/bind_host': value => $bind_host; + 'DEFAULT/bind_port': value => $bind_port; + } + + glance_registry_config { + 'DEFAULT/sql_connection': value => $sql_connection; + 'DEFAULT/sql_idle_timeout': value => $sql_idle_timeout; + } + + if $auth_uri { + glance_registry_config { 'keystone_authtoken/auth_uri': value => $auth_uri; } + } else { + glance_registry_config { 'keystone_authtoken/auth_uri': value => "${auth_protocol}://${auth_host}:5000/"; } + } + + # auth config + glance_registry_config { + 'keystone_authtoken/auth_host': value => $auth_host; + 'keystone_authtoken/auth_port': value => $auth_port; + 'keystone_authtoken/auth_protocol': value => $auth_protocol; + } + + if $auth_admin_prefix { + validate_re($auth_admin_prefix, '^(/.+[^/])?$') + glance_registry_config { + 'keystone_authtoken/auth_admin_prefix': value => $auth_admin_prefix; + } + } else { + glance_registry_config { + 'keystone_authtoken/auth_admin_prefix': ensure => absent; + } + } + + # Set the pipeline, it is allowed to be blank + if $pipeline != '' { + validate_re($pipeline, '^(\w+([+]\w+)*)*$') + glance_registry_config { + 'paste_deploy/flavor': + ensure => present, + value => $pipeline, + } + } else { + glance_registry_config { 'paste_deploy/flavor': ensure => absent } + } + + # keystone config + if $auth_type == 'keystone' { + glance_registry_config { + 'keystone_authtoken/admin_tenant_name': value => $keystone_tenant; + 'keystone_authtoken/admin_user' : value => $keystone_user; + 'keystone_authtoken/admin_password' : value => $keystone_password; + } + } + + # SSL Options + if $cert_file { + glance_registry_config { + 'DEFAULT/cert_file' : value => $cert_file; + } + } else { + glance_registry_config { + 'DEFAULT/cert_file': ensure => absent; + } + } + if $key_file { + glance_registry_config { + 'DEFAULT/key_file' : value => $key_file; + } + } else { + glance_registry_config { + 'DEFAULT/key_file': ensure => absent; + } + } + if $ca_file { + glance_registry_config { + 'DEFAULT/ca_file' : value => $ca_file; + } + } else { + glance_registry_config { + 'DEFAULT/ca_file': ensure => absent; + } + } + + # Logging + if $log_file { + glance_registry_config { + 'DEFAULT/log_file': value => $log_file; + } + } else { + glance_registry_config { + 'DEFAULT/log_file': ensure => absent; + } + } + + if $log_dir { + glance_registry_config { + 'DEFAULT/log_dir': value => $log_dir; + } + } else { + glance_registry_config { + 'DEFAULT/log_dir': ensure => absent; + } + } + + # Syslog + if $use_syslog { + glance_registry_config { + 'DEFAULT/use_syslog': value => true; + 'DEFAULT/syslog_log_facility': value => $log_facility; + } + } else { + glance_registry_config { + 'DEFAULT/use_syslog': value => false; + } + } + + file { ['/etc/glance/glance-registry.conf', + '/etc/glance/glance-registry-paste.ini']: + } + + if $enabled { + + Exec['glance-manage db_sync'] ~> Service['glance-registry'] + + exec { 'glance-manage db_sync': + command => $::glance::params::db_sync_command, + path => '/usr/bin', + user => 'glance', + refreshonly => true, + logoutput => on_failure, + subscribe => [Package['glance'], File['/etc/glance/glance-registry.conf']], + } + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + + service { 'glance-registry': + ensure => $service_ensure, + name => $::glance::params::registry_service_name, + enable => $enabled, + hasstatus => true, + hasrestart => true, + subscribe => File['/etc/glance/glance-registry.conf'], + require => Class['glance'] + } + +} diff --git a/glance/spec/classes/glance_api_spec.rb b/glance/spec/classes/glance_api_spec.rb new file mode 100644 index 000000000..d13e6c5f5 --- /dev/null +++ b/glance/spec/classes/glance_api_spec.rb @@ -0,0 +1,289 @@ +require 'spec_helper' + +describe 'glance::api' do + + let :facts do + { + :osfamily => 'Debian', + :processorcount => '7', + } + end + + let :default_params do + { + :verbose => false, + :debug => false, + :bind_host => '0.0.0.0', + :bind_port => '9292', + :registry_host => '0.0.0.0', + :registry_port => '9191', + :log_file => '/var/log/glance/api.log', + :log_dir => '/var/log/glance', + :auth_type => 'keystone', + :enabled => true, + :backlog => '4096', + :workers => '7', + :auth_host => '127.0.0.1', + :auth_port => '35357', + :auth_protocol => 'http', + :auth_uri => 'http://127.0.0.1:5000/', + :keystone_tenant => 'services', + :keystone_user => 'glance', + :keystone_password => 'ChangeMe', + :sql_idle_timeout => '3600', + :sql_connection => 'sqlite:///var/lib/glance/glance.sqlite', + :show_image_direct_url => false, + :notifier_strategy => 'noop' + } + end + + [{:keystone_password => 'ChangeMe'}, + { + :verbose => true, + :debug => true, + :bind_host => '127.0.0.1', + :bind_port => '9222', + :registry_host => '127.0.0.1', + :registry_port => '9111', + :auth_type => 'not_keystone', + :enabled => false, + :backlog => '4095', + :workers => '5', + :auth_host => '127.0.0.2', + :auth_port => '35358', + :auth_protocol => 'https', + :auth_uri => 'https://127.0.0.2:5000/v2.0/', + :keystone_tenant => 'admin2', + :keystone_user => 'admin2', + :keystone_password => 'ChangeMe2', + :sql_idle_timeout => '36002', + :sql_connection => 'mysql:///var:lib@glance/glance', + :show_image_direct_url => true + } + ].each do |param_set| + + describe "when #{param_set == {:keystone_password => 'ChangeMe'} ? "using default" : "specifying"} class parameters" do + + let :param_hash do + default_params.merge(param_set) + end + + let :params do + param_set + end + + it { should contain_class 'glance' } + + it { should contain_service('glance-api').with( + 'ensure' => param_hash[:enabled] ? 'running': 'stopped', + 'enable' => param_hash[:enabled], + 'hasstatus' => true, + 'hasrestart' => true + ) } + + it 'should lay down default api config' do + [ + 'verbose', + 'debug', + 'bind_host', + 'bind_port', + 'registry_host', + 'registry_port', + 'show_image_direct_url', + 'notifier_strategy' + ].each do |config| + should contain_glance_api_config("DEFAULT/#{config}").with_value(param_hash[config.intern]) + end + end + + it 'should lay down default cache config' do + [ + 'verbose', + 'debug', + 'registry_host', + 'registry_port' + ].each do |config| + should contain_glance_cache_config("DEFAULT/#{config}").with_value(param_hash[config.intern]) + end + end + + it 'should config db' do + should contain_glance_api_config('DEFAULT/sql_connection').with_value(param_hash[:sql_connection]) + should contain_glance_api_config('DEFAULT/sql_idle_timeout').with_value(param_hash[:sql_idle_timeout]) + end + + it 'should have no ssl options' do + should contain_glance_api_config('DEFAULT/ca_file').with_ensure('absent') + should contain_glance_api_config('DEFAULT/cert_file').with_ensure('absent') + should contain_glance_api_config('DEFAULT/key_file').with_ensure('absent') + end + + it 'should lay down default auth config' do + [ + 'auth_host', + 'auth_port', + 'auth_protocol' + ].each do |config| + should contain_glance_api_config("keystone_authtoken/#{config}").with_value(param_hash[config.intern]) + end + end + it { should contain_glance_api_config('keystone_authtoken/auth_admin_prefix').with_ensure('absent') } + + it 'should configure itself for keystone if that is the auth_type' do + if params[:auth_type] == 'keystone' + should contain('paste_deploy/flavor').with_value('keystone+cachemanagement') + ['admin_tenant_name', 'admin_user', 'admin_password'].each do |config| + should contain_glance_api_config("keystone_authtoken/#{config}").with_value(param_hash[config.intern]) + end + ['admin_tenant_name', 'admin_user', 'admin_password'].each do |config| + should contain_glance_cache_config("keystone_authtoken/#{config}").with_value(param_hash[config.intern]) + end + end + end + end + end + + describe 'with overridden pipeline' do + let :params do + { + :keystone_password => 'ChangeMe', + :pipeline => 'keystone', + } + end + + it { should contain_glance_api_config('paste_deploy/flavor').with_value('keystone') } + end + + describe 'with blank pipeline' do + let :params do + { + :keystone_password => 'ChangeMe', + :pipeline => '', + } + end + + it { should contain_glance_api_config('paste_deploy/flavor').with_ensure('absent') } + end + + [ + 'keystone/', + 'keystone+', + '+keystone', + 'keystone+cachemanagement+', + '+' + ].each do |pipeline| + describe "with pipeline incorrect value #{pipeline}" do + let :params do + { + :keystone_password => 'ChangeMe', + :pipeline => pipeline + } + end + + it { expect { should contain_glance_api_config('filter:paste_deploy/flavor') }.to\ + raise_error(Puppet::Error, /validate_re\(\): .* does not match/) } + end + end + + describe 'with overriden auth_admin_prefix' do + let :params do + { + :keystone_password => 'ChangeMe', + :auth_admin_prefix => '/keystone/main' + } + end + + it { should contain_glance_api_config('keystone_authtoken/auth_admin_prefix').with_value('/keystone/main') } + end + + [ + '/keystone/', + 'keystone/', + 'keystone', + '/keystone/admin/', + 'keystone/admin/', + 'keystone/admin' + ].each do |auth_admin_prefix| + describe "with auth_admin_prefix_containing incorrect value #{auth_admin_prefix}" do + let :params do + { + :keystone_password => 'ChangeMe', + :auth_admin_prefix => auth_admin_prefix + } + end + + it { expect { should contain_glance_api_config('filter:authtoken/auth_admin_prefix') }.to\ + raise_error(Puppet::Error, /validate_re\(\): "#{auth_admin_prefix}" does not match/) } + end + end + + describe 'with syslog disabled by default' do + let :params do + default_params + end + + it { should contain_glance_api_config('DEFAULT/use_syslog').with_value(false) } + it { should_not contain_glance_api_config('DEFAULT/syslog_log_facility') } + end + + describe 'with syslog enabled' do + let :params do + default_params.merge({ + :use_syslog => 'true', + }) + end + + it { should contain_glance_api_config('DEFAULT/use_syslog').with_value(true) } + it { should contain_glance_api_config('DEFAULT/syslog_log_facility').with_value('LOG_USER') } + end + + describe 'with syslog enabled and custom settings' do + let :params do + default_params.merge({ + :use_syslog => 'true', + :log_facility => 'LOG_LOCAL0' + }) + end + + it { should contain_glance_api_config('DEFAULT/use_syslog').with_value(true) } + it { should contain_glance_api_config('DEFAULT/syslog_log_facility').with_value('LOG_LOCAL0') } + end + + describe 'with log_file enabled by default' do + let(:params) { default_params } + + it { should contain_glance_api_config('DEFAULT/log_file').with_value(default_params[:log_file]) } + + context 'with log_file disabled' do + let(:params) { default_params.merge!({ :log_file => false }) } + it { should contain_glance_api_config('DEFAULT/log_file').with_ensure('absent') } + end + end + + describe 'with log_dir enabled by default' do + let(:params) { default_params } + + it { should contain_glance_api_config('DEFAULT/log_dir').with_value(default_params[:log_dir]) } + + context 'with log_dir disabled' do + let(:params) { default_params.merge!({ :log_dir => false }) } + it { should contain_glance_api_config('DEFAULT/log_dir').with_ensure('absent') } + end + end + + describe 'with ssl options' do + let :params do + default_params.merge({ + :ca_file => '/tmp/ca_file', + :cert_file => '/tmp/cert_file', + :key_file => '/tmp/key_file' + }) + end + + context 'with ssl options' do + it { should contain_glance_api_config('DEFAULT/ca_file').with_value('/tmp/ca_file') } + it { should contain_glance_api_config('DEFAULT/cert_file').with_value('/tmp/cert_file') } + it { should contain_glance_api_config('DEFAULT/key_file').with_value('/tmp/key_file') } + end + end +end diff --git a/glance/spec/classes/glance_backend_cinder_spec.rb b/glance/spec/classes/glance_backend_cinder_spec.rb new file mode 100644 index 000000000..f3f33ac63 --- /dev/null +++ b/glance/spec/classes/glance_backend_cinder_spec.rb @@ -0,0 +1,99 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# Unit tests for glance::backend::cinder class +# + +require 'spec_helper' + +describe 'glance::backend::cinder' do + + let :pre_condition do + 'class { "glance::api": keystone_password => "pass" }' + end + + shared_examples_for 'glance with cinder backend' do + + context 'when default parameters' do + + it 'configures glance-api.conf' do + should contain_glance_api_config('DEFAULT/default_store').with_value('cinder') + should contain_glance_api_config('DEFAULT/cinder_api_insecure').with_value(false) + should contain_glance_api_config('DEFAULT/cinder_catalog_info').with_value('volume:cinder:publicURL') + should contain_glance_api_config('DEFAULT/os_region_name').with_value('RegionOne') + should contain_glance_api_config('DEFAULT/cinder_http_retries').with_value('3') + should contain_glance_api_config('DEFAULT/cinder_ca_certificates_file').with(:ensure => 'absent') + should contain_glance_api_config('DEFAULT/cinder_endpoint_template').with(:ensure => 'absent') + end + it 'configures glance-cache.conf' do + should contain_glance_cache_config('DEFAULT/cinder_api_insecure').with_value(false) + should contain_glance_cache_config('DEFAULT/cinder_catalog_info').with_value('volume:cinder:publicURL') + should contain_glance_cache_config('DEFAULT/os_region_name').with_value('RegionOne') + should contain_glance_cache_config('DEFAULT/cinder_http_retries').with_value('3') + should contain_glance_cache_config('DEFAULT/cinder_ca_certificates_file').with(:ensure => 'absent') + should contain_glance_cache_config('DEFAULT/cinder_endpoint_template').with(:ensure => 'absent') + end + end + + context 'when overriding parameters' do + let :params do + { + :cinder_api_insecure => true, + :cinder_ca_certificates_file => '/etc/ssh/ca.crt', + :cinder_catalog_info => 'volume:cinder:internalURL', + :cinder_endpoint_template => 'http://srv-foo:8776/v1/%(project_id)s', + :cinder_http_retries => '10', + :os_region_name => 'foo' + } + end + it 'configures glance-api.conf' do + should contain_glance_api_config('DEFAULT/default_store').with_value('cinder') + should contain_glance_api_config('DEFAULT/cinder_api_insecure').with_value(true) + should contain_glance_api_config('DEFAULT/cinder_ca_certificates_file').with_value('/etc/ssh/ca.crt') + should contain_glance_api_config('DEFAULT/cinder_catalog_info').with_value('volume:cinder:internalURL') + should contain_glance_api_config('DEFAULT/cinder_endpoint_template').with_value('http://srv-foo:8776/v1/%(project_id)s') + should contain_glance_api_config('DEFAULT/cinder_http_retries').with_value('10') + should contain_glance_api_config('DEFAULT/os_region_name').with_value('foo') + end + it 'configures glance-cache.conf' do + should contain_glance_cache_config('DEFAULT/cinder_api_insecure').with_value(true) + should contain_glance_cache_config('DEFAULT/cinder_ca_certificates_file').with_value('/etc/ssh/ca.crt') + should contain_glance_cache_config('DEFAULT/cinder_catalog_info').with_value('volume:cinder:internalURL') + should contain_glance_cache_config('DEFAULT/cinder_endpoint_template').with_value('http://srv-foo:8776/v1/%(project_id)s') + should contain_glance_cache_config('DEFAULT/cinder_http_retries').with_value('10') + should contain_glance_cache_config('DEFAULT/os_region_name').with_value('foo') + end + end + + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'glance with cinder backend' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'glance with cinder backend' + end +end diff --git a/glance/spec/classes/glance_backend_file_spec.rb b/glance/spec/classes/glance_backend_file_spec.rb new file mode 100644 index 000000000..fac7fcf46 --- /dev/null +++ b/glance/spec/classes/glance_backend_file_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe 'glance::backend::file' do + let :facts do + { :osfamily => 'Debian' } + end + + let :pre_condition do + 'class { "glance::api": keystone_password => "pass" }' + end + + it 'configures glance-api.conf' do + should contain_glance_api_config('DEFAULT/default_store').with_value('file') + should contain_glance_api_config('DEFAULT/filesystem_store_datadir').with_value('/var/lib/glance/images/') + end + + it 'configures glance-cache.conf' do + should contain_glance_cache_config('DEFAULT/filesystem_store_datadir').with_value('/var/lib/glance/images/') + end + + describe 'when overriding datadir' do + let :params do + {:filesystem_store_datadir => '/tmp/'} + end + + it 'configures glance-api.conf' do + should contain_glance_api_config('DEFAULT/filesystem_store_datadir').with_value('/tmp/') + end + + it 'configures glance-cache.conf' do + should contain_glance_cache_config('DEFAULT/filesystem_store_datadir').with_value('/tmp/') + end + end +end diff --git a/glance/spec/classes/glance_backend_rbd_spec.rb b/glance/spec/classes/glance_backend_rbd_spec.rb new file mode 100644 index 000000000..8e4a5817d --- /dev/null +++ b/glance/spec/classes/glance_backend_rbd_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe 'glance::backend::rbd' do + let :facts do + { + :osfamily => 'Debian' + } + end + + let :params do + { + :rbd_store_user => 'glance', + } + end + + it { should contain_glance_api_config('DEFAULT/default_store').with_value('rbd') } + it { should contain_glance_api_config('DEFAULT/rbd_store_pool').with_value('images') } + it { should contain_glance_api_config('DEFAULT/rbd_store_ceph_conf').with_value('/etc/ceph/ceph.conf') } + it { should contain_glance_api_config('DEFAULT/rbd_store_chunk_size').with_value('8') } + + it { should contain_package('python-ceph').with( + :name => 'python-ceph', + :ensure => 'present' + ) + } + + describe 'when passing params' do + let :params do + { + :rbd_store_user => 'user', + :rbd_store_chunk_size => '2', + } + it { should contain_glance_api_config('DEFAULT/rbd_store_user').with_value('user') } + it { should contain_glance_api_config('DEFAULT/rbd_store_chunk_size').with_value('2') } + end + end +end diff --git a/glance/spec/classes/glance_backend_swift_spec.rb b/glance/spec/classes/glance_backend_swift_spec.rb new file mode 100644 index 000000000..3ced3433a --- /dev/null +++ b/glance/spec/classes/glance_backend_swift_spec.rb @@ -0,0 +1,69 @@ +require 'spec_helper' + +describe 'glance::backend::swift' do + let :facts do + { + :osfamily => 'Debian' + } + end + + let :params do + { + :swift_store_user => 'user', + :swift_store_key => 'key', + } + end + + let :pre_condition do + 'class { "glance::api": keystone_password => "pass" }' + end + + describe 'when default parameters' do + + it 'configures glance-api.conf' do + should contain_glance_api_config('DEFAULT/default_store').with_value('swift') + should contain_glance_api_config('DEFAULT/swift_store_key').with_value('key') + should contain_glance_api_config('DEFAULT/swift_store_user').with_value('user') + should contain_glance_api_config('DEFAULT/swift_store_auth_version').with_value('2') + should contain_glance_api_config('DEFAULT/swift_store_auth_address').with_value('127.0.0.1:5000/v2.0/') + should contain_glance_api_config('DEFAULT/swift_store_container').with_value('glance') + should contain_glance_api_config('DEFAULT/swift_store_create_container_on_put').with_value(false) + end + + it 'configures glance-cache.conf' do + should contain_glance_cache_config('DEFAULT/swift_store_key').with_value('key') + should contain_glance_cache_config('DEFAULT/swift_store_user').with_value('user') + should contain_glance_cache_config('DEFAULT/swift_store_auth_version').with_value('2') + should contain_glance_cache_config('DEFAULT/swift_store_auth_address').with_value('127.0.0.1:5000/v2.0/') + should contain_glance_cache_config('DEFAULT/swift_store_container').with_value('glance') + should contain_glance_cache_config('DEFAULT/swift_store_create_container_on_put').with_value(false) + end + end + + describe 'when overriding parameters' do + let :params do + { + :swift_store_user => 'user', + :swift_store_key => 'key', + :swift_store_auth_version => '1', + :swift_store_auth_address => '127.0.0.2:8080/v1.0/', + :swift_store_container => 'swift', + :swift_store_create_container_on_put => true + } + end + + it 'configures glance-api.conf' do + should contain_glance_api_config('DEFAULT/swift_store_container').with_value('swift') + should contain_glance_api_config('DEFAULT/swift_store_create_container_on_put').with_value(true) + should contain_glance_api_config('DEFAULT/swift_store_auth_version').with_value('1') + should contain_glance_api_config('DEFAULT/swift_store_auth_address').with_value('127.0.0.2:8080/v1.0/') + end + + it 'configures glance-cache.conf' do + should contain_glance_cache_config('DEFAULT/swift_store_container').with_value('swift') + should contain_glance_cache_config('DEFAULT/swift_store_create_container_on_put').with_value(true) + should contain_glance_cache_config('DEFAULT/swift_store_auth_version').with_value('1') + should contain_glance_cache_config('DEFAULT/swift_store_auth_address').with_value('127.0.0.2:8080/v1.0/') + end + end +end diff --git a/glance/spec/classes/glance_cache_cleaner_spec.rb b/glance/spec/classes/glance_cache_cleaner_spec.rb new file mode 100644 index 000000000..8b8bbe48e --- /dev/null +++ b/glance/spec/classes/glance_cache_cleaner_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe 'glance::cache::cleaner' do + + let :facts do + { :osfamily => 'Debian' } + end + + it 'configures a cron' do + should contain_cron('glance-cache-cleaner').with( + :command => 'glance-cache-cleaner', + :environment => 'PATH=/bin:/usr/bin:/usr/sbin', + :user => 'glance', + :minute => 1, + :hour => 0, + :monthday => '*', + :month => '*', + :weekday => '*' + ) + end +end diff --git a/glance/spec/classes/glance_cache_pruner_spec.rb b/glance/spec/classes/glance_cache_pruner_spec.rb new file mode 100644 index 000000000..5bcf7cd2a --- /dev/null +++ b/glance/spec/classes/glance_cache_pruner_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +describe 'glance::cache::pruner' do + + let :facts do + { :osfamily => 'Debian' } + end + + it 'configures a cron' do + should contain_cron('glance-cache-pruner').with( + :command => 'glance-cache-pruner', + :environment => 'PATH=/bin:/usr/bin:/usr/sbin', + :user => 'glance', + :minute => '*/30', + :hour => '*', + :monthday => '*', + :month => '*', + :weekday => '*' + ) + end +end diff --git a/glance/spec/classes/glance_client_spec.rb b/glance/spec/classes/glance_client_spec.rb new file mode 100644 index 000000000..3c9a4a27d --- /dev/null +++ b/glance/spec/classes/glance_client_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe 'glance::client' do + + shared_examples 'glance client' do + it { should include_class('glance::params') } + it { should contain_package('python-glanceclient').with( + :name => 'python-glanceclient', + :ensure => 'present' + ) + } + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + include_examples 'glance client' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + include_examples 'glance client' + end +end diff --git a/glance/spec/classes/glance_db_mysql_spec.rb b/glance/spec/classes/glance_db_mysql_spec.rb new file mode 100644 index 000000000..ace9fcca8 --- /dev/null +++ b/glance/spec/classes/glance_db_mysql_spec.rb @@ -0,0 +1,101 @@ +require 'spec_helper' + +describe 'glance::db::mysql' do + let :facts do + { + :osfamily => 'Debian' + } + end + + let :pre_condition do + 'include mysql::server' + end + + describe "with default params" do + let :params do + { + :password => 'glancepass1' + } + end + + it { should include_class('mysql::python') } + + it { should contain_mysql__db('glance').with( + :password => 'glancepass1', + :require => 'Class[Mysql::Config]', + :charset => 'latin1' + )} + + end + + describe "overriding default params" do + let :params do + { + :password => 'glancepass2', + :dbname => 'glancedb2', + :charset => 'utf8', + } + end + + it { should contain_mysql__db('glancedb2').with( + :password => 'glancepass2', + :charset => 'utf8' + )} + + end + + describe "overriding allowed_hosts param to array" do + let :params do + { + :password => 'glancepass2', + :dbname => 'glancedb2', + :allowed_hosts => ['127.0.0.1','%'] + } + end + + it {should_not contain_glance__db__mysql__host_access("127.0.0.1").with( + :user => 'glance', + :password => 'glancepass2', + :database => 'glancedb2' + )} + it {should contain_glance__db__mysql__host_access("%").with( + :user => 'glance', + :password => 'glancepass2', + :database => 'glancedb2' + )} + + end + + describe "overriding allowed_hosts param to string" do + let :params do + { + :password => 'glancepass2', + :dbname => 'glancedb2', + :allowed_hosts => '192.168.1.1' + } + end + + it {should contain_glance__db__mysql__host_access("192.168.1.1").with( + :user => 'glance', + :password => 'glancepass2', + :database => 'glancedb2' + )} + end + + describe "overriding allowed_hosts param equals to host param " do + let :params do + { + :password => 'glancepass2', + :dbname => 'glancedb2', + :allowed_hosts => '127.0.0.1' + } + end + + it {should_not contain_glance__db__mysql__host_access("127.0.0.1").with( + :user => 'glance', + :password => 'glancepass2', + :database => 'glancedb2' + )} + end + +end diff --git a/glance/spec/classes/glance_db_postgresql_spec.rb b/glance/spec/classes/glance_db_postgresql_spec.rb new file mode 100644 index 000000000..dcb2b0126 --- /dev/null +++ b/glance/spec/classes/glance_db_postgresql_spec.rb @@ -0,0 +1,26 @@ +require 'spec_helper' + +describe 'glance::db::postgresql' do + + let :req_params do + {:password => 'pw'} + end + + let :facts do + { + :postgres_default_version => '8.4', + :osfamily => 'RedHat', + } + end + + describe 'with only required params' do + let :params do + req_params + end + it { should contain_postgresql__db('glance').with( + :user => 'glance', + :password => 'pw' + ) } + end + +end diff --git a/glance/spec/classes/glance_keystone_auth_spec.rb b/glance/spec/classes/glance_keystone_auth_spec.rb new file mode 100644 index 000000000..551d00df1 --- /dev/null +++ b/glance/spec/classes/glance_keystone_auth_spec.rb @@ -0,0 +1,119 @@ +require 'spec_helper' + +describe 'glance::keystone::auth' do + + describe 'with defaults' do + + let :params do + {:password => 'pass'} + end + + it { should contain_keystone_user('glance').with( + :ensure => 'present', + :password => 'pass' + )} + + it { should contain_keystone_user_role('glance@services').with( + :ensure => 'present', + :roles => 'admin' + ) } + + it { should contain_keystone_service('glance').with( + :ensure => 'present', + :type => 'image', + :description => 'Openstack Image Service' + ) } + + it { should contain_keystone_endpoint('RegionOne/glance').with( + :ensure => 'present', + :public_url => 'http://127.0.0.1:9292', + :admin_url => 'http://127.0.0.1:9292', + :internal_url => 'http://127.0.0.1:9292' + )} + + end + + describe 'when auth_type, password, and service_type are overridden' do + + let :params do + { + :auth_name => 'glancey', + :password => 'password', + :service_type => 'imagey' + } + end + + it { should contain_keystone_user('glancey').with( + :ensure => 'present', + :password => 'password' + )} + + it { should contain_keystone_user_role('glancey@services').with( + :ensure => 'present', + :roles => 'admin' + ) } + + it { should contain_keystone_service('glancey').with( + :ensure => 'present', + :type => 'imagey', + :description => 'Openstack Image Service' + ) } + + end + + describe 'when address, region, port and protocoll are overridden' do + + let :params do + { + :password => 'pass', + :public_address => '10.0.0.1', + :admin_address => '10.0.0.2', + :internal_address => '10.0.0.3', + :port => '9393', + :region => 'RegionTwo', + :public_protocol => 'https', + :admin_protocol => 'https', + :internal_protocol => 'https' + } + end + + it { should contain_keystone_endpoint('RegionTwo/glance').with( + :ensure => 'present', + :public_url => 'https://10.0.0.1:9393', + :admin_url => 'https://10.0.0.2:9393', + :internal_url => 'https://10.0.0.3:9393' + )} + + end + + describe 'when endpoint is not set' do + + let :params do + { + :configure_endpoint => false, + :password => 'pass', + } + end + + it { should_not contain_keystone_endpoint('glance') } + end + + describe 'when configuring glance-api and the keystone endpoint' do + let :pre_condition do + "class { 'glance::api': keystone_password => 'test' }" + end + + let :facts do + { :osfamily => 'Debian' } + end + + let :params do + { + :password => 'test', + :configure_endpoint => true + } + end + + it { should contain_keystone_endpoint('RegionOne/glance').with_notify('Service[glance-api]') } + end +end diff --git a/glance/spec/classes/glance_notify_qpid_spec.rb b/glance/spec/classes/glance_notify_qpid_spec.rb new file mode 100644 index 000000000..36440f3ed --- /dev/null +++ b/glance/spec/classes/glance_notify_qpid_spec.rb @@ -0,0 +1,36 @@ +require 'spec_helper' +describe 'glance::notify::qpid' do + let :facts do + { + :osfamily => 'Debian' + } + end + let :params do + {:qpid_password => 'pass'} + end + + let :pre_condition do + 'class { "glance::api": keystone_password => "pass" }' + end + + it { should contain_glance_api_config('DEFAULT/notifier_strategy').with_value('qpid') } + it { should contain_glance_api_config('DEFAULT/notifier_strategy').without_value('noop') } + it { should contain_glance_api_config('DEFAULT/qpid_username').with_value('guest') } + it { should contain_glance_api_config('DEFAULT/qpid_password').with_value('pass') } + it { should contain_glance_api_config('DEFAULT/qpid_hostname').with_value('localhost') } + it { should contain_glance_api_config('DEFAULT/qpid_port').with_value('5672') } + + describe 'when passing params' do + let :params do + { + :qpid_password => 'pass', + :qpid_usernane => 'guest2', + :qpid_hostname => 'localhost2', + :qpid_port => '5673' + } + it { should contain_glance_api_config('DEFAULT/qpid_username').with_value('guest2') } + it { should contain_glance_api_config('DEFAULT/qpid_hostname').with_value('localhost2') } + it { should contain_glance_api_config('DEFAULT/qpid_port').with_value('5673') } + end + end +end diff --git a/glance/spec/classes/glance_notify_rabbitmq_spec.rb b/glance/spec/classes/glance_notify_rabbitmq_spec.rb new file mode 100644 index 000000000..b28bed1f6 --- /dev/null +++ b/glance/spec/classes/glance_notify_rabbitmq_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' +describe 'glance::notify::rabbitmq' do + let :facts do + { + :osfamily => 'Debian' + } + end + + let :pre_condition do + 'class { "glance::api": keystone_password => "pass" }' + end + + let :params do + {:rabbit_password => 'pass'} + end + + it { should contain_glance_api_config('DEFAULT/notifier_strategy').with_value('rabbit') } + it { should contain_glance_api_config('DEFAULT/notifier_strategy').without_value('noop') } + it { should contain_glance_api_config('DEFAULT/rabbit_password').with_value('pass') } + it { should contain_glance_api_config('DEFAULT/rabbit_userid').with_value('guest') } + it { should contain_glance_api_config('DEFAULT/rabbit_host').with_value('localhost') } + it { should contain_glance_api_config('DEFAULT/rabbit_port').with_value('5672') } + it { should contain_glance_api_config('DEFAULT/rabbit_virtual_host').with_value('/') } + it { should contain_glance_api_config('DEFAULT/rabbit_notification_exchange').with_value('glance') } + it { should contain_glance_api_config('DEFAULT/rabbit_notification_topic').with_value('notifications') } + + describe 'when passing params' do + let :params do + { + :rabbit_password => 'pass', + :rabbit_userid => 'guest2', + :rabbit_host => 'localhost2', + :rabbit_port => '5673', + :rabbit_use_ssl => true, + :rabbit_durable_queues => true, + } + it { should contain_glance_api_config('DEFAULT/rabbit_userid').with_value('guest2') } + it { should contain_glance_api_config('DEFAULT/rabbit_host').with_value('localhost2') } + it { should contain_glance_api_config('DEFAULT/rabbit_port').with_value('5673') } + it { should contain_glance_api_config('DEFAULT/rabbit_use_ssl').with_value('true') } + it { should contain_glance_api_config('DEFAULT/rabbit_durable_queues').with_value('true') } + end + end +end diff --git a/glance/spec/classes/glance_registry_spec.rb b/glance/spec/classes/glance_registry_spec.rb new file mode 100644 index 000000000..a5acf704e --- /dev/null +++ b/glance/spec/classes/glance_registry_spec.rb @@ -0,0 +1,269 @@ + +describe 'glance::registry' do + + let :facts do + { + :osfamily => 'Debian' + } + end + + let :default_params do + { + :verbose => false, + :debug => false, + :bind_host => '0.0.0.0', + :bind_port => '9191', + :log_file => '/var/log/glance/registry.log', + :log_dir => '/var/log/glance', + :sql_connection => 'sqlite:///var/lib/glance/glance.sqlite', + :sql_idle_timeout => '3600', + :enabled => true, + :auth_type => 'keystone', + :auth_host => '127.0.0.1', + :auth_port => '35357', + :auth_protocol => 'http', + :auth_uri => 'http://127.0.0.1:5000/', + :keystone_tenant => 'services', + :keystone_user => 'glance', + :keystone_password => 'ChangeMe', + } + end + + [ + {:keystone_password => 'ChangeMe'}, + { + :verbose => true, + :debug => true, + :bind_host => '127.0.0.1', + :bind_port => '9111', + :sql_connection => 'sqlite:///var/lib/glance.sqlite', + :sql_idle_timeout => '360', + :enabled => false, + :auth_type => 'keystone', + :auth_host => '127.0.0.1', + :auth_port => '35357', + :auth_protocol => 'http', + :auth_uri => 'http://127.0.0.1:5000/', + :keystone_tenant => 'admin', + :keystone_user => 'admin', + :keystone_password => 'ChangeMe', + } + ].each do |param_set| + + describe "when #{param_set == {:keystone_password => 'ChangeMe'} ? "using default" : "specifying"} class parameters" do + let :param_hash do + default_params.merge(param_set) + end + + let :params do + param_set + end + + it { should contain_class 'glance::registry' } + + it { should contain_service('glance-registry').with( + 'ensure' => param_hash[:enabled] ? 'running' : 'stopped', + 'enable' => param_hash[:enabled], + 'hasstatus' => true, + 'hasrestart' => true, + 'subscribe' => 'File[/etc/glance/glance-registry.conf]', + 'require' => 'Class[Glance]' + )} + + it 'should only sync the db if the service is enabled' do + + if param_hash[:enabled] + should contain_exec('glance-manage db_sync').with( + 'path' => '/usr/bin', + 'refreshonly' => true, + 'logoutput' => 'on_failure', + 'subscribe' => ['Package[glance]', 'File[/etc/glance/glance-registry.conf]'], + 'notify' => 'Service[glance-registry]' + ) + end + end + it 'should configure itself' do + [ + 'verbose', + 'debug', + 'bind_port', + 'bind_host', + 'sql_connection', + 'sql_idle_timeout' + ].each do |config| + should contain_glance_registry_config("DEFAULT/#{config}").with_value(param_hash[config.intern]) + end + [ + 'auth_host', + 'auth_port', + 'auth_protocol' + ].each do |config| + should contain_glance_registry_config("keystone_authtoken/#{config}").with_value(param_hash[config.intern]) + end + should contain_glance_registry_config('keystone_authtoken/auth_admin_prefix').with_ensure('absent') + if param_hash[:auth_type] == 'keystone' + should contain_glance_registry_config("paste_deploy/flavor").with_value('keystone') + should contain_glance_registry_config("keystone_authtoken/admin_tenant_name").with_value(param_hash[:keystone_tenant]) + should contain_glance_registry_config("keystone_authtoken/admin_user").with_value(param_hash[:keystone_user]) + should contain_glance_registry_config("keystone_authtoken/admin_password").with_value(param_hash[:keystone_password]) + end + end + end + end + + describe 'with overridden pipeline' do + # At the time of writing there was only blank and keystone as options + # but there is no reason that there can't be more options in the future. + let :params do + { + :keystone_password => 'ChangeMe', + :pipeline => 'validoptionstring', + } + end + + it { should contain_glance_registry_config('paste_deploy/flavor').with_value('validoptionstring') } + end + + describe 'with blank pipeline' do + let :params do + { + :keystone_password => 'ChangeMe', + :pipeline => '', + } + end + + it { should contain_glance_registry_config('paste_deploy/flavor').with_ensure('absent') } + end + + [ + 'keystone/', + 'keystone+', + '+keystone', + 'keystone+cachemanagement+', + '+' + ].each do |pipeline| + describe "with pipeline incorrect value #{pipeline}" do + let :params do + { + :keystone_password => 'ChangeMe', + :auth_type => 'keystone', + :pipeline => pipeline + } + end + + it { expect { should contain_glance_registry_config('filter:paste_deploy/flavor') }.to\ + raise_error(Puppet::Error, /validate_re\(\): .* does not match/) } + end + end + + describe 'with overriden auth_admin_prefix' do + let :params do + { + :keystone_password => 'ChangeMe', + :auth_admin_prefix => '/keystone/main' + } + end + + it { should contain_glance_registry_config('keystone_authtoken/auth_admin_prefix').with_value('/keystone/main') } + end + + [ + '/keystone/', + 'keystone/', + 'keystone', + '/keystone/admin/', + 'keystone/admin/', + 'keystone/admin' + ].each do |auth_admin_prefix| + describe "with auth_admin_prefix_containing incorrect value #{auth_admin_prefix}" do + let :params do + { + :keystone_password => 'ChangeMe', + :auth_admin_prefix => auth_admin_prefix + } + end + + it { expect { should contain_glance_registry_config('filter:authtoken/auth_admin_prefix') }.to\ + raise_error(Puppet::Error, /validate_re\(\): "#{auth_admin_prefix}" does not match/) } + end + end + + describe 'with syslog disabled by default' do + let :params do + default_params + end + + it { should contain_glance_registry_config('DEFAULT/use_syslog').with_value(false) } + it { should_not contain_glance_registry_config('DEFAULT/syslog_log_facility') } + end + + describe 'with syslog enabled' do + let :params do + default_params.merge({ + :use_syslog => 'true', + }) + end + + it { should contain_glance_registry_config('DEFAULT/use_syslog').with_value(true) } + it { should contain_glance_registry_config('DEFAULT/syslog_log_facility').with_value('LOG_USER') } + end + + describe 'with syslog enabled and custom settings' do + let :params do + default_params.merge({ + :use_syslog => 'true', + :log_facility => 'LOG_LOCAL0' + }) + end + + it { should contain_glance_registry_config('DEFAULT/use_syslog').with_value(true) } + it { should contain_glance_registry_config('DEFAULT/syslog_log_facility').with_value('LOG_LOCAL0') } + end + + describe 'with log_file enabled by default' do + let(:params) { default_params } + + it { should contain_glance_registry_config('DEFAULT/log_file').with_value(default_params[:log_file]) } + + context 'with log_file disabled' do + let(:params) { default_params.merge!({ :log_file => false }) } + it { should contain_glance_registry_config('DEFAULT/log_file').with_ensure('absent') } + end + end + + describe 'with log_dir enabled by default' do + let(:params) { default_params } + + it { should contain_glance_registry_config('DEFAULT/log_dir').with_value(default_params[:log_dir]) } + + context 'with log_dir disabled' do + let(:params) { default_params.merge!({ :log_dir => false }) } + it { should contain_glance_registry_config('DEFAULT/log_dir').with_ensure('absent') } + end + end + + describe 'with no ssl options (default)' do + let(:params) { default_params } + + it { should contain_glance_registry_config('DEFAULT/ca_file').with_ensure('absent')} + it { should contain_glance_registry_config('DEFAULT/cert_file').with_ensure('absent')} + it { should contain_glance_registry_config('DEFAULT/key_file').with_ensure('absent')} + end + + describe 'with ssl options' do + let :params do + default_params.merge({ + :ca_file => '/tmp/ca_file', + :cert_file => '/tmp/cert_file', + :key_file => '/tmp/key_file' + }) + end + + context 'with ssl options' do + it { should contain_glance_registry_config('DEFAULT/ca_file').with_value('/tmp/ca_file') } + it { should contain_glance_registry_config('DEFAULT/cert_file').with_value('/tmp/cert_file') } + it { should contain_glance_registry_config('DEFAULT/key_file').with_value('/tmp/key_file') } + end + end + +end diff --git a/glance/spec/classes/glance_spec.rb b/glance/spec/classes/glance_spec.rb new file mode 100644 index 000000000..fba8e5199 --- /dev/null +++ b/glance/spec/classes/glance_spec.rb @@ -0,0 +1,37 @@ +require 'spec_helper' + +describe 'glance' do + + let :facts do + { + :osfamily => 'Debian' + } + end + + let :default_params do + {:package_ensure => 'present'} + end + + [ + {}, + {:package_ensure => 'latest'} + ].each do |param_set| + + describe "when #{param_set == {} ? "using default" : "specifying"} class parameters" do + + let :param_hash do + param_set == {} ? default_params : params + end + + let :params do param_set end + + it { should contain_package('glance').with_ensure(param_hash[:package_ensure]) } + it { should contain_file('/etc/glance/').with( + 'ensure' => 'directory', + 'owner' => 'glance', + 'mode' => '0770', + 'require' => 'Package[glance]' + )} + end + end +end diff --git a/glance/spec/shared_examples.rb b/glance/spec/shared_examples.rb new file mode 100644 index 000000000..d92156a36 --- /dev/null +++ b/glance/spec/shared_examples.rb @@ -0,0 +1,5 @@ +shared_examples_for "a Puppet::Error" do |description| + it "with message matching #{description.inspect}" do + expect { should have_class_count(1) }.to raise_error(Puppet::Error, description) + end +end diff --git a/glance/spec/spec.opts b/glance/spec/spec.opts new file mode 100644 index 000000000..91cd6427e --- /dev/null +++ b/glance/spec/spec.opts @@ -0,0 +1,6 @@ +--format +s +--colour +--loadby +mtime +--backtrace diff --git a/glance/spec/spec_helper.rb b/glance/spec/spec_helper.rb new file mode 100644 index 000000000..076e2bb39 --- /dev/null +++ b/glance/spec/spec_helper.rb @@ -0,0 +1,7 @@ +require 'puppetlabs_spec_helper/module_spec_helper' +require 'shared_examples' + +RSpec.configure do |c| + c.alias_it_should_behave_like_to :it_configures, 'configures' + c.alias_it_should_behave_like_to :it_raises, 'raises' +end diff --git a/glance/spec/unit/provider/glance_spec.rb b/glance/spec/unit/provider/glance_spec.rb new file mode 100644 index 000000000..0834ce01a --- /dev/null +++ b/glance/spec/unit/provider/glance_spec.rb @@ -0,0 +1,59 @@ +require 'puppet' +require 'spec_helper' +require 'puppet/provider/glance' +require 'tempfile' + + +klass = Puppet::Provider::Glance + +describe Puppet::Provider::Glance do + + after :each do + klass.reset + end + + describe 'when retrieving the auth credentials' do + + it 'should fail if the glance config file does not have the expected contents' do + mock = {} + Puppet::Util::IniConfig::File.expects(:new).returns(mock) + mock.expects(:read).with('/etc/glance/glance-api.conf') + expect do + klass.glance_credentials + end.to raise_error(Puppet::Error, /does not contain all required sections/) + end + + describe 'when testing glance connection retries' do + + ['[Errno 111] Connection refused', '(HTTP 400)', 'HTTP Unable to establish connection'].reverse.each do |valid_message| + it "should retry when glance is not ready with error #{valid_message}" do + mock = {'keystone_authtoken' => + { + 'auth_host' => '127.0.0.1', + 'auth_port' => '35357', + 'auth_protocol' => 'http', + 'admin_tenant_name' => 'foo', + 'admin_user' => 'user', + 'admin_password' => 'pass' + } + } + Puppet::Util::IniConfig::File.expects(:new).returns(mock) + mock.expects(:read).with('/etc/glance/glance-api.conf') + klass.expects(:sleep).with(10).returns(nil) + klass.expects(:glance).twice.with( + '-T', + 'foo', + '-I', + 'user', + '-K', + 'pass', + '-N', + 'http://127.0.0.1:35357/v2.0/', + ['test_retries'] + ).raises(Exception, valid_message).then.returns('') + klass.auth_glance('test_retries') + end + end + end + end +end diff --git a/glance/tests/api.pp b/glance/tests/api.pp new file mode 100644 index 000000000..7b4931916 --- /dev/null +++ b/glance/tests/api.pp @@ -0,0 +1,4 @@ +class { 'glance::api': + debug => true, + verbose => true, +} diff --git a/glance/tests/init.pp b/glance/tests/init.pp new file mode 100644 index 000000000..57cf401a1 --- /dev/null +++ b/glance/tests/init.pp @@ -0,0 +1 @@ +class { 'glance': } diff --git a/glance/tests/registry.pp b/glance/tests/registry.pp new file mode 100644 index 000000000..8635cb267 --- /dev/null +++ b/glance/tests/registry.pp @@ -0,0 +1,4 @@ +class { 'glance::registry': + debug => true, + verbose => true, +} diff --git a/glance/tests/site.pp b/glance/tests/site.pp new file mode 100644 index 000000000..2859b72d7 --- /dev/null +++ b/glance/tests/site.pp @@ -0,0 +1,57 @@ + +# uses the keystone packages +# to ensure that we use the latest precise packages +Exec { logoutput => 'on_failure' } + +node glance_keystone_mysql { + class { 'mysql::server': } + class { 'keystone': + verbose => true, + debug => true, + catalog_type => 'sql', + admin_token => 'admin_token', + } + class { 'keystone::db::mysql': + password => 'keystone', + } + class { 'keystone::roles::admin': + email => 'test@puppetlabs.com', + password => 'ChangeMe', + } + class { 'glance::api': + verbose => true, + debug => true, + auth_type => 'keystone', + keystone_tenant => 'services', + keystone_user => 'glance', + keystone_password => 'glance_password', + sql_connection => 'mysql://glance:glance@127.0.0.1/glance', + } + class { 'glance::backend::file': } + + class { 'glance::db::mysql': + password => 'glance', + dbname => 'glance', + user => 'glance', + host => '127.0.0.1', + # allowed_hosts = undef, + # $cluster_id = 'localzone' + } + + class { 'glance::registry': + verbose => true, + debug => true, + auth_type => 'keystone', + keystone_tenant => 'services', + keystone_user => 'glance', + keystone_password => 'glance_password', + sql_connection => 'mysql://glance:glance@127.0.0.1/glance', + } + class { 'glance::keystone::auth': + password => 'glance_pass', + } +} + +node default { + fail("could not find a matching node entry for ${clientcert}") +} diff --git a/gluster/.gitignore b/gluster/.gitignore new file mode 100644 index 000000000..036d98633 --- /dev/null +++ b/gluster/.gitignore @@ -0,0 +1,8 @@ +old/ +tmp/ +pkg/ +docs/ +hacking/ +rpmbuild/ +screencasts/ +builder/versions/*.sh diff --git a/gluster/.gitmodules b/gluster/.gitmodules new file mode 100644 index 000000000..c41791b6c --- /dev/null +++ b/gluster/.gitmodules @@ -0,0 +1,21 @@ +[submodule "vagrant/gluster/puppet/modules/stdlib"] + path = vagrant/gluster/puppet/modules/stdlib + url = https://github.com/purpleidea/puppetlabs-stdlib.git +[submodule "vagrant/gluster/puppet/modules/apt"] + path = vagrant/gluster/puppet/modules/apt + url = https://github.com/purpleidea/puppetlabs-apt.git +[submodule "vagrant/gluster/puppet/modules/common"] + path = vagrant/gluster/puppet/modules/common + url = https://github.com/purpleidea/puppet-common.git +[submodule "vagrant/gluster/puppet/modules/keepalived"] + path = vagrant/gluster/puppet/modules/keepalived + url = https://github.com/purpleidea/puppet-keepalived.git +[submodule "vagrant/gluster/puppet/modules/puppet"] + path = vagrant/gluster/puppet/modules/puppet + url = https://github.com/purpleidea/puppet-puppet.git +[submodule "vagrant/gluster/puppet/modules/shorewall"] + path = vagrant/gluster/puppet/modules/shorewall + url = https://github.com/purpleidea/puppet-shorewall.git +[submodule "vagrant/gluster/puppet/modules/yum"] + path = vagrant/gluster/puppet/modules/yum + url = https://github.com/purpleidea/puppet-yum.git diff --git a/gluster/COPYING b/gluster/COPYING new file mode 100644 index 000000000..dba13ed2d --- /dev/null +++ b/gluster/COPYING @@ -0,0 +1,661 @@ + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU Affero General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU Affero General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU Affero General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU Affero General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU Affero General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU AGPL, see +. diff --git a/gluster/COPYRIGHT b/gluster/COPYRIGHT new file mode 100644 index 000000000..480149fb7 --- /dev/null +++ b/gluster/COPYRIGHT @@ -0,0 +1,16 @@ +Copyright (C) 2012-2013+ James Shubin +Written by James Shubin + +This puppet module is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This puppet module is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . + diff --git a/gluster/DOCUMENTATION.md b/gluster/DOCUMENTATION.md new file mode 100644 index 000000000..6f50993bc --- /dev/null +++ b/gluster/DOCUMENTATION.md @@ -0,0 +1,853 @@ +#Puppet-Gluster + +##A GlusterFS Puppet module by [James](https://ttboj.wordpress.com/) +####Available from: +####[https://github.com/purpleidea/puppet-gluster/](https://github.com/purpleidea/puppet-gluster/) + +####Also available from: +####[https://forge.gluster.org/puppet-gluster/](https://forge.gluster.org/puppet-gluster/) + +####This documentation is available in: [Markdown](https://github.com/purpleidea/puppet-gluster/blob/master/DOCUMENTATION.md) or [PDF](https://github.com/purpleidea/puppet-gluster/raw/master/puppet-gluster-documentation.pdf) format. + +####Table of Contents + +1. [Overview](#overview) +2. [Module description - What the module does](#module-description) +3. [Setup - Getting started with Puppet-Gluster](#setup) + * [What can Puppet-Gluster manage?](#what-can-puppet-gluster-manage) + * [Simple setup](#simple-setup) + * [Elastic setup](#elastic-setup) + * [Advanced setup](#advanced-setup) + * [Client setup](#client-setup) +4. [Usage/FAQ - Notes on management and frequently asked questions](#usage-and-frequently-asked-questions) +5. [Reference - Class and type reference](#reference) + * [gluster::simple](#glustersimple) + * [gluster::elastic](#glusterelastic) + * [gluster::server](#glusterserver) + * [gluster::host](#glusterhost) + * [gluster::brick](#glusterbrick) + * [gluster::volume](#glustervolume) + * [gluster::volume::property](#glustervolumeproperty) + * [gluster::mount](#glustermount) +6. [Examples - Example configurations](#examples) +7. [Limitations - Puppet versions, OS compatibility, etc...](#limitations) +8. [Development - Background on module development](#development) +9. [Author - Author and contact information](#author) + +##Overview + +The Puppet-Gluster module installs, configures, and manages a GlusterFS cluster. + +##Module Description + +This Puppet-Gluster module handles installation, configuration, and management +of GlusterFS across all of the hosts in the cluster. + +##Setup + +###What can Puppet-Gluster manage? + +Puppet-Gluster is designed to be able to manage as much or as little of your +GlusterFS cluster as you wish. All features are optional. If there is a feature +that doesn't appear to be optional, and you believe it should be, please let me +know. Having said that, it makes good sense to me to have Puppet-Gluster manage +as much of your GlusterFS infrastructure as it can. At the moment, it cannot +rack new servers, but I am accepting funding to explore this feature ;) At the +moment it can manage: + +* GlusterFS packages (rpm) +* GlusterFS configuration files (/var/lib/glusterd/) +* GlusterFS host peering (gluster peer probe) +* GlusterFS storage partitioning (fdisk) +* GlusterFS storage formatting (mkfs) +* GlusterFS brick creation (mkdir) +* GlusterFS services (glusterd) +* GlusterFS firewalling (whitelisting) +* GlusterFS volume creation (gluster volume create) +* GlusterFS volume state (started/stopped) +* GlusterFS volume properties (gluster volume set) +* And much more... + +###Simple setup + +include '::gluster::simple' is enough to get you up and running. When using the +gluster::simple class, or with any other Puppet-Gluster configuration, +identical definitions must be used on all hosts in the cluster. The simplest +way to accomplish this is with a single shared puppet host definition like: + +```puppet +node /^annex\d+$/ { # annex{1,2,..N} + class { '::gluster::simple': + } +} +``` + +If you wish to pass in different parameters, you can specify them in the class +before you provision your hosts: + +```puppet +class { '::gluster::simple': + replica => 2, + volume => ['volume1', 'volume2', 'volumeN'], +} +``` + +###Elastic setup + +The gluster::elastic class is not yet available. Stay tuned! + +###Advanced setup + +Some system administrators may wish to manually itemize each of the required +components for the Puppet-Gluster deployment. This happens automatically with +the higher level modules, but may still be a desirable feature, particularly +for non-elastic storage pools where the configuration isn't expected to change +very often (if ever). + +To put together your cluster piece by piece, you must manually include and +define each class and type that you wish to use. If there are certain aspects +that you wish to manage yourself, you can omit them from your configuration. +See the [reference](#reference) section below for the specifics. Here is one +possible example: + +```puppet +class { '::gluster::server': + shorewall => true, +} + +gluster::host { 'annex1.example.com': + # use uuidgen to make these + uuid => '1f660ca2-2c78-4aa0-8f4d-21608218c69c', +} + +# note that this is using a folder on your existing file system... +# this can be useful for prototyping gluster using virtual machines +# if this isn't a separate partition, remember that your root fs will +# run out of space when your gluster volume does! +gluster::brick { 'annex1.example.com:/data/gluster-storage1': + areyousure => true, +} + +gluster::host { 'annex2.example.com': + # NOTE: specifying a host uuid is now optional! + # if you don't choose one, one will be assigned + #uuid => '2fbe6e2f-f6bc-4c2d-a301-62fa90c459f8', +} + +gluster::brick { 'annex2.example.com:/data/gluster-storage2': + areyousure => true, +} + +$brick_list = [ + 'annex1.example.com:/data/gluster-storage1', + 'annex2.example.com:/data/gluster-storage2', +] + +gluster::volume { 'examplevol': + replica => 2, + bricks => $brick_list, + start => undef, # i'll start this myself +} + +# namevar must be: # +gluster::volume::property { 'examplevol#auth.reject': + value => ['192.0.2.13', '198.51.100.42', '203.0.113.69'], +} +``` + +###Client setup + +Mounting a GlusterFS volume on a client is fairly straightforward. Simply use +the 'gluster::mount' type. + +```puppet + gluster::mount { '/mnt/gluster/puppet/': + server => 'annex.example.com:/puppet', + rw => true, + shorewall => false, + } +``` + +In this example, 'annex.example.com' points to the VIP of the GlusterFS +cluster. Using the VIP for mounting increases the chance that you'll get an +available server when you try to mount. This generally works better than RRDNS +or similar schemes. + +##Usage and frequently asked questions + +All management should be done by manipulating the arguments on the appropriate +Puppet-Gluster classes and types. Since certain manipulations are either not +yet possible with Puppet-Gluster, or are not supported by GlusterFS, attempting +to manipulate the Puppet configuration in an unsupported way will result in +undefined behaviour, and possible even data loss, however this is unlikely. + +###How do I change the replica count? + +You must set this before volume creation. This is a limitation of GlusterFS. +There are certain situations where you can change the replica count by adding +a multiple of the existing brick count to get this desired effect. These cases +are not yet supported by Puppet-Gluster. If you want to use Puppet-Gluster +before and / or after this transition, you can do so, but you'll have to do the +changes manually. + +###Do I need to use a virtual IP? + +Using a virtual IP (VIP) is strongly recommended as a distributed lock manager +(DLM) and also to provide a highly-available (HA) IP address for your clients +to connect to. For a more detailed explanation of the reasoning please see: + +[How to avoid cluster race conditions or: How to implement a distributed lock manager in puppet](https://ttboj.wordpress.com/2012/08/23/how-to-avoid-cluster-race-conditions-or-how-to-implement-a-distributed-lock-manager-in-puppet/) + +Remember that even if you're using a hosted solution (such as AWS) that doesn't +provide an additional IP address, or you want to avoid using an additional IP, +and you're okay not having full HA client mounting, you can use an unused +private RFC1918 IP address as the DLM VIP. Remember that a layer 3 IP can +co-exist on the same layer 2 network with the layer 3 network that is used by +your cluster. + +###Is it possible to have Puppet-Gluster complete in a single run? + +No. This is a limitation of Puppet, and is related to how GlusterFS operates. +For example, it is not reliably possible to predict which ports a particular +GlusterFS volume will run on until after the volume is started. As a result, +this module will initially whitelist connections from GlusterFS host IP +addresses, and then further restrict this to only allow individual ports once +this information is known. This is possible in conjunction with the +[puppet-shorewall](https://github.com/purpleidea/puppet-shorewall) module. +You should notice that each run should complete without error. If you do see an +error, it means that either something is wrong with your system and / or +configuration, or because there is a bug in Puppet-Gluster. + +###Can you integrate this with vagrant? + +Yes, see the +[vagrant/](https://github.com/purpleidea/puppet-gluster/tree/master/vagrant) +directory. This has been tested on Fedora 20, with vagrant-libvirt, as I have +no desire to use VirtualBox for fun. I have written an article about this: + +[Automatically deploying GlusterFS with Puppet-Gluster + Vagrant!](https://ttboj.wordpress.com/2014/01/08/automatically-deploying-glusterfs-with-puppet-gluster-vagrant/) + +You'll probably first need to read my three earlier articles to learn some +vagrant tricks, and to get the needed dependencies installed: + +* [Vagrant on Fedora with libvirt](https://ttboj.wordpress.com/2013/12/09/vagrant-on-fedora-with-libvirt/) +* [Vagrant vsftp and other tricks](https://ttboj.wordpress.com/2013/12/21/vagrant-vsftp-and-other-tricks/) +* [Vagrant clustered SSH and ‘screen’](https://ttboj.wordpress.com/2014/01/02/vagrant-clustered-ssh-and-screen/) + +###Puppet runs fail with "Invalid relationship" errors. + +When running Puppet, you encounter a compilation failure like: + +```bash +Error: Could not retrieve catalog from remote server: +Error 400 on SERVER: Invalid relationship: Exec[gluster-volume-stuck-volname] { +require => Gluster::Brick[annex2.example.com:/var/lib/puppet/tmp/gluster/data/] +}, because Gluster::Brick[annex2.example.com:/var/lib/puppet/tmp/gluster/data/] +doesn't seem to be in the catalog +Warning: Not using cache on failed catalog +Error: Could not retrieve catalog; skipping run +``` + +This can occur if you have changed (usually removed) the available bricks, but +have not cleared the exported resources on the Puppet master, or if there are +stale (incorrect) brick "tags" on the individual host. These tags can usually +be found in the _/var/lib/puppet/tmp/gluster/brick/_ directory. In other words, +when a multi host cluster comes up, each puppet agent tells the master about +which bricks it has available, and each agent also pulls down this list and +stores it in the brick directory. If there is a discrepancy, then the compile +will fail because the individual host is using old data as part of its facts +when it uses the stale brick data as part of its compilation. + +This commonly happens if you're trying to deploy a different Puppet-Gluster +setup without having first erased the host specific exported resources on the +Puppet master or if the machine hasn't been re-provisioned from scratch. + +To solve this problem, do a clean install, and make sure that you've cleaned +the Puppet master with: + +```bash +puppet node deactivate HOSTNAME +``` + +for each host you're using, and that you've removed all of the files from the +brick directories on each host. + +###Provisioning fails with: "Can't open /dev/sdb1 exclusively." + +If when provisioning you get an error like: + +_"Can't open /dev/sdb1 exclusively. Mounted filesystem?"_ + +It is possible that dracut might have found an existing logical volume on the +device, and device mapper has made it available. This is common if you are +re-using dirty block devices that haven't run through a _dd_ first. Here is an +example of the diagnosis and treatment of this problem: + +```bash +[root@server mapper]# pwd +/dev/mapper +[root@server mapper]# dmesg | grep dracut +dracut: dracut-004-336.el6_5.2 +dracut: rd_NO_LUKS: removing cryptoluks activation +dracut: Starting plymouth daemon +dracut: rd_NO_DM: removing DM RAID activation +dracut: rd_NO_MD: removing MD RAID activation +dracut: Scanning devices sda3 sdb for LVM logical volumes myvg/rootvol +dracut: inactive '/dev/vg_foo/lv' [4.35 TiB] inherit +dracut: inactive '/dev/myvg/rootvol' [464.00 GiB] inherit +dracut: Mounted root filesystem /dev/mapper/myvg-rootvol +dracut: Loading SELinux policy +dracut: +dracut: Switching root +[root@server mapper]# /sbin/pvcreate --dataalignment 2560K /dev/sdb1 + Can't open /dev/sdb1 exclusively. Mounted filesystem? +[root@server mapper]# ls +control myvg-rootvol vg_foo-lv +[root@server mapper]# ls -lAh +total 0 +crw-rw----. 1 root root 10, 58 Mar 7 16:42 control +lrwxrwxrwx. 1 root root 7 Mar 13 09:56 myvg-rootvol -> ../dm-0 +lrwxrwxrwx. 1 root root 7 Mar 13 09:56 vg_foo-lv -> ../dm-1 +[root@server mapper]# dmsetup remove vg_foo-lv +[root@server mapper]# ls +control myvg-rootvol +[root@server mapper]# pvcreate --dataalignment 2560K /dev/sdb1 + Physical volume "/dev/sdb1" successfully created +[root@server mapper]# HAPPY_ADMIN='yes' +``` + +If you frequently start with "dirty" block devices, you may consider adding a +_dd_ to your hardware provisioning step. The downside is that this can be very +time consuming, and potentially dangerous if you accidentally re-provision the +wrong machine. + +###I changed the hardware manually, and now my system won't boot. + +If you're using Puppet-Gluster to manage storage, the filesystem will be +mounted with _UUID_ entries in _/etc/fstab_. This ensures that the correct +filesystem will be mounted, even if the device order changes. If a filesystem +is not available at boot time, startup will abort and offer you the chance to +go into read-only maintenance mode. Either fix the hardware issue, or edit the +_/etc/fstab_ file. + + +###I can't edit /etc/fstab in the maintenance shell because it is read-only. + +In the maintenance shell, your root filesystem will be mounted read-only, to +prevent changes. If you need to edit a file such as _/etc/fstab_, you'll first +need to remount the root filesystem in read-write mode. You can do this with: + +```bash +mount -n -o remount / +``` + +###Awesome work, but it's missing support for a feature and/or platform! + +Since this is an Open Source / Free Software project that I also give away for +free (as in beer, free as in gratis, free as in libre), I'm unable to provide +unlimited support. Please consider donating funds, hardware, virtual machines, +and other resources. For specific needs, you could perhaps sponsor a feature! + +###You didn't answer my question, or I have a question! + +Contact me through my [technical blog](https://ttboj.wordpress.com/contact/) +and I'll do my best to help. If you have a good question, please remind me to +add my answer to this documentation! + +##Reference +Please note that there are a number of undocumented options. For more +information on these options, please view the source at: +[https://github.com/purpleidea/puppet-gluster/](https://github.com/purpleidea/puppet-gluster/). +If you feel that a well used option needs documenting here, please contact me. + +###Overview of classes and types + +* [gluster::simple](#glustersimple): Simple Puppet-Gluster deployment. +* [gluster::elastic](#glusterelastic): Under construction. +* [gluster::server](#glusterserver): Base class for server hosts. +* [gluster::host](#glusterhost): Host type for each participating host. +* [gluster::brick](#glusterbrick): Brick type for each defined brick, per host. +* [gluster::volume](#glustervolume): Volume type for each defined volume. +* [gluster::volume::property](#glustervolumeproperty): Manages properties for each volume. +* [gluster::mount](#glustermount): Client volume mount point management. + +###gluster::simple +This is gluster::simple. It should probably take care of 80% of all use cases. +It is particularly useful for deploying quick test clusters. It uses a +finite-state machine (FSM) to decide when the cluster has settled and volume +creation can begin. For more information on the FSM in Puppet-Gluster see: +[https://ttboj.wordpress.com/2013/09/28/finite-state-machines-in-puppet/](https://ttboj.wordpress.com/2013/09/28/finite-state-machines-in-puppet/) + +####`replica` +The replica count. Can't be changed automatically after initial deployment. + +####`volume` +The volume name or list of volume names to create. + +####`path` +The valid brick path for each host. Defaults to local file system. If you need +a different path per host, then Gluster::Simple will not meet your needs. + +####`count` +Number of bricks to build per host. This value is used unless _brick_params_ is +being used. + +####`vip` +The virtual IP address to be used for the cluster distributed lock manager. +This option can be used in conjunction with the _vrrp_ option, but it does not +require it. If you don't want to provide a virtual ip, but you do want to +enforce that certain operations only run on one host, then you can set this +option to be the ip address of an arbitrary host in your cluster. Keep in mind +that if that host is down, certain options won't ever occur. + +####`vrrp` +Whether to automatically deploy and manage _Keepalived_ for use as a _DLM_ and +for use in volume mounting, etc... Using this option requires the _vip_ option. + +####`layout` +Which brick layout to use. The available options are: _chained_, and (default). +To generate a default (symmetrical, balanced) layout, leave this option blank. +If you'd like to include an algorithm that generates a different type of brick +layout, it is easy to drop in an algorithm. Please contact me with the details! + +####`version` +Which version of GlusterFS do you want to install? This is especially handy +when testing new beta releases. You can read more about the technique at: +[Testing GlusterFS during Glusterfest](https://ttboj.wordpress.com/2014/01/16/testing-glusterfs-during-glusterfest/). + +####`repo` +Whether or not to add the necessary software repositories to install the needed +packages. This will typically pull in GlusterFS from _download.gluster.org_ and +should be set to false if you have your own mirrors or repositories managed as +part of your base image. + +####`brick_params` +This parameter lets you specify a hash to use when creating the individual +bricks. This is especially useful because it lets you have the power of +Gluster::Simple when managing a cluster of iron (physical machines) where you'd +like to specify brick specific parameters. This sets the brick count when the +_count_ parameter is 0. The format of this parameter might look like: + +```bash +$brick_params = { + fqdn1 => [ + {dev => '/dev/disk/by-uuid/01234567-89ab-cdef-0123-456789abcdef'}, + {dev => '/dev/sdc', partition => false}, + ], + fqdn2 => [{ + dev => '/dev/disk/by-path/pci-0000:02:00.0-scsi-0:1:0:0', + raid_su => 256, raid_sw => 10, + }], + fqdnN => [...], +} +``` + +####`brick_param_defaults` +This parameter lets you specify a hash of defaults to use when creating each +brick with the _brick_params_ parameter. It is useful because it avoids the +need to repeat the values that are common across all bricks in your cluster. +Since most options work this way, this is an especially nice feature to have. +The format of this parameter might look like: + +```bash +$brick_param_defaults = { + lvm => false, + xfs_inode64 => true, + force => true, +} +``` + +####`brick_params_defaults` +This parameter lets you specify a list of defaults to use when creating each +brick. Each element in the list represents a different brick. The value of each +element is a hash with the actual defaults that you'd like to use for creating +that brick. If you do not specify a brick count by any other method, then the +number of elements in this array will be used as the brick count. This is very +useful if you have consistent device naming across your entire cluster, because +you can very easily specify the devices and brick counts once for all hosts. If +for some reason a particular device requires unique values, then it can be set +manually with the _brick_params_ parameter. Please note the spelling of this +parameter. It is not the same as the _brick_param_defaults_ parameter which is +a global defaults parameter which will apply to all bricks. +The format of this parameter might look like: + +```bash +$brick_params_defaults = [ + {'dev' => '/dev/sdb'}, + {'dev' => '/dev/sdc'}, + {'dev' => '/dev/sdd'}, + {'dev' => '/dev/sde'}, +] +``` + +####`setgroup` +Set a volume property group. The two most common or well-known groups are the +_virt_ group, and the _small-file-perf_ group. This functionality is emulated +whether you're using the RHS version of GlusterFS or if you're using the +upstream GlusterFS project, which doesn't (currently) have the _volume set +group_ command. As package managers update the list of available groups or +their properties, Puppet-Gluster will automatically keep your set group +up-to-date. It is easy to extend Puppet-Gluster to add a custom group without +needing to patch the GlusterFS source. + +####`ping` +Whether to use _fping_ or not to help with ensuring the required hosts are +available before doing certain types of operations. Optional, but recommended. +Boolean value. + +####`again` +Do you want to use _Exec['again']_ ? This helps build your cluster quickly! + +####`baseport` +Specify the base port option as used in the glusterd.vol file. This is useful +if the default port range of GlusterFS conflicts with the ports used for +virtual machine migration, or if you simply like to choose the ports that +you're using. Integer value. + +####`rpcauthallowinsecure` +This is needed in some setups in the glusterd.vol file, particularly (I think) +for some users of _libgfapi_. Boolean value. + +####`shorewall` +Boolean to specify whether puppet-shorewall integration should be used or not. + +###gluster::elastic +Under construction. + +###gluster::server +Main server class for the cluster. Must be included when building the GlusterFS +cluster manually. Wrapper classes such as [gluster::simple](#glustersimple) +include this automatically. + +####`vip` +The virtual IP address to be used for the cluster distributed lock manager. + +####`shorewall` +Boolean to specify whether puppet-shorewall integration should be used or not. + +###gluster::host +Main host type for the cluster. Each host participating in the GlusterFS +cluster must define this type on itself, and on every other host. As a result, +this is not a singleton like the [gluster::server](#glusterserver) class. + +####`ip` +Specify which IP address this host is using. This defaults to the +_$::ipaddress_ variable. Be sure to set this manually if you're declaring this +yourself on each host without using exported resources. If each host thinks the +other hosts should have the same IP address as itself, then Puppet-Gluster and +GlusterFS won't work correctly. + +####`uuid` +Universally unique identifier (UUID) for the host. If empty, Puppet-Gluster +will generate this automatically for the host. You can generate your own +manually with _uuidgen_, and set them yourself. I found this particularly +useful for testing, because I would pick easy to recognize UUID's like: +_aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa_, +_bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb_, and so on. If you set a UUID manually, +and Puppet-Gluster has a chance to run, then it will remember your choice, and +store it locally to be used again if you no longer specify the UUID. This is +particularly useful for upgrading an existing un-managed GlusterFS installation +to a Puppet-Gluster managed one, without changing any UUID's. + +###gluster::brick +Main brick type for the cluster. Each brick is an individual storage segment to +be used on a host. Each host must have at least one brick to participate in the +cluster, but usually a host will have multiple bricks. A brick can be as simple +as a file system folder, or it can be a separate file system. Please read the +official GlusterFS documentation, if you aren't entirely comfortable with the +concept of a brick. + +For most test clusters, and for experimentation, it is easiest to use a +directory on the root file system. You can even use a _/tmp_ sub folder if you +don't care about the persistence of your data. For more serious clusters, you +might want to create separate file systems for your data. On self-hosted iron, +it is not uncommon to create multiple RAID-6 drive pools, and to then create a +separate file system per virtual drive. Each file system can then be used as a +single brick. + +So that each volume in GlusterFS has the maximum ability to grow, without +having to partition storage separately, the bricks in Puppet-Gluster are +actually folders (on whatever backing store you wish) which then contain +sub folders-- one for each volume. As a result, all the volumes on a given +GlusterFS cluster can share the total available storage space. If you wish to +limit the storage used by each volume, you can setup quotas. Alternatively, you +can buy more hardware, and elastically grow your GlusterFS volumes, since the +price per GB will be significantly less than any proprietary storage system. +The one downside to this brick sharing, is that if you have chosen the brick +per host count specifically to match your performance requirements, and +each GlusterFS volume on the same cluster has drastically different brick per +host performance requirements, then this won't suit your needs. I doubt that +anyone actually has such requirements, but if you do insist on needing this +compartmentalization, then you can probably use the Puppet-Gluster grouping +feature to accomplish this goal. Please let me know about your use-case, and +be warned that the grouping feature hasn't been extensively tested. + +To prove to you that I care about automation, this type offers the ability to +automatically partition and format your file systems. This means you can plug +in new iron, boot, provision and configure the entire system automatically. +Regrettably, I don't have a lot of test hardware to routinely use this feature. +If you'd like to donate some, I'd be happy to test this thoroughly. Having said +that, I have used this feature, I consider it to be extremely safe, and it has +never caused me to lose data. If you're uncertain, feel free to look at the +code, or avoid using this feature entirely. If you think there's a way to make +it even safer, then feel free to let me know. + +####`dev` +Block device, such as _/dev/sdc_ or _/dev/disk/by-id/scsi-0123456789abcdef_. By +default, Puppet-Gluster will assume you're using a folder to store the brick +data, if you don't specify this parameter. + +####`raid_su` +Get this information from your RAID device. This is used to do automatic +calculations for alignment, so that the: + +``` + dev -> part -> lvm -> fs +``` + +stack is aligned properly. Future work is possible to manage your RAID devices, +and to read these values automatically. Specify this value as an integer number +of kilobytes (k). + +####`raid_sw` +Get this information from your RAID device. This is used to do automatic +calculations for alignment, so that the: + +``` + dev -> part -> lvm -> fs +``` + +stack is aligned properly. Future work is possible to manage your RAID devices, +and to read these values automatically. Specify this value as an integer. + +####`partition` +Do you want to partition the device and build the next layer on that partition, +or do you want to build on the block device directly? The "next layer" will +typically be lvm if you're using lvm, or your file system (such as xfs) if +you're skipping the lvm layer. + +####`labeltype` +Only _gpt_ is supported. Other options include _msdos_, but this has never been +used because of it's size limitations. + +####`lvm` +Do you want to use lvm on the lower level device (typically a partition, or the +device itself), or not. Using lvm might be required when using a commercially +supported GlusterFS solution. + +####`lvm_thinp` +Set to _true_ to enable LVM thin provisioning. Read 'man 7 lvmthin' to +understand what thin provisioning is all about. This is needed for one form of +GlusterFS snapshots. Obviously this requires that you also enable _LVM_. + +####`lvm_virtsize` +The value that will be passed to _--virtualsize_. By default this will pass in +a command that will return the size of your volume group. This is usually a +sane value, and help you to remember not to overcommit. + +####`lvm_chunksize` +Value of _--chunksize_ for _lvcreate_ when using thin provisioning. + +####`lvm_metadatasize` +Value of _--poolmetadatasize_ for _lvcreate_ when using thin provisioning. + +####`fsuuid` +File system UUID. This ensures we can distinctly identify a file system. You +can set this to be used with automatic file system creation, or you can specify +the file system UUID that you'd like to use. If you leave this blank, then +Puppet-Gluster can automatically pick an fs UUID for you. This is especially +useful if you are automatically deploying a large cluster on physical iron. + +####`fstype` +This should be _xfs_ or _ext4_. Using _xfs_ is recommended, but _ext4_ is also +quite common. This only affects a file system that is getting created by this +module. If you provision a new machine, with a root file system of _ext4_, and +the brick you create is a root file system path, then this option does nothing. + +####`xfs_inode64` +Set _inode64_ mount option when using the _xfs_ fstype. Choose _true_ to set. + +####`xfs_nobarrier` +Set _nobarrier_ mount option when using the _xfs_ fstype. Choose _true_ to set. + +####`ro` +Whether the file system should be mounted read only. For emergencies only. + +####`force` +If _true_, this will overwrite any xfs file system it sees. This is useful for +rebuilding GlusterFS repeatedly and wiping data. There are other safeties in +place to stop this. In general, you probably don't ever want to touch this. + +####`areyousure` +Do you want to allow Puppet-Gluster to do dangerous things? You have to set +this to _true_ to allow Puppet-Gluster to _fdisk_ and _mkfs_ your file system. + +####`again` +Do you want to use _Exec['again']_ ? This helps build your cluster quickly! + +####`comment` +Add any comment you want. This is also occasionally used internally to do magic +things. + +###gluster::volume +Main volume type for the cluster. This is where a lot of the magic happens. +Remember that changing some of these parameters after the volume has been +created won't work, and you'll experience undefined behaviour. There could be +FSM based error checking to verify that no changes occur, but it has been left +out so that this code base can eventually support such changes, and so that the +user can manually change a parameter if they know that it is safe to do so. + +####`bricks` +List of bricks to use for this volume. If this is left at the default value of +_true_, then this list is built automatically. The algorithm that determines +this order does not support all possible situations, and most likely can't +handle certain corner cases. It is possible to examine the FSM to view the +selected brick order before it has a chance to create the volume. The volume +creation script won't run until there is a stable brick list as seen by the FSM +running on the host that has the DLM. If you specify this list of bricks +manually, you must choose the order to match your desired volume layout. If you +aren't sure about how to order the bricks, you should review the GlusterFS +documentation first. + +####`transport` +Only _tcp_ is supported. Possible values can include _rdma_, but this won't get +any testing if I don't have access to infiniband hardware. Donations welcome. + +####`replica` +Replica count. Usually you'll want to set this to _2_. Some users choose _3_. +Other values are seldom seen. A value of _1_ can be used for simply testing a +distributed setup, when you don't care about your data or high availability. A +value greater than _4_ is probably wasteful and unnecessary. It might even +cause performance issues if a synchronous write is waiting on a slow fourth +server. + +####`stripe` +Stripe count. Thoroughly unsupported and untested option. Not recommended for +use by GlusterFS. + +####`layout` +Which brick layout to use. The available options are: _chained_, and (default). +To generate a default (symmetrical, balanced) layout, leave this option blank. +If you'd like to include an algorithm that generates a different type of brick +layout, it is easy to drop in an algorithm. Please contact me with the details! + +####`ping` +Do we want to include ping checks with _fping_? + +####`settle` +Do we want to run settle checks? + +####`again` +Do you want to use _Exec['again']_ ? This helps build your cluster quickly! + +####`start` +Requested state for the volume. Valid values include: _true_ (start), _false_ +(stop), or _undef_ (un-managed start/stop state). + +###gluster::volume::property +Main volume property type for the cluster. This allows you to manage GlusterFS +volume specific properties. There are a wide range of properties that volumes +support. For the full list of properties, you should consult the GlusterFS +documentation, or run the _gluster volume set help_ command. To set a property +you must use the special name pattern of: _volume_#_key_. The value argument is +used to set the associated value. It is smart enough to accept values in the +most logical format for that specific property. Some properties aren't yet +supported, so please report any problems you have with this functionality. +Because this feature is an awesome way to _document as code_ the volume +specific optimizations that you've made, make sure you use this feature even if +you don't use all the others. + +####`value` +The value to be used for this volume property. + +###gluster::mount +Main type to use to mount GlusterFS volumes. This type offers special features, +like shorewall integration, and repo support. + +####`server` +Server specification to use when mounting. Format is _:/volume_. You +may use an _FQDN_ or an _IP address_ to specify the server. + +####`rw` +Mount read-write or read-only. Defaults to read-only. Specify _true_ for +read-write. + +####`mounted` +Mounted argument from standard mount type. Defaults to _true_ (_mounted_). + +####`repo` +Boolean to select if you want automatic repository (package) management or not. + +####`version` +Specify which GlusterFS version you'd like to use. + +####`ip` +IP address of this client. This is usually auto-detected, but you can choose +your own value manually in case there are multiple options available. + +####`shorewall` +Boolean to specify whether puppet-shorewall integration should be used or not. + +##Examples +For example configurations, please consult the [examples/](https://github.com/purpleidea/puppet-gluster/tree/master/examples) directory in the git +source repository. It is available from: + +[https://github.com/purpleidea/puppet-gluster/tree/master/examples](https://github.com/purpleidea/puppet-gluster/tree/master/examples) + +It is also available from: + +[https://forge.gluster.org/puppet-gluster/puppet-gluster/trees/master/examples](https://forge.gluster.org/puppet-gluster/puppet-gluster/trees/master/examples/) + +##Limitations + +This module has been tested against open source Puppet 3.2.4 and higher. + +The module has been tested on: + +* CentOS 6.4/6.5 + +It will probably work without incident or without major modification on: + +* CentOS 5.x/6.x +* RHEL 5.x/6.x + +It will most likely work with other Puppet versions and on other platforms, but +testing under other conditions has been light due to lack of resources. It will +most likely not work on Debian/Ubuntu systems without modification. I would +really love to add support for these operating systems, but I do not have any +test resources to do so. Please sponsor this if you'd like to see it happen. + +##Development + +This is my personal project that I work on in my free time. +Donations of funding, hardware, virtual machines, and other resources are +appreciated. Please contact me if you'd like to sponsor a feature, invite me to +talk/teach or for consulting. + +You can follow along [on my technical blog](https://ttboj.wordpress.com/). + +##Author + +Copyright (C) 2010-2013+ James Shubin + +* [github](https://github.com/purpleidea/) +* [@purpleidea](https://twitter.com/#!/purpleidea) +* [https://ttboj.wordpress.com/](https://ttboj.wordpress.com/) + diff --git a/gluster/INSTALL b/gluster/INSTALL new file mode 100644 index 000000000..f497841f7 --- /dev/null +++ b/gluster/INSTALL @@ -0,0 +1,18 @@ +To install this puppet module, copy this folder to your puppet modulepath. + +You can usually find out where this is by running: + +$ puppet config print modulepath + +on your puppetmaster. In my case, this contains the directory: + +/etc/puppet/modules/ + +I keep all of my puppet modules in git managed directories named: + +puppet- + +You must remove the 'puppet-' prefix from the directory name for it to work! + +Happy hacking! + diff --git a/gluster/Makefile b/gluster/Makefile new file mode 100644 index 000000000..bb98acf00 --- /dev/null +++ b/gluster/Makefile @@ -0,0 +1,146 @@ +# GlusterFS module by James +# Copyright (C) 2010-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +.PHONY: all docs rpm srpm spec tar upload upload-sources upload-srpms upload-rpms +.SILENT: + +# version of the program +VERSION := $(shell cat VERSION) +RELEASE = 1 +SPEC = rpmbuild/SPECS/puppet-gluster.spec +SOURCE = rpmbuild/SOURCES/puppet-gluster-$(VERSION).tar.bz2 +SRPM = rpmbuild/SRPMS/puppet-gluster-$(VERSION)-$(RELEASE).src.rpm +RPM = rpmbuild/RPMS/puppet-gluster-$(VERSION)-$(RELEASE).rpm +SERVER = 'download.gluster.org' +REMOTE_PATH = 'purpleidea/puppet-gluster' + +all: docs rpm + +docs: puppet-gluster-documentation.pdf + +puppet-gluster-documentation.pdf: DOCUMENTATION.md + pandoc DOCUMENTATION.md -o 'puppet-gluster-documentation.pdf' + +# +# aliases +# +# TODO: does making an rpm depend on making a .srpm first ? +rpm: $(SRPM) $(RPM) + # do nothing + +srpm: $(SRPM) + # do nothing + +spec: $(SPEC) + # do nothing + +tar: $(SOURCE) + # do nothing + +upload: upload-sources upload-srpms upload-rpms + # do nothing + +# +# rpmbuild +# +$(RPM): $(SPEC) $(SOURCE) + @echo Running rpmbuild -bb... + rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bb $(SPEC) && \ + mv rpmbuild/RPMS/noarch/puppet-gluster-$(VERSION)-$(RELEASE).*.rpm $(RPM) + +$(SRPM): $(SPEC) $(SOURCE) + @echo Running rpmbuild -bs... + rpmbuild --define '_topdir $(shell pwd)/rpmbuild' -bs $(SPEC) + # renaming is not needed because we aren't using the dist variable + #mv rpmbuild/SRPMS/puppet-gluster-$(VERSION)-$(RELEASE).*.src.rpm $(SRPM) + +# +# spec +# +$(SPEC): rpmbuild/ puppet-gluster.spec.in + @echo Running templater... + #cat puppet-gluster.spec.in > $(SPEC) + sed -e s/__VERSION__/$(VERSION)/ -e s/__RELEASE__/$(RELEASE)/ < puppet-gluster.spec.in > $(SPEC) + # append a changelog to the .spec file + git log --format="* %cd %aN <%aE>%n- (%h) %s%d%n" --date=local | sed -r 's/[0-9]+:[0-9]+:[0-9]+ //' >> $(SPEC) + +# +# archive +# +$(SOURCE): rpmbuild/ + @echo Running git archive... + # use HEAD if tag doesn't exist yet, so that development is easier... + git archive --prefix=puppet-gluster-$(VERSION)/ -o $(SOURCE) $(VERSION) 2> /dev/null || (echo 'Warning: $(VERSION) does not exist.' && git archive --prefix=puppet-gluster-$(VERSION)/ -o $(SOURCE) HEAD) + +# TODO: ensure that each sub directory exists +rpmbuild/: + mkdir -p rpmbuild/{BUILD,BUILDROOT,RPMS,SOURCES,SPECS,SRPMS} + +# +# sha256sum +# +rpmbuild/SOURCES/SHA256SUMS: rpmbuild/SOURCES/ $(SOURCE) + @echo Running SOURCES sha256sum... + cd rpmbuild/SOURCES/ && sha256sum *.tar.bz2 > SHA256SUMS; cd - + +rpmbuild/SRPMS/SHA256SUMS: rpmbuild/SRPMS/ $(SRPM) + @echo Running SRPMS sha256sum... + cd rpmbuild/SRPMS/ && sha256sum *src.rpm > SHA256SUMS; cd - + +rpmbuild/RPMS/SHA256SUMS: rpmbuild/RPMS/ $(RPM) + @echo Running RPMS sha256sum... + cd rpmbuild/RPMS/ && sha256sum *.rpm > SHA256SUMS; cd - + +# +# gpg +# +rpmbuild/SOURCES/SHA256SUMS.asc: rpmbuild/SOURCES/SHA256SUMS + @echo Running SOURCES gpg... + # the --yes forces an overwrite of the SHA256SUMS.asc if necessary + gpg2 --yes --clearsign rpmbuild/SOURCES/SHA256SUMS + +rpmbuild/SRPMS/SHA256SUMS.asc: rpmbuild/SRPMS/SHA256SUMS + @echo Running SRPMS gpg... + gpg2 --yes --clearsign rpmbuild/SRPMS/SHA256SUMS + +rpmbuild/RPMS/SHA256SUMS.asc: rpmbuild/RPMS/SHA256SUMS + @echo Running RPMS gpg... + gpg2 --yes --clearsign rpmbuild/RPMS/SHA256SUMS + +# +# upload +# +# upload to public server +upload-sources: rpmbuild/SOURCES/ rpmbuild/SOURCES/SHA256SUMS rpmbuild/SOURCES/SHA256SUMS.asc + if [ "`cat rpmbuild/SOURCES/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SOURCES/ && cat SHA256SUMS'`" ]; then \ + echo Running SOURCES upload...; \ + rsync -avz rpmbuild/SOURCES/ $(SERVER):$(REMOTE_PATH)/SOURCES/; \ + fi + +upload-srpms: rpmbuild/SRPMS/ rpmbuild/SRPMS/SHA256SUMS rpmbuild/SRPMS/SHA256SUMS.asc + if [ "`cat rpmbuild/SRPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/SRPMS/ && cat SHA256SUMS'`" ]; then \ + echo Running SRPMS upload...; \ + rsync -avz rpmbuild/SRPMS/ $(SERVER):$(REMOTE_PATH)/SRPMS/; \ + fi + +upload-rpms: rpmbuild/RPMS/ rpmbuild/RPMS/SHA256SUMS rpmbuild/RPMS/SHA256SUMS.asc + if [ "`cat rpmbuild/RPMS/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/RPMS/ && cat SHA256SUMS'`" ]; then \ + echo Running RPMS upload...; \ + rsync -avz --prune-empty-dirs rpmbuild/RPMS/ $(SERVER):$(REMOTE_PATH)/RPMS/; \ + fi + +# vim: ts=8 diff --git a/gluster/Modulefile b/gluster/Modulefile new file mode 100644 index 000000000..3f4ada734 --- /dev/null +++ b/gluster/Modulefile @@ -0,0 +1,12 @@ +name 'purpleidea-gluster' +author 'James Shubin' +license 'GNU Affero General Public License, Version 3.0+' +summary 'GlusterFS module by James' +description 'A Puppet module for GlusterFS' +project_page 'https://github.com/purpleidea/puppet-gluster/' +source 'https://ttboj.wordpress.com/' +version '0.0.1' + +## Add dependencies, if any: +# dependency 'username/name', '>= 1.2.0' + diff --git a/gluster/README b/gluster/README new file mode 100644 index 000000000..6fe966f75 --- /dev/null +++ b/gluster/README @@ -0,0 +1,33 @@ +This is puppet-gluster a puppet module for gluster. + +Please read the INSTALL file for instructions on getting this installed. +Look in the examples/ folder for usage. If none exist, please contribute one! +This code may be a work in progress. The interfaces may change without notice. +Patches are welcome, but please be patient. They are best received by email. +Please ping me if you have big changes in mind, before you write a giant patch. + +Module specific notes: +* This is _the_ puppet module for gluster. Accept no imitations! +* All the participating nodes, need to have an identical puppet-gluster config. +* Using gluster::simple is probably the best way to try this out. +* This is easily deployed with vagrant. See the vagrant/ directory! +* You can use less of the available resources, if you only want to manage some. +* You can get CentOS and RHEL rpms from: + * http://download.gluster.org/pub/gluster/glusterfs/LATEST/CentOS/ or: + * http://repos.fedorapeople.org/repos/kkeithle/glusterfs/epel-6/x86_64/ +* Documentation is now available! Please report grammar and spelling bugs. + +Dependencies: +* puppetlabs-stdlib (required) +* my puppet-common module (optional) +* my puppet-shorewall module (optional) +* my puppet-keepalived module (optional) +* my puppet-puppet module (optional) +* my puppet-yum module (optional) +* gluster packages (see above notes) +* pandoc (for building a pdf of the documentation) + + +Happy hacking, +James Shubin , https://ttboj.wordpress.com/ + diff --git a/gluster/README.md b/gluster/README.md new file mode 120000 index 000000000..fc84f4f88 --- /dev/null +++ b/gluster/README.md @@ -0,0 +1 @@ +DOCUMENTATION.md \ No newline at end of file diff --git a/gluster/VERSION b/gluster/VERSION new file mode 100644 index 000000000..4e379d2bf --- /dev/null +++ b/gluster/VERSION @@ -0,0 +1 @@ +0.0.2 diff --git a/gluster/builder/Makefile b/gluster/builder/Makefile new file mode 100644 index 000000000..8c4ab0f42 --- /dev/null +++ b/gluster/builder/Makefile @@ -0,0 +1,148 @@ +# Makefile for building Vagrant (libvirt) base image "boxes" for Puppet-Gluster +# Copyright (C) 2010-2013+ James Shubin +# Written by James Shubin +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . + +# NOTE: if you change any of the values in this file (such as SIZE or --install +# arguments) make won't notice the change, you'll have to manually clean first. + +.PHONY: all builder convert box local upload clean +.SILENT: + +# TODO: build base image for virt-builder from iso instead of using templates + +# virt-builder os-version +VERSION = centos-6 +BOX = $(VERSION).box +SIZE = 40 +#OUTPUT = /tmp/gluster +#OUTPUT := $(shell pwd) +OUTPUT := $(shell echo ~/tmp/builder/$(VERSION)) +SERVER = 'user@host.example.org' +REMOTE_PATH = 'public_html/vagrant' + +all: box + +# +# aliases +# +builder: $(OUTPUT)/builder.img +convert: $(OUTPUT)/box.img +box: $(OUTPUT)/$(BOX) +local: $(OUTPUT)/SHA256SUMS.asc + +# +# clean +# +# delete created files +clean: + @echo Running clean... + # TODO: technically, the 'true' should check if all the files are rm-ed + rm $(OUTPUT)/{{builder,box}.img,metadata.json,$(BOX),SHA256SUMS{,.asc}} || true + +# +# virt-builder +# +# build image with virt-builder +# NOTE: some of this system prep is based on the vagrant-libvirt scripts +# TODO: install: ruby ruby-devel make gcc rubygems ? +$(OUTPUT)/builder.img: files/* + @echo Running virt-builder... + [ -d $(OUTPUT) ] || mkdir -p $(OUTPUT)/ # ensure path is present first! + virt-builder $(VERSION) \ + --output $(OUTPUT)/builder.img \ + --format qcow2 \ + --size $(SIZE)G \ + --install rsync,nfs-utils,sudo,openssh-server,openssh-clients \ + --install screen,vim-enhanced,git,wget,file,man,tree,nmap,tcpdump,htop,lsof,telnet,mlocate,bind-utils,koan,iftop,yum-utils,nc \ + --root-password file:files/password \ + --upload files/epel-release-6-8.noarch.rpm:/root/epel-release-6-8.noarch.rpm \ + --upload files/puppetlabs-release-el-6.noarch.rpm:/root/puppetlabs-release-el-6.noarch.rpm \ + --run-command 'yum install -y /root/epel-release-6-8.noarch.rpm && rm -f /root/epel-release-6-8.noarch.rpm' \ + --run-command 'yum install -y bash-completion moreutils' \ + --run-command 'yum install -y /root/puppetlabs-release-el-6.noarch.rpm && rm -f /root/puppetlabs-release-el-6.noarch.rpm' \ + --run-command 'yum install -y puppet' \ + --run-command 'yum update -y' \ + --run files/user.sh \ + --run files/ssh.sh \ + --run files/network.sh \ + --run files/cleanup.sh + + # boot machine once to run the selinux relabelling, see: + # https://www.redhat.com/archives/libguestfs/2014-January/msg00183.html + # https://github.com/libguestfs/libguestfs/commit/20a4bfde9628cfeb8bea441cab7dcc94843b34e3 + qemu-system-x86_64 -machine accel=kvm:tcg -cpu host -m 512 -drive file=$(OUTPUT)/builder.img,format=qcow2,if=virtio -no-reboot -serial stdio -nographic || (rm $(OUTPUT)/builder.img; false) + reset # TODO: qemu-system-x86_64 borks the terminal :( + +# +# convert +# +# workaround sparse qcow2 images bug +# thread: https://www.redhat.com/archives/libguestfs/2014-January/msg00008.html +$(OUTPUT)/box.img: $(OUTPUT)/builder.img + @echo Running convert... + qemu-img convert -O qcow2 $(OUTPUT)/builder.img $(OUTPUT)/box.img + +# +# metadata.json +# +$(OUTPUT)/metadata.json: + @echo Running templater... + [ -d $(OUTPUT) ] || mkdir -p $(OUTPUT)/ # ensure path is present first! + echo '{"provider": "libvirt", "format": "qcow2", "virtual_size": $(SIZE)}' > $(OUTPUT)/metadata.json + echo '' >> $(OUTPUT)/metadata.json # newline + +# +# tar +# +# create custom box +# format at: https://github.com/pradels/vagrant-libvirt/tree/master/example_box +$(OUTPUT)/$(BOX): Vagrantfile $(OUTPUT)/metadata.json $(OUTPUT)/box.img + @echo Running tar... + tar -cvzf $(OUTPUT)/$(BOX) ./Vagrantfile --directory=$(OUTPUT)/ ./metadata.json ./box.img + +# +# sha256sum +# +$(OUTPUT)/SHA256SUMS: $(OUTPUT)/$(BOX) + @echo Running sha256sum... + cd $(OUTPUT) && sha256sum $(BOX) > SHA256SUMS; cd - + +# +# gpg +# +$(OUTPUT)/SHA256SUMS.asc: $(OUTPUT)/SHA256SUMS + @echo Running gpg... + # the --yes forces an overwrite of the SHA256SUMS.asc if necessary + gpg2 --yes --clearsign $(OUTPUT)/SHA256SUMS + +# +# upload +# +# upload to public server +# NOTE: user downloads while file uploads are in progress don't cause problems! +upload: $(OUTPUT)/$(BOX) $(OUTPUT)/SHA256SUMS $(OUTPUT)/SHA256SUMS.asc + if [ "`cat $(OUTPUT)/SHA256SUMS`" != "`ssh $(SERVER) 'cd $(REMOTE_PATH)/$(VERSION)/ && sha256sum $(BOX)'`" ]; then \ + echo Running upload...; \ + scp -p $(OUTPUT)/{$(BOX),SHA256SUMS{,.asc}} $(SERVER):$(REMOTE_PATH)/$(VERSION)/; \ + fi +# this method works too, but always hits the server on every make call +#upload: +#ifeq ($(shell cat $(OUTPUT)/SHA256SUMS), $(shell ssh $(SERVER) 'cd $(REMOTE_PATH)/ && sha256sum $(BOX)')) +# @echo true +#else +# @echo false +#endif + diff --git a/gluster/builder/README b/gluster/builder/README new file mode 100644 index 000000000..51cec757a --- /dev/null +++ b/gluster/builder/README @@ -0,0 +1,13 @@ +This Makefile builds vagrant base image ("boxes") for vagrant-libvirt. +A short article describing the process can be found here: + +https://ttboj.wordpress.com/2014/01/20/building-base-images-for-vagrant-with-a-makefile + +This was built for Puppet-Gluster+Vagrant [1], but it can be used anywhere. + +Happy hacking, + +James / @purpleidea + +[1] https://ttboj.wordpress.com/2014/01/08/automatically-deploying-glusterfs-with-puppet-gluster-vagrant/ + diff --git a/gluster/builder/Vagrantfile b/gluster/builder/Vagrantfile new file mode 100644 index 000000000..d760bdb80 --- /dev/null +++ b/gluster/builder/Vagrantfile @@ -0,0 +1,57 @@ +# -*- mode: ruby -*- +# vi: set ft=ruby : + +Vagrant.configure('2') do |config| + + # Example configuration of new VM.. + # + #config.vm.define :test_vm do |test_vm| + # Box name + # + #test_vm.vm.box = 'centos-6' + + # Domain Specific Options + # + # See README for more info. + # + #test_vm.vm.provider :libvirt do |domain| + # domain.memory = 2048 + # domain.cpus = 2 + #end + + # Interfaces for VM + # + # Networking features in the form of `config.vm.network` support private + # networks concept. No public network or port forwarding are supported in + # current version of provider. See README for more info. + # + #test_vm.vm.network :private_network, :ip => '10.20.30.40' + #end + + # Options for libvirt vagrant provider. + config.vm.provider :libvirt do |libvirt| + + # A hypervisor name to access. Different drivers can be specified, but + # this version of provider creates KVM machines only. Some examples of + # drivers are qemu (KVM/qemu), xen (Xen hypervisor), lxc (Linux Containers), + # esx (VMware ESX), vmwarews (VMware Workstation) and more. Refer to + # documentation for available drivers (http://libvirt.org/drivers.html). + libvirt.driver = 'qemu' + + # The name of the server, where libvirtd is running. + #libvirt.host = 'localhost' + + # If use ssh tunnel to connect to Libvirt. + libvirt.connect_via_ssh = false + + # The username and password to access Libvirt. Password is not used when + # connecting via ssh. + libvirt.username = 'root' + #libvirt.password = 'secret' + + # Libvirt storage pool name, where box image and instance snapshots will + # be stored. + libvirt.storage_pool_name = 'default' + end +end + diff --git a/gluster/builder/files/cleanup.sh b/gluster/builder/files/cleanup.sh new file mode 100644 index 000000000..765b34362 --- /dev/null +++ b/gluster/builder/files/cleanup.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Do some cleanup.. +rm -f ~root/.bash_history +#rm -r "$(gem env gemdir)"/doc/* +yum clean all + diff --git a/gluster/builder/files/epel-release-6-8.noarch.rpm b/gluster/builder/files/epel-release-6-8.noarch.rpm new file mode 100644 index 0000000000000000000000000000000000000000..588a577e489358a3b9638f3b90c527034fcb8565 GIT binary patch literal 14540 zcmbVy2V4}*vhR{}2Fb7_0VOW5faDC4GYBHf23XQAyF>wzB$AXM2uct^1SLw4oCOJz zjR=SY$w@^}fj29B=Y02^^WMGpjlbFIe^*s^bxn6w&B(^n%_$NPa2+GDNN-6T(i;iG zBPF4dN`%h;!AE1mN5E1PO27W=r8~~;R8ssN! z5THx43}8|K>jIR3R{;v>6C0k$kgT6zQdfWiN~*#Zp*LqjPH(XuKl2ccPv5kG#wW2KigD-q`i{h znbW*pocQ!}H3&+#%S?ACnbl+Lc7yB(tP+VFoJg3&ba(MH3FN>j)nQ|Pl zG5@jL;j5ZuS^+K6Z4U!+P;135M?C+*+19>SsXF6sf+@{>#1u-yn^KZfNo7=Umz(To ze-;J!5HaJ)uWP^3NSmNy3n6Q7X8*j5L~Q%=B9(FAYalt3H+UtL!6)VneR9#MFYF>s;d>WE3~iAb6%b8b1^CzA zxXkGmFD13*6^0oLd#vKSA4}cWRAQB?0?@x}FbBW+MoG@8q=9}qivCCvUtoC@Nc$^A zQ_Yi9zIuvEZ=im)LHPbe7CmA^sOTA~lFYB1k;^OL=>jpdpGqHY`%&A)v@$4`s4OdS zg+1bC%QHtwPomXU(3G2J;#19uD8}uc@PDq+a96smAbPQ(#EYzV=YXlh-lE2?+!e(- z#6u#jLpB=%mr;^+L&>4!l%NPjIYqcUQbyj*4T(}#Qc^(3q971i6b$Mnivso#aLFP5 z(>~I>#&B#hq|7YUJg-|=6fwg3{@5o$C?DrCANpAJmi5cUxkedhHjf?W(1No!xYb-M zMEd#pv*s_7QzwkMh?8;=+s9y!VT@vAWkcgn$Iq$A>1nQidp@3=rQ?`oMW2;-Bl<-J zXgDiKSFbY`z18sj*+NyuRAEzAJKpS7#YU{X!?Z|}sho~;huhZ|d2>H@lPTQS&rTQr zELyLB-fMkx@D-Y*v@5-A3)$hkJvN?wm`ha9*_(+- z)K#Hs#qW3s!}MLE^JK{%RBj=)bg8(jpOnv9eel`A#UdAe==2p%-NwI}dtz%QE7IS& z$C0vLiNij8NvolwYV@P0hqKr9LjLB_re2&I7s%7%qgt&}NVaxp)@=-3u=*^8;BIlR zUb&&f04bWWQQqOg6*00-fos|mFV2~#r%n8V3YL#IM3pf)p{+#tUf(->J#+8z;@#F2 z@}TVFzWKfL)*lRsVs`r zB3T0s^!r^;5%F4VzH_w3>`C{b6$H9;9oR5`V%+iZaSG4|ViE}Sw>YQ)0?`fuT>vG- zG1?Y@5@Zm-O$pqD>#w*6#6b}8DP9360W+LpLL4Murc+FagH*uHa*8)jvBfFg1Sk*} zLGMm6Ax;wXKc3=8NeR9H zFbP49fT;lrv`GlQCSV$X0{$i;*h|2KJOp4;-&0J;LxB9BJVoFW{7&E}o$?9e5b%>z z%z8>+0u=BUDH}iu`U|K1>;NUm6Z`6!QX`A_lb{vZ_tFu)`y*h!FA0w|zQPMDV-Xet8~u!r2@6kj;OlrK&(VGjfP z)P(0s(APZ0$EVl;pul)Ez!-o0XLyRir`QOf0FQ<+FE9?N13(G#ngAuN-w~h$K0%Lw zodHVV6YL?#Uq0n~oMM+#+yqdd&LDL=>8B<5m0%y@6my?q-XI>p|nSmYEF z>JeamTFsMwQo_C@V1nHQdjbIp*h`BAD6p?cgHHK3Px-;8_`wNgNk7F60434^{k^^d zK|x9&yO?T)*~1qg+d}QI2aa( z@kGM?r7$>mKn?`5%hvzX54JdAfc^yY^UXYeXc>nuk(%1kuX@aw*LEZ!(f_yPB9NYsW1UAF?gME=m1QPLQHArtLVVQVL z01giL-vem7A(6g-S9~ymNCdSI67GQk3krb=gZ=RpSW*%ULlE@cLjLsrRWE}D!Oqk| zVBk_$2TLRU;nMgJ{E7dhaUQ^$o3=DC)7%3bQ;Q{lPf@6IC zxfrOlv#^$xj^Y1oCXf&wuu>8Y7L|4ZW^?{Coisx9|7qMmRh)%RG@Sn|+Zp`-*8KB$ zLF9e_y$YNQFmOD8JV*#T24j&$C;zlIB8yJsPJS)!D(?K(TPc!&p%5riRtbf2lT&~y zAR)>yH)VOa3!zSA=cWh)ysrp{Da*Z-| zB%`1p2a=bAC_|Ls2t^oNPC)?*g()JT2pNd7tUO!}j)KT4%Ru4EGD;{J7(x*Ru#}YK z-B2h5Qb7iy1cO3kKyZY-GB6t)DK9S%kx@p<1L|^6MI{&tDF^5(LFAE0B?Md{KxC$Ac*~4RUGtpL{R?o>c~XcH9(97(E`IIjFh~jEJ$7oDkbxW5`Gn=Ai(dR>m(-r!!$rp zNlFIbSY$eMm_KpWG^Z8QG^sW1FmX%iFi~XCG=4MIVN^J$X&51|sZWmh$F28czC-W3 zW``=?ALilf1JgVi_;0~h9V`hZ72CxLIGrr^!EVUp%E}|K-eqrPXp!;{=Zl)57I^Y`U08F0PXFA!2uH> z{$Nd503Piv6@Wi6S?+%(Ci_=5u{!yx0i+RkG>`yrXkT}*D)6a866x!%>5T>+{=cV@ zlbIkO8;lQF5Rb+93xaiE-rlevSO{3vAAZvOS7%De{=18OV1FK!7gE#R2L>zxi0UU} z%lwaJePD1L1}_B&Na6tuM&SV0o@@jl%@8u4B+xB+B6d2LTYx)Q z2nqq>Ibn9fq4!3^|JpwPI8Bn^0H9*>LHfghjPC^wzNpHrxkuTaG4V?AUs`ek@`meA}h=o8{|G(bAdjO%f zCe+=35&}?npIoPTf$~qd{pZI?!dL{If9P3Rm`Vb*tfaA?qa>lom4t%+W#64-9)BR@ z17QFtwXm2!J^yU|wXFbEpe6mg#da2@Hik( z!Ei`v1P13rTSTqI{d=&&atyF>e{~U%t)vKhtFX27pzq|hKQ-Uvu zKw8#1Mn+(NWUxO7fyM!aIPebf-;MI0`GkD@=Uc_`@oz#=$9SlC)!`D5pXYwj77&93 zh=7+#5UGVB^wXsXLG{ZZ4!UVlH^@N%&Gm<^43^}fJr~#SnYb$H>ww>9#ly4nZNQ{^ zRx$U>5Exs{{2ENZ&4rgt4xS551)7WGPqQ@{?uxLlw!89`ye$}}cxBHxRQt?UCCi}O ztkZ7VQt)f$lgswmpSlagk`);PRIm?*wkGJ#}2iDyP8OQ2PuH$DxO%xI~FyuvVZ^jZWk*3*!9zs((I-?UA$aaqU)ckP=!K=w&ucW)0vE<{PcAg1HV4zvNIKMG zrkoEwTy>^{A(h9g@qx(m^}suZ`IF>~zSl_(q`ZYA&l`s+?Pg0VEFBgeFqBMua>%7? zV#>mQvx=1oVBs{6<7-h2F;wu7w%?^|DSp4^E?pcf*CGC8?MhtMhkGjh^P9^TUP$=F zeb*=bpgoWA_Pw2r3#<-1a!;v6h3x z*7Cq1rI&~+SB;3`tYtzz=M|@I&cMz?V=HF=3-QiA+DMk%(lKLJPsW2k7fQItG zUhfX+Kxq!%)v{*YAHQx9U5K4{xnOoqneL};N#>Bay){HD&8elN%X5ETbK;A(JLlNe z7e1j!Rlat?<}(6j*s{A^QexrhzC&dbF7$S4F+cc^bvTHAXS`?pNunSvyBQ?2KUTk! zks9q^*A+j4R#R-+uz5bgySjJ#YSvDwL#DIV-F^=J8CAurj^Pb)Tk$D68aIF3$}6P9 z>rT}_GbS_peA8%Srr5r()aQ5q7}b($T(Cdl)!9!0$}~?L)7Q@^P#Z(iXA-6tQ<4+= zNGXUr7=sTTHzHw>kwJP-kwGH8)IG`Rbm@)Qv3^GSCj$d?2aL~EX^hfpzSDlK`=Bwu z>pO5Tg^nDUI51+)y(GO8SVvlAeTFMxTY2yBTRRytOyL8~Ti^GLWRlp%m+`Qp_oVN< z9%kfFvA()2b2$yeDBqmfG?o$VTNnFzjc*JrzVmw;diCW}*i~e(`zvgWX673YNLPut z06dMDk>gcw;dHc&!-4AUtO=B?c|X~4Mw?6GL8n3W=gSwy-##GWl0%zq91wqb!}s+D z6&G3V?rVv@vzM!8lj<*ub?V;KlczpAcHPJ#1+TlNVJ@J~_w-5Z=U3^F-CO8zTi<#8 zO)3aVi;axP$5nUOm#uhG_}B74mJn;hHK%qsmG;QB>#Dg-H5C%CQ^oBw+fx#YcNh0kyJJ5te7oaW%-8*G zSk5hCt@!@K0UFzzcVoYBH;QAU#iq~MXiz?)3SQ)1)-qq)h^$%KpE1wlDo<^$QG>5M zhLWiu`U5RXBwk!y!1@S;n|w6yyL{aPVL;ox=uWosWPP>4k6wSLZL4oY>#|c(!K$Vt z+*G~OmMdw)HQ4g0LT(I&pGj69r<_EYM(@EjoZXIoL&RRScu3@h zShG_l$y1E&OF4H@P*q@gJ|@uJ`mLOk+V9hC zKgbi0xkDk;xAR~%IBVc~$`2{VCvie`qTX%{^eYa@G#v(Obi-_U82sot;cK9vW-eNN za}yDMCW{@$DM!Tz#@hplbbBZ^ZVmd>Y}Bp3DHSuYRXG)?oKG%>qA_gE?rrAvodqHK z*534ejU~5Bbq$o$C6xm@%yIp9ll{LcmR`S1%E9)1V3foSiMc6tRr(AikApr-De7g? zn;KsA-9y7AuRRMI^R4?!M^=9FZ_`Ao#`Gq6C!-S?Bl79&yUPzBDYGUuc0Egx?3ROS zS+5ogQ-U0+9QH#xKT3`f^$rz_Jn=n@)V2-Z5x&w?q~-cS{=47J<~{6ou;m-?_0C@)Bm=a3afRX_7CF&tlZJ2h@qVSS=sFKySOs-hS(RfjOl2gXe=m zmF6f@r#0J1g(%w#`WrXo1&m)Fem(cO&w)%N=Y#f_aYnO8maO2Sjmq9E!Go!FwQodT z;@b958$p}ZMY>VeeG~UPh56za&xn$kSytI1{m89osTDVKs{fgs@4yIrM+)Kys;bUhhwg}&r0kwkD+Yt?knqGfX2)P#eij6b-mquvPF?p<9(@XxT8*sz(O$laEMq@T(Jtq2?Vbzq$_%A9 z^RB+7oO|Bsdk?thi<}{iB{`dm-{O_8f(L@09q-&PRDN$u9;Pc_11F!YPiOOl@8ut# zsb!VSuf399Ip1W@OHTIzo$Tf0HDQpnKL`AmZSG<%><#&P!iWuMG2rXxR?+Hb5>G2s z_isGYH|K5o{rdfA<@}&*2OrOVQ@}#7>5@BS^lSxkY+mqcuehd4$OZ*nf8DGFO^=}1 z;`fK=lPt>L3dC}*!c;V7DQo7i}@8@%B5Vmf}Qxmo;tHHpNrl_}qsCwKU6 zn3AKbwy#w7-XH2t^Zr(i$qF2M?CIyF4=-fC+NjFJx4n}P(81HYTFoSWmL6}#_3$py zVVtPmYR|XJuKr{L6Xg5>tjJjeYlsNeka%9#p&1UapZyR9>1?v$qKwN zQ)8c>;&(}(>a&`(x7gsUz2bh!SeYY6SN=y?rrMP(w)y)>wn4SeS}$7Xm3Om{rO48% z@OE(#yX!h1u6=8M=*u96e-xAl$KhU9KnL$z;yiie9v-!4+~bU5b1Y#0G(BN5c`Y)+ zp*haGVc9n)j}F2D5|-^a&9CwO`))oZhn z9;awfB{1`;pL^T36>NA}7^$xL5}|?=Sb6!Kn7AzI^E72z$!6nmg2gGcU`D0swWlw8y%aq+B0A17sCTtwR5(WY0v#$p(b+oOW~sqB|ktkopu zx$6Z=`igPV5q1GeDZ}##GBlAoD;>ma{#6Cjdx(NVjTip<^53Vqoj|!R&ONQ69kp~1 zEjJ*ExGlRG9?2gbSiGL~=wg|q#=Q8qnlnz@z0@~I2iHGoC!W1IlcDuADB8lHI~DTc z?VIym(4t43)tPNM=^B1xIrHs?2=J4(qWG!FXmAN)>E5V#6m?otg z?C)w1NmRex9?IbJ_|eU&@ikDig}oSyROHzy<^8^oN2`&Qb*<@N$5SSsO*^|=j1c7= zv}tlp=MM>H$SzE{$8Z;$Y}f5LqEK7PKt+%;px$RDIRv&bA!A8oYcLEIZLDfjeJ z&E;1m1yZ=SS}`G>2V0AkABSy9O_~Q^M6gcX6u2V$(C^FKCe>qyd2NbS^l(C8IV!Eu z1-`ZI^7ZF6Mb47;EaJFfIn{jq(q&tL4)BNRsDY&To@Z;(Hrs36KJky;)>T|8QvR>dU$;N5kzAWWd`>AE4xtxeNu`R?#_Slq0z1_lRrI!MGh1a02E0EbR zs?am%E5qfr0dx!I)q?%5s@oC5%HWJ=DS#-%*er?ZGE5c*<}90O!WrKO*a{WyT@Li z6Hyj%FQ`{vCRM@E1T5WB$nvnqR5FFLjT2kU%y~QU6n+Xn9bmrWPb~Z#>d2{q4^j1$6RT%qTvYAMAVCe_r>p?{{!Y_3vOgf{6rFmO^ zyjLGMS1sM}B%NWBtTCAyef#O%*ekT*C_YaQ)<;4~X%rK--lDRv(QflvnxJQ>jrG(s z2V)d#P1!}gpUwFwEbJZH&t8_#iRNio&MxRO_X5}D=m*Qsn(Tiv#1GRpGx)qrD_FZ> ztrxubOL)%OoKhxoo7LDyTZWUe%&Q{o~uKlC;~7gpf04eL^}Uwk5R z;S;n@X?j4EJUJBqPT~_A>ycSkkauE~Emly@(jXzaxQpYma-aZoXR;L0FVaEIY9o;I z`d(Y(zM=f*-J9y|K}j0lnLIjp>#r!3nk`nCHpAU?4S2NT10GL4OeBle%o$*mZoxJ@ zUO>JL%=0SSgK&AWn8>I$(+Af6^q3c~;Q|(p`)vw;wOlfC{1zQjl!m?&^q{sWyON~u;L3ee>(vS~7gfg5 z@?Q76P{}9k{InEw&(mk&&5~p9+$GfNjdx;F1*qy#|D!QDei?D|$uuo(BF!f=ug&3-VBfXP z$Qa6!4LtEl$|P#?H<^BbID&AO4Ky7VkOP=~VF9#cT|es_vTKfB36hS&O+3qB7axDIr;=Od~dj-@49NujzbQ*t54{=^~f%R){tEdvNoJwRFa8f?-GSPDmd~giBHX z!Cc3JrP~l_;AQWPly}&+htFh-AyK$@N0&9`Fx|7vQIoJUfpcRtwoD5wD?Bgtf4|8! zU0#VBx1XfeHv2~0Ec<=r+){GMd+th4xHS+?#R{ZRwB?(Xa{0$#4)%|;lG5+XJ{_{Dok35w|rYQzbcW^-m{`1Rx`*QYG1wv zA?6(GZV%Z=3!_ z_M7QdMqSZui-|Hd9yEq+0qUCtdE{yiT++A*`lZZF@msOWsF4Cg$qSLlU<3mU({JDS zAfm|p2P^IN=N%oa&U2`s_)$BWM3Q)_paYlfz25=Y;Jc6!f;9-GT%| zVRtqS7Kw!pM2LNE+hp(WsCwb4nS`Pj9+}c3ZA5l3PK?3b*&mdL`4&A)RHFi|LCcUZ zrK)dWXZPu4!~@csBHA?VcPk_LY?cLiTWhO76|=C)k5*9SE+e0xm9ermIM-aPE_RH>=ZrP&m^ z$4e3k`f9%en*I6ycYDaK;f5*}F{g#An~n6^tFL&fK1w7eZDEZdgMwn|DN^Y#D1!-_|c7=``6#cf;16 z(&g{jeM^6LrsI&)mT$IWf{odGq0`~QR_RLPlbkNGbk$Hrl{uZ|-Z7hx)ePYnaWS%l z@M&h+shSNFTzcwc!b`m!hfzy@tu)IX>h~n)7__H$9NH!>!iGPNomf%_7 zAj8*-V;J+62~HnBQ$09x}1A$#bynXSRB`gxf5pdZ-2@`?au-FSE%S4dSaHYcV+iYAZHPjf>3K$W33@eJ1G^6(y-UadN!gt)sj^0pFak{H8a3ZSUu5j%e8a@+GYL^~=FhlV%T!4bs)k ztv^I+Ts&}Rz@by6J$hH&cfg)=)uC>AEO+^*6d|h(RvgL82S#$RLm$zr#3NP+ugujF`u%<92H}F4TLcjOX zQWSNZtnivXo<(8cf+;Mtou&a^ZBa-4KmAH}1Vv*LJSi=r*7DmgjVI!fOU>hjMJykU z8zx0<#iUceB%?65eQy^JhlaS=%dm;Z={=5R{b|U*i?Ir7NI5^NGNbS*KAu>c5w-e* zW%mv;w)Ka249zCHYx$#_@67XFebcd^pAE9KIHs?vg%oNkW)z6FZw_zg#k*FD3i!2~ zjs>c(Pt7%IyLG0w%IVR&<{%GgNfJ2Le}9!$fg%DwFSEJ%a3mA&|NL`|XT`vWRFDRv{yT7*>z)fYDxcbU6ZZ&jSWkDWlRCHvV6(vj3 z5s_dg^=HPAUIB#+CW9u{tP8iFkeWB{#Mo`UcW!)=0+~2}j}q2+x8ZTF{+2$4C1s5K z2cOu*vOzbeO2+d}w{orTc}aA$69+p{+zw*zT#;S*H9?cjczuZf9O%Z_^;DiWcidpK zQ{>e@#9GX!R5Yribo!gJouW%4(K2HWM$F}*vP$ZPKXW&mI_P$v+sCx9o*(HztkodT z%kbV%s;?Jt$Xgqr`j{QkdH`?F7yS{meo3fn_=Z@`i$=d6Ee#2yb(dpG7BbnLxYp3z zKOObTC7v;b6}oP$StQEm^H0B{+pr|A$jNJGt3~0}_!}6MaHJc27Q^ywono^VYQkT+ z9(9K+PT%;QX{YgtJSwCTT8qxE*$So$sVWX<@e_>^EKJs$P^P^2`>jmLH_@8->q`UT z%qAv%%vDT&5-~S-OGSz_hKEDmJ>2adU^(FYc&6KQ`5=2U`^Tmel{(8j%|3H=&9s(x zW=db(CGOA?*}=&YHfM$||D>QOt5Vyj($ba#bB%&)Pjc7`evULpXtLcR;;|=-Y6>AM zCULCQ7JXD3Fc$-VTcB}df3Aj0^ie7=wYWh<$nWz~?TgAi!@`>3qlbqeYyL+S=k@V2 zuio2ia<%^A-)-O=bGP?^`8}}m9KHBx(W=-e>V8xFhi_5BY!A;czNO3 zL4OsKNBCTECUfx7F|R#r{9+Mo5iVkTYxI2E8(h2bGh)e2*_T{0XKz`xSGecCkbbt? zog%X-@X=55=3_ONNS}A@uSaY|%y_0m+DAJ+)W5(-b*%DE z_3Z9C^p5>#Tl0;9%H3D@p0L|L45{l)kW_DQ6LuFTaiv18Ot)5#2krY zjx`L}p}90W(1GuKvN>dW9d9_Mbh90?Bx#^nD8c5^I}!a=TzZ5u+nG&G@nb*z`!*bcXYWF19~g`C6@x$$}n4=r2HyOx@+WR>oQWh5>$HKFf+l_Arx z@n_R>CZpewkrm(ZiEQw;RXkRm7fJoO*gnf)om>L%6~lb*YR!p}jv^Hq7yETaTCqXy z%gahkmoB+fY||3?f_@J>#Q`GwQO`Zs7m&;i_1}FZG=}NBIgdk35#(E;VoBj8!lp{% z6fMj~i|o959d>Pr2sFH7a%P6(VG29HvBR`cXxKg-GMZKkp*Y9!rJS7clXRTXN(=UWs^kalwJMsE{)bwZ_dX7sUT)%1qLneQLbeH5zQ(l zSkEqWne?J{dsKbq6)%gW1zUWpfXP`pZfC87;u;BQGT1{d{i*VejPN?H+hJ@i%c sP`@P@FtTdgEH&cAP!MqeM6|1Wy^aXfC;pVEer1-2@CPp-YR60e2R;l*O#lD@ literal 0 HcmV?d00001 diff --git a/gluster/builder/files/network.sh b/gluster/builder/files/network.sh new file mode 100644 index 000000000..404bb9ace --- /dev/null +++ b/gluster/builder/files/network.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Disable firewall +chkconfig iptables off +chkconfig ip6tables off +chkconfig sshd on + +# Networking setup... +# Don't fix ethX names to hw address. +#rm -f /etc/udev/rules.d/*persistent-net.rules +#rm -f /etc/udev/rules.d/*-net.rules +rm -rf /var/lib/dhclient/* # remove any old leases that could be around... + +# XXX: unsure if this will help, but we'll try it out: +# Problem situation: Two interfaces are connected to same network. One interface +# wants to renew DHCP lease and asks server for address. DHCPACK message from +# server arrives, client moves to BOUND state. The client performs a check on +# the suggested address to ensure that the address is not already in use. On +# arping for specified IP address, other interface replies and that's why +# dhclient-script replies with DHCPDECLINE message. (See RFC2131, 4.4.1.). +# Solution: Set sysctl to reply only if the target IP address is local address +# configured on the incoming interface. (See kernel documentation +# Documentation/networking/ip-sysctl.txt) +set_sysctl() { + grep "$1" /etc/sysctl.conf > /dev/null + [ $? -eq 0 ] && sed -i '/'$1'/d' /etc/sysctl.conf + echo "$1 = $2" >> /etc/sysctl.conf +} +set_sysctl 'net.ipv4.conf.all.arp_ignore' 1 +set_sysctl 'net.ipv4.conf.all.arp_announce' 2 +set_sysctl 'net.ipv4.conf.all.rp_filter' 3 + +# Interface eth0 should get IP address via dhcp. +#cat > /etc/sysconfig/network-scripts/ifcfg-eth0 << EOF +#DEVICE="eth0" +#BOOTPROTO="dhcp" +#ONBOOT="yes" +#NM_CONTROLLED="no" +#EOF + diff --git a/gluster/builder/files/password b/gluster/builder/files/password new file mode 100644 index 000000000..d6a97626e --- /dev/null +++ b/gluster/builder/files/password @@ -0,0 +1 @@ +vagrant diff --git a/gluster/builder/files/puppetlabs-release-el-6.noarch.rpm b/gluster/builder/files/puppetlabs-release-el-6.noarch.rpm new file mode 100644 index 0000000000000000000000000000000000000000..0e99d19b240484cde2077dc6cc90439dfa5fc3b8 GIT binary patch literal 5712 zcma)92{@G7|9>%-lKaP5OCNe zwET0Rn$gLjZw3CLW+fn&d(ZA|Z*$ zNK1N#WuznWvy_#+FXcNw9>l*dQASQ`=32G-uS~jQbw<-w=LJUQn&Mdr2kPhDQT`~c z*XH?txoE)ilfezuYdG7x#oh-Vq^R*>&^7>s9K9Bde3eLI8EHS zP<%Qly;AD*wM6TX?IV(@gLkrgBhAY!m8Wi3=KJP^EV{;UE{ z@oU`fPAwg^H|&Xc^Ng^!RpQ$f^e_Iz7P@Olsel)cwU1qHl_+ZTDoa zddOPWr`T!i^F&rf9J;CFY~ATsV&=qNqV=fo`o=wZKUST;aMDlhsg9=q9&XIp;~SCI z1vT~;ck>K4?>zVM++U}&8&y{p?l{^w?&UzdPbE-7@FWtAz@jmk42lWEgibY~5kjbV ziYcB=Ms>sNf8%Z5m-BhHz`+dO$ze;(335drznRt`pKUAsZhUEI~Y6{k`L`i97g+Jx2$;T!EW ztrm`^D_@UKp12zxm!;8AsA4Or6p?f4T+K#CPfFUSs*t97-~D|fs!yV(B+5d(z9n`R zCaqj#*Gd)L{PSj7;Wwg!`t#jVrou~2{d%kZ5YUnBAMS*0u9jcw)E!AGdZ7IqWB*`a zSvtkQlJ4`qPa`<8=0Nhf(C*UxFITYV~XJ}{pEpicGQ>56ektkM4&^}en1I4^T%tHWz{bD)*LVwH|E-eysRLo<=oF(ReuLt7-_Rbxx9EiaBU+dAK6D)(nM!6y*5G&#twzpBhkHCdQu{lgsV8lc@ z>}XUF`_HJjC`{MvLsnicSb`BA@ro6S&7eE`Uw2uCnH27yb%d!f{={JM)+xj zzi^fa(A0=9Jx2w{3-Jv7X6Zg?Z|SzR!O~;;9|YC~{Dr`-o%suaHDhyGJj? z2o7rvZ_D%rCIjZfXVOI|b~y{AQHT|y40yc_vAhr=kAnii^b`L3N&|gIE?dN=bFdyW zm;4vcUVJEK&LVj9u;xv7PD+JRFWs7tkZjg+e|k zI^a`SfD^LgP;(L%7eNHp5wJ#>vx!tP7>lB~sCg*LMFngI@I@EKav5Qu=J29~xF~K6 zo6E$B=pi8M2!qe&A)Dh6g1E|n<3D{1rYjFFlnV>|znTPMx~`eH)kVrzke z+XYmNIJVUCOK0`JwHl60Pw-ES7-~;o&i>w>pr~qCyjS_;0m<1MRRxdBy}`O_O|Pm- za(BJy`N78@tGi?sb@!I_pi)?tc`veb^7hIDKVH?o%^mO~NJ{RH8IxfSR3%n#EmgM; z`yp_?f6#WJ26LIkhP4i?gG-nWXjf?MZn=a_{mbq&$*;;2s?D6d5m2m~``8q|t`!1Z3-6Xpv)h0(umi8nxc%;lTiXcu+U3 zG5JvVj*emLZToyi$!T+fEo1WN8dtv0xrMB$Xw#@Kv-+wwfv2Eybq|K0p187CftH$c z;MwFv(7sN?v?pCi8qKH+F}tzq#psSJcs!b<|VMaL7K+*li|^%j^GY|wK1#2`?Q>0w(I|N#hhNQ;>~?!aTV2O9Rxq^CZ&r8ly^z@F zS$tlRdwR%mS8ZXTyGzE`*UFfJ{4~mjB|*lDGSPEQOK^v(-sV25T2`MMpXNJqgEd9n zV?~OeI$pznq?IXk`TCD4qVdC6!ZXT~f=Bmu4u1+%d=W~RsC%_wLD9|BK z+YM)$?Aw-5j9VY32ke;pZT#@_MH8+2qcg%+-5Sh8hBM_BmL!q-y5|HsvwXwMoJ`1> zC$!@f=PfJ!cFo!GK~&0UNh!ALT~);<TfLFgg zSgOCPsIzdk>pyf+*7sn>U{%z8=8DhTntHd?v-s1iMQe&UOoQ8(`a+oD-(ECQpq z*i`HbPyc!-(fn}QUK95}`Th}~MnAaQdam5L$~R`?`BGbwsbcNRY|MC7VA1K3lWQ(7 zJ+$jcg?HSGkfXHkrzc+`5y8g{|3b7X+CuC3Qw{7P;X;lt?ZsJ_5AmE?wyq((W5|=$P%THNu@V@n& zFYzsxsh5WDDeew_GQM|Q)i1~;(~zNX%<<5Dtq&XfDrF4~liO`>u6sm@%4jpY`rPxV zG+IORBc~SQo?R{|iJ{f5S6X4OpBkC@C6g>OszNUFN5@)1!&gj^T&1LMHcnYxO;T%I z^G$wEL2u*dTO8+*5RVMgZB*qiW?BN*#*j)ucU8>xa3QAX!TQ5jBmd|P#tv`j$?3bPR9I80p2Eof*A~sa9Vz$UxV-PvUVQqnuHh!XoGl&jKi_gKvPCy)^rRWA$3Y(G&K= zjrW>t1-dGY2tU(8{zus0nW(tKAOkaq*sj_iNkcI4W} zku(0QlwR8(-`Qx}d4AGU!;88!PdJDA{Lt@vL|1HH+sEEnVBWQ6YwlYgFQ0i!f>cf_ zg)?5OamLPGUn0A>oN{~X{WiZ(DLRN%WZIO6u`~B_kGVkKvA)GiYOL6~_OU^6HFxiI zCHm)j<4k|^NF@W&HB8g_C!6+@taC#Hs;D|67-I9$`A?*ozUp0`gbl;*Y{KRd ~root/.ssh/authorized_keys << EOF +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key +EOF +chmod 600 ~root/.ssh/authorized_keys + +# allow interhost communication +cat > ~root/.ssh/id_rsa << EOF +-----BEGIN RSA PRIVATE KEY----- +MIIEogIBAAKCAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzI +w+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoP +kcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2 +hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NO +Td0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcW +yLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQIBIwKCAQEA4iqWPJXtzZA68mKd +ELs4jJsdyky+ewdZeNds5tjcnHU5zUYE25K+ffJED9qUWICcLZDc81TGWjHyAqD1 +Bw7XpgUwFgeUJwUlzQurAv+/ySnxiwuaGJfhFM1CaQHzfXphgVml+fZUvnJUTvzf +TK2Lg6EdbUE9TarUlBf/xPfuEhMSlIE5keb/Zz3/LUlRg8yDqz5w+QWVJ4utnKnK +iqwZN0mwpwU7YSyJhlT4YV1F3n4YjLswM5wJs2oqm0jssQu/BT0tyEXNDYBLEF4A +sClaWuSJ2kjq7KhrrYXzagqhnSei9ODYFShJu8UWVec3Ihb5ZXlzO6vdNQ1J9Xsf +4m+2ywKBgQD6qFxx/Rv9CNN96l/4rb14HKirC2o/orApiHmHDsURs5rUKDx0f9iP +cXN7S1uePXuJRK/5hsubaOCx3Owd2u9gD6Oq0CsMkE4CUSiJcYrMANtx54cGH7Rk +EjFZxK8xAv1ldELEyxrFqkbE4BKd8QOt414qjvTGyAK+OLD3M2QdCQKBgQDtx8pN +CAxR7yhHbIWT1AH66+XWN8bXq7l3RO/ukeaci98JfkbkxURZhtxV/HHuvUhnPLdX +3TwygPBYZFNo4pzVEhzWoTtnEtrFueKxyc3+LjZpuo+mBlQ6ORtfgkr9gBVphXZG +YEzkCD3lVdl8L4cw9BVpKrJCs1c5taGjDgdInQKBgHm/fVvv96bJxc9x1tffXAcj +3OVdUN0UgXNCSaf/3A/phbeBQe9xS+3mpc4r6qvx+iy69mNBeNZ0xOitIjpjBo2+ +dBEjSBwLk5q5tJqHmy/jKMJL4n9ROlx93XS+njxgibTvU6Fp9w+NOFD/HvxB3Tcz +6+jJF85D5BNAG3DBMKBjAoGBAOAxZvgsKN+JuENXsST7F89Tck2iTcQIT8g5rwWC +P9Vt74yboe2kDT531w8+egz7nAmRBKNM751U/95P9t88EDacDI/Z2OwnuFQHCPDF +llYOUI+SpLJ6/vURRbHSnnn8a/XG+nzedGH5JGqEJNQsz+xT2axM0/W/CRknmGaJ +kda/AoGANWrLCz708y7VYgAtW2Uf1DPOIYMdvo6fxIB5i9ZfISgcJ/bbCUkFrhoH ++vq/5CIWxCPp0f85R4qxxQ5ihxJ0YDQT9Jpx4TMss4PSavPaBH3RXow5Ohe+bYoQ +NE5OgEXk2wVfZczCZpigBKbKZHNYcelXtTt/nP3rsCuGcM4h53s= +-----END RSA PRIVATE KEY----- +EOF +chmod 600 ~root/.ssh/id_rsa + +cat > ~root/.ssh/id_rsa.pub << EOF +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key +EOF + +# vagrant user ssh +[ -d ~vagrant/.ssh ] || mkdir ~vagrant/.ssh +chmod 700 ~vagrant/.ssh +cat > ~vagrant/.ssh/authorized_keys << EOF +ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQEA6NF8iallvQVp22WDkTkyrtvp9eWW6A8YVr+kz4TjGYe7gHzIw+niNltGEFHzD8+v1I2YJ6oXevct1YeS0o9HZyN1Q9qgCgzUFtdOKLv6IedplqoPkcmF0aYet2PkEDo3MlTBckFXPITAMzF8dJSIFo9D8HfdOV0IAdx4O7PtixWKn5y2hMNG0zQPyUecp4pzC6kivAIhyfHilFR61RGL+GPXQ2MWZWFYbAGjyiYJnAmCP3NOTd0jMZEnDkbUvxhMmBYSdETk1rRgm+R4LOzFUGaHqHDLKLX+FIPKcF96hrucXzcWyLbIbEgE98OHlnVYCzRdK8jlqm8tehUc9c9WhQ== vagrant insecure public key +EOF +chmod 600 ~vagrant/.ssh/authorized_keys +chown -R vagrant:vagrant ~vagrant/.ssh/ + diff --git a/gluster/builder/files/user.sh b/gluster/builder/files/user.sh new file mode 100644 index 000000000..224a05801 --- /dev/null +++ b/gluster/builder/files/user.sh @@ -0,0 +1,16 @@ +#!/bin/bash + +echo 'vagrant' | passwd --stdin root +grep 'vagrant' /etc/passwd > /dev/null +if [ $? -ne 0 ]; then + echo '* Creating user vagrant.' + useradd vagrant + echo 'vagrant' | passwd --stdin vagrant +fi +grep '^admin:' /etc/group > /dev/null || groupadd admin +usermod -G admin vagrant + +#echo 'Defaults env_keep += "SSH_AUTH_SOCK"' >> /etc/sudoers +echo '%admin ALL=NOPASSWD: ALL' >> /etc/sudoers +sed -i 's/Defaults\s*requiretty/Defaults !requiretty/' /etc/sudoers + diff --git a/gluster/builder/versions/template.sh b/gluster/builder/versions/template.sh new file mode 100755 index 000000000..f78b02cfd --- /dev/null +++ b/gluster/builder/versions/template.sh @@ -0,0 +1,13 @@ +#!/bin/bash + +# to use this script, from its parent dir, run: ./versions/