├── .gitignore ├── .gitreview ├── .project ├── .pydevproject ├── .stestr.conf ├── .zuul.yaml ├── LICENSE ├── Makefile ├── README.Swift_ring_management ├── README.md ├── actions.yaml ├── actions ├── __init__.py ├── actions.py ├── add-user ├── add_user.py ├── diskusage ├── dispersion-populate ├── dispersion-report ├── openstack-upgrade ├── openstack_upgrade.py ├── pause ├── remove-devices ├── resume └── set-weight ├── bindep.txt ├── charm-helpers-hooks.yaml ├── charmcraft.yaml ├── charmhelpers ├── __init__.py ├── cli │ ├── __init__.py │ ├── benchmark.py │ ├── commands.py │ ├── hookenv.py │ ├── host.py │ └── unitdata.py ├── contrib │ ├── __init__.py │ ├── charmsupport │ │ ├── __init__.py │ │ ├── nrpe.py │ │ └── volumes.py │ ├── hahelpers │ │ ├── __init__.py │ │ ├── apache.py │ │ └── cluster.py │ ├── hardening │ │ ├── README.hardening.md │ │ ├── __init__.py │ │ ├── apache │ │ │ ├── __init__.py │ │ │ ├── checks │ │ │ │ ├── __init__.py │ │ │ │ └── config.py │ │ │ └── templates │ │ │ │ ├── 99-hardening.conf │ │ │ │ ├── __init__.py │ │ │ │ └── alias.conf │ │ ├── audits │ │ │ ├── __init__.py │ │ │ ├── apache.py │ │ │ ├── apt.py │ │ │ └── file.py │ │ ├── defaults │ │ │ ├── __init__.py │ │ │ ├── apache.yaml │ │ │ ├── apache.yaml.schema │ │ │ ├── mysql.yaml │ │ │ ├── mysql.yaml.schema │ │ │ ├── os.yaml │ │ │ ├── os.yaml.schema │ │ │ ├── ssh.yaml │ │ │ └── ssh.yaml.schema │ │ ├── harden.py │ │ ├── host │ │ │ ├── __init__.py │ │ │ ├── checks │ │ │ │ ├── __init__.py │ │ │ │ ├── apt.py │ │ │ │ ├── limits.py │ │ │ │ ├── login.py │ │ │ │ ├── minimize_access.py │ │ │ │ ├── pam.py │ │ │ │ ├── profile.py │ │ │ │ ├── securetty.py │ │ │ │ ├── suid_sgid.py │ │ │ │ └── sysctl.py │ │ │ └── templates │ │ │ │ ├── 10.hardcore.conf │ │ │ │ ├── 99-hardening.sh │ │ │ │ ├── 99-juju-hardening.conf │ │ │ │ ├── __init__.py │ │ │ │ ├── login.defs │ │ │ │ ├── modules │ │ │ │ ├── passwdqc.conf │ │ │ │ ├── pinerolo_profile.sh │ │ │ │ ├── securetty │ │ │ │ └── tally2 │ │ ├── mysql │ │ │ ├── __init__.py │ │ │ ├── checks │ │ │ │ ├── __init__.py │ │ │ │ └── config.py │ │ │ └── templates │ │ │ │ ├── __init__.py │ │ │ │ └── hardening.cnf │ │ ├── ssh │ │ │ ├── __init__.py │ │ │ ├── checks │ │ │ │ ├── __init__.py │ │ │ │ └── config.py │ │ │ └── templates │ │ │ │ ├── __init__.py │ │ │ │ ├── ssh_config │ │ │ │ └── sshd_config │ │ ├── templating.py │ │ └── utils.py │ ├── hardware │ │ ├── __init__.py │ │ └── pci.py │ ├── network │ │ ├── __init__.py │ │ └── ip.py │ ├── openstack │ │ ├── __init__.py │ │ ├── alternatives.py │ │ ├── audits │ │ │ ├── __init__.py │ │ │ └── openstack_security_guide.py │ │ ├── cert_utils.py │ │ ├── context.py │ │ ├── deferred_events.py │ │ ├── exceptions.py │ │ ├── files │ │ │ ├── __init__.py │ │ │ ├── check_deferred_restarts.py │ │ │ ├── check_haproxy.sh │ │ │ ├── check_haproxy_queue_depth.sh │ │ │ └── policy_rc_d_script.py │ │ ├── ha │ │ │ ├── __init__.py │ │ │ └── utils.py │ │ ├── ip.py │ │ ├── keystone.py │ │ ├── neutron.py │ │ ├── policy_rcd.py │ │ ├── policyd.py │ │ ├── ssh_migrations.py │ │ ├── templates │ │ │ ├── __init__.py │ │ │ ├── ceph.conf │ │ │ ├── git.upstart │ │ │ ├── haproxy.cfg │ │ │ ├── logrotate │ │ │ ├── memcached.conf │ │ │ ├── openstack_https_frontend │ │ │ ├── openstack_https_frontend.conf │ │ │ ├── section-audit-middleware-notifications │ │ │ ├── section-ceph-bluestore-compression │ │ │ ├── section-filter-audit │ │ │ ├── section-keystone-authtoken │ │ │ ├── section-keystone-authtoken-legacy │ │ │ ├── section-keystone-authtoken-mitaka │ │ │ ├── section-keystone-authtoken-v3only │ │ │ ├── section-oslo-cache │ │ │ ├── section-oslo-messaging-rabbit │ │ │ ├── section-oslo-messaging-rabbit-ocata │ │ │ ├── section-oslo-middleware │ │ │ ├── section-oslo-notifications │ │ │ ├── section-placement │ │ │ ├── section-rabbitmq-oslo │ │ │ ├── section-service-user │ │ │ ├── section-zeromq │ │ │ ├── vendor_data.json │ │ │ ├── wsgi-openstack-api.conf │ │ │ └── wsgi-openstack-metadata.conf │ │ ├── templating.py │ │ ├── utils.py │ │ └── vaultlocker.py │ ├── peerstorage │ │ └── __init__.py │ ├── python.py │ └── storage │ │ ├── __init__.py │ │ └── linux │ │ ├── __init__.py │ │ ├── bcache.py │ │ ├── ceph.py │ │ ├── loopback.py │ │ ├── lvm.py │ │ └── utils.py ├── core │ ├── __init__.py │ ├── decorators.py │ ├── files.py │ ├── fstab.py │ ├── hookenv.py │ ├── host.py │ ├── host_factory │ │ ├── __init__.py │ │ ├── centos.py │ │ └── ubuntu.py │ ├── hugepage.py │ ├── kernel.py │ ├── kernel_factory │ │ ├── __init__.py │ │ ├── centos.py │ │ └── ubuntu.py │ ├── services │ │ ├── __init__.py │ │ ├── base.py │ │ └── helpers.py │ ├── strutils.py │ ├── sysctl.py │ ├── templating.py │ └── unitdata.py ├── fetch │ ├── __init__.py │ ├── archiveurl.py │ ├── bzrurl.py │ ├── centos.py │ ├── giturl.py │ ├── python │ │ ├── __init__.py │ │ ├── debug.py │ │ ├── packages.py │ │ ├── rpdb.py │ │ └── version.py │ ├── snap.py │ ├── ubuntu.py │ └── ubuntu_apt_pkg.py ├── osplatform.py └── payload │ ├── __init__.py │ └── execd.py ├── config.yaml ├── copyright ├── files └── .gitkeep ├── hardening.yaml ├── hooks ├── __init__.py ├── amqp-relation-broken ├── amqp-relation-changed ├── amqp-relation-departed ├── amqp-relation-joined ├── certificates-relation-changed ├── certificates-relation-departed ├── certificates-relation-joined ├── cluster-relation-changed ├── cluster-relation-departed ├── cluster-relation-joined ├── config-changed ├── ha-relation-changed ├── ha-relation-joined ├── identity-service-relation-changed ├── identity-service-relation-joined ├── install ├── install.real ├── leader-deposed ├── leader-elected ├── leader-settings-changed ├── nrpe-external-master-relation-changed ├── nrpe-external-master-relation-joined ├── object-store-relation-joined ├── post-series-upgrade ├── pre-series-upgrade ├── rings-consumer-relation-changed ├── rings-consumer-relation-departed ├── rings-consumer-relation-joined ├── rings-distributor-relation-changed ├── rings-distributor-relation-departed ├── rings-distributor-relation-joined ├── start ├── stop ├── swift-storage-relation-broken ├── swift-storage-relation-changed ├── swift-storage-relation-joined ├── swift_hooks.py ├── update-status └── upgrade-charm ├── icon.svg ├── lib ├── __init__.py ├── swift_context.py └── swift_utils.py ├── metadata.yaml ├── osci.yaml ├── rename.sh ├── requirements.txt ├── revision ├── scripts ├── add_to_cluster └── remove_from_cluster ├── setup.cfg ├── swift_manager ├── .stestr.conf ├── manager.py └── test_manager.py ├── templates ├── memcached.conf ├── mitaka │ ├── dispersion.conf │ └── proxy-server.conf ├── ports.conf ├── queens │ ├── dispersion.conf │ └── proxy-server.conf ├── rocky │ └── proxy-server.conf ├── swift-rings ├── swift-rings.conf ├── swift.conf ├── train │ └── proxy-server.conf └── yoga │ └── proxy-server.conf ├── test-requirements.txt ├── tests ├── bundles │ ├── noble-caracal.yaml │ └── overlays │ │ ├── noble-caracal-gr-r1.yaml.j2 │ │ └── noble-caracal-gr-r2.yaml.j2 └── tests.yaml ├── tox.ini └── unit_tests ├── __init__.py ├── swift-test ├── test_actions.py ├── test_actions_openstack_upgrade.py ├── test_swift_context.py ├── test_swift_hooks.py ├── test_swift_utils.py └── test_templates.py /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | .coverage 3 | .testrepository 4 | .stestr 5 | .tox 6 | tags 7 | *.sw[nop] 8 | *.pyc 9 | *.charm 10 | func-results.json 11 | __pycache__ 12 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/charm-swift-proxy.git 5 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | swift-proxy 4 | 5 | 6 | 7 | 8 | 9 | org.python.pydev.PyDevBuilder 10 | 11 | 12 | 13 | 14 | 15 | org.python.pydev.pythonNature 16 | 17 | 18 | -------------------------------------------------------------------------------- /.pydevproject: -------------------------------------------------------------------------------- 1 | 2 | 3 | python 2.7 4 | Default 5 | 6 | /${PROJECT_DIR_NAME}/tests 7 | /${PROJECT_DIR_NAME}/lib 8 | /${PROJECT_DIR_NAME}/actions 9 | /${PROJECT_DIR_NAME}/hooks 10 | 11 | 12 | -------------------------------------------------------------------------------- /.stestr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_path=./unit_tests 3 | top_dir=./ 4 | -------------------------------------------------------------------------------- /.zuul.yaml: -------------------------------------------------------------------------------- 1 | - project: 2 | templates: 3 | - openstack-python3-charm-jobs 4 | - openstack-cover-jobs 5 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | PYTHON := /usr/bin/env python3 2 | 3 | lint: 4 | @tox -e pep8 5 | 6 | test: 7 | @echo Starting unit tests... 8 | @tox -e py27 9 | 10 | functional_test: 11 | @echo Starting Amulet tests... 12 | @tox -e func27 13 | 14 | bin/charm_helpers_sync.py: 15 | @mkdir -p bin 16 | @curl -o bin/charm_helpers_sync.py https://raw.githubusercontent.com/juju/charm-helpers/master/tools/charm_helpers_sync/charm_helpers_sync.py 17 | 18 | sync: bin/charm_helpers_sync.py 19 | @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-hooks.yaml 20 | 21 | publish: lint test 22 | bzr push lp:charms/swift-proxy 23 | bzr push lp:charms/trusty/swift-proxy 24 | 25 | .PHONY: lint unit_test test sync publish 26 | -------------------------------------------------------------------------------- /README.Swift_ring_management: -------------------------------------------------------------------------------- 1 | == Swift Charm Ring Management == 2 | 3 | Swift uses rings and builders to manage data distribution across storage devices 4 | in the cluster. More information on this can be found in the upstream 5 | documentation [0]. 6 | 7 | In order to function correctly, the rings and builders need to be the same 8 | across all swift proxy units and the rings need to be distributed to storage 9 | units. The swift proxy charm achieves this by electing a leader unit and having 10 | that unit manage ring and builder files by updating them when new nodes or 11 | devices are added to the cluster and distributing those files to other nodes in 12 | the cluster. 13 | 14 | Since over time the leader may change, rings syncs use acknowledgements from 15 | peer units to determine whether a synchronisation has completed. This way if 16 | the leader switches to another unit, we are able to know that the new leader 17 | has up-to-date ring and builder files. 18 | 19 | When new devices are added to storage units, the leader proxy unit is notified 20 | and adds them to the ring. Once complete, the leader unit will broadcast a 21 | notification that rings and builders are ready to be synced across the cluster 22 | (only proxy units get builders) and each unit in the cluster should then begin 23 | syncing from the leader and acknowledge receipt. 24 | 25 | During synchronisation, swift-proxy services are stopped in order to avoid 26 | having requests being handled while rings are being updated. 27 | 28 | 29 | [0] http://docs.openstack.org/developer/swift/overview_ring.html 30 | -------------------------------------------------------------------------------- /actions.yaml: -------------------------------------------------------------------------------- 1 | add-user: 2 | description: | 3 | Add a user to swauth. 4 | This adds a given user / pass to swauth. Auth-type must be set to swauth. 5 | Note that swauth is not supported for OpenStack Train and later. 6 | params: 7 | account: 8 | type: string 9 | description: account to add this user to 10 | username: 11 | type: string 12 | description: username for the newly created user 13 | password: 14 | type: string 15 | description: password for the newly created user 16 | required: 17 | - account 18 | - username 19 | - password 20 | pause: 21 | description: | 22 | Pause swift-proxy services. 23 | If the swift-proxy deployment is clustered using the hacluster charm, the 24 | corresponding hacluster unit on the node must first be paused as well. 25 | Not doing so may lead to an interruption of service. 26 | resume: 27 | description: | 28 | Resume swift-proxy services. 29 | If the swift-proxy deployment is clustered using the hacluster charm, the 30 | corresponding hacluster unit on the node must be resumed as well. 31 | openstack-upgrade: 32 | description: | 33 | Perform openstack upgrades. Config option action-managed-upgrade must be 34 | set to True. 35 | diskusage: 36 | description: Run swift-recon -d on the specified unit. Returns values in GB. 37 | remove-devices: 38 | description: | 39 | Removes the device(s) from the ring. This should normally just be used for 40 | a device that has failed. For a device you wish to decommission, it's best 41 | to set its weight to 0, wait for it to drain all its data, then use this 42 | remove-from-ring action. 43 | params: 44 | ring: 45 | type: string 46 | description: | 47 | Swift ring to remove the device(s) from. Valid options are 'account', 48 | 'container', 'object' or 'all'. 49 | search-value: 50 | type: string 51 | description: | 52 | The search-value can be of the form: 53 | . 54 | drz-:R:/ 55 | _ 56 | . 57 | Where and are replication ip and port. Any part is 58 | optional, but you must include at least one part. 59 | required: 60 | - ring 61 | - search-value 62 | set-weight: 63 | description: Sets the device's weight. 64 | params: 65 | ring: 66 | type: string 67 | description: | 68 | Swift ring to set the weight for. Valid options are 'account', 69 | 'container', 'object' or 'all'. 70 | search-value: 71 | type: string 72 | description: | 73 | The search-value can be of the form: 74 | . 75 | drz-:R:/ 76 | _ 77 | . 78 | Where and are replication ip and port. Any part is 79 | optional, but you must include at least one part. 80 | weight: 81 | type: number 82 | description: The device's weight 83 | required: 84 | - ring 85 | - search-value 86 | - weight 87 | dispersion-populate: 88 | description: Run swift-dispersion-populate command on the specified unit. 89 | dispersion-report: 90 | description: Run swift-dispersion-report command on the specified unit. 91 | -------------------------------------------------------------------------------- /actions/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /actions/add-user: -------------------------------------------------------------------------------- 1 | add_user.py -------------------------------------------------------------------------------- /actions/add_user.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright 2016 Canonical Ltd 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import os 18 | import sys 19 | 20 | _path = os.path.dirname(os.path.realpath(__file__)) 21 | _parent = os.path.abspath(os.path.join(_path, '..')) 22 | 23 | 24 | def _add_path(path): 25 | if path not in sys.path: 26 | sys.path.insert(1, path) 27 | 28 | 29 | _add_path(_parent) 30 | 31 | 32 | from subprocess import ( 33 | check_call, 34 | CalledProcessError 35 | ) 36 | 37 | from charmhelpers.core.hookenv import ( 38 | action_get, 39 | config, 40 | action_set, 41 | action_fail, 42 | leader_get, 43 | log, 44 | ) 45 | 46 | from charmhelpers.contrib.openstack.utils import ( 47 | os_release, 48 | CompareOpenStackReleases, 49 | ) 50 | 51 | from charmhelpers.contrib.hahelpers.cluster import ( 52 | determine_api_port, 53 | ) 54 | 55 | from lib.swift_utils import ( 56 | try_initialize_swauth, 57 | ) 58 | 59 | 60 | def add_user(): 61 | """Add a swauth user to swift.""" 62 | if config('auth-type') == 'swauth': 63 | cmp_openstack = CompareOpenStackReleases(os_release('swift')) 64 | if cmp_openstack >= 'train': 65 | message = "swauth is not supported for OpenStack Train and later" 66 | log(message) 67 | action_fail(message) 68 | return None 69 | try_initialize_swauth() 70 | account = action_get('account') 71 | username = action_get('username') 72 | password = action_get('password') 73 | bind_port = config('bind-port') 74 | bind_port = determine_api_port(bind_port, singlenode_mode=True) 75 | success = True 76 | try: 77 | check_call([ 78 | "swauth-add-user", 79 | "-A", "http://localhost:{}/auth/".format(bind_port), 80 | "-K", leader_get('swauth-admin-key'), 81 | "-a", account, username, password]) 82 | except CalledProcessError as e: 83 | success = False 84 | log("Has a problem adding user: {}".format(e.output)) 85 | action_fail( 86 | "Adding user {} failed with: \"{}\"" 87 | .format(username, str(e))) 88 | if success: 89 | message = "Successfully added the user {}".format(username) 90 | action_set({ 91 | 'add-user.result': 'Success', 92 | 'add-user.message': message, 93 | }) 94 | 95 | 96 | if __name__ == '__main__': 97 | add_user() 98 | -------------------------------------------------------------------------------- /actions/diskusage: -------------------------------------------------------------------------------- 1 | actions.py -------------------------------------------------------------------------------- /actions/dispersion-populate: -------------------------------------------------------------------------------- 1 | actions.py -------------------------------------------------------------------------------- /actions/dispersion-report: -------------------------------------------------------------------------------- 1 | actions.py -------------------------------------------------------------------------------- /actions/openstack-upgrade: -------------------------------------------------------------------------------- 1 | openstack_upgrade.py -------------------------------------------------------------------------------- /actions/openstack_upgrade.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright 2016 Canonical Ltd 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | import os 17 | import sys 18 | 19 | _path = os.path.dirname(os.path.realpath(__file__)) 20 | _parent = os.path.abspath(os.path.join(_path, '..')) 21 | 22 | 23 | def _add_path(path): 24 | if path not in sys.path: 25 | sys.path.insert(1, path) 26 | 27 | 28 | _add_path(_parent) 29 | 30 | 31 | from charmhelpers.contrib.openstack.utils import ( 32 | do_action_openstack_upgrade, 33 | ) 34 | 35 | from hooks.swift_hooks import ( 36 | config_changed, 37 | CONFIGS, 38 | ) 39 | 40 | from lib.swift_utils import ( 41 | do_openstack_upgrade, 42 | ) 43 | 44 | 45 | def openstack_upgrade(): 46 | """Upgrade packages to config-set Openstack version. 47 | 48 | If the charm was installed from source we cannot upgrade it. 49 | For backwards compatibility a config flag must be set for this 50 | code to run, otherwise a full service level upgrade will fire 51 | on config-changed.""" 52 | 53 | if (do_action_openstack_upgrade('swift', 54 | do_openstack_upgrade, 55 | CONFIGS)): 56 | config_changed() 57 | 58 | 59 | if __name__ == '__main__': 60 | openstack_upgrade() 61 | -------------------------------------------------------------------------------- /actions/pause: -------------------------------------------------------------------------------- 1 | actions.py -------------------------------------------------------------------------------- /actions/remove-devices: -------------------------------------------------------------------------------- 1 | actions.py -------------------------------------------------------------------------------- /actions/resume: -------------------------------------------------------------------------------- 1 | actions.py -------------------------------------------------------------------------------- /actions/set-weight: -------------------------------------------------------------------------------- 1 | actions.py -------------------------------------------------------------------------------- /bindep.txt: -------------------------------------------------------------------------------- 1 | libffi-dev [platform:dpkg] 2 | libxml2-dev [platform:dpkg] 3 | libxslt1-dev [platform:dpkg] 4 | -------------------------------------------------------------------------------- /charm-helpers-hooks.yaml: -------------------------------------------------------------------------------- 1 | repo: https://github.com/juju/charm-helpers 2 | destination: charmhelpers 3 | include: 4 | - core 5 | - osplatform 6 | - cli 7 | - fetch 8 | - contrib.openstack|inc=* 9 | - contrib.storage.linux 10 | - contrib.hardware.pci 11 | - contrib.hahelpers: 12 | - apache 13 | - cluster 14 | - payload.execd 15 | - contrib.network.ip 16 | - contrib.peerstorage 17 | - contrib.python 18 | - contrib.charmsupport 19 | - contrib.hardening|inc=* 20 | - contrib.openstack.policyd 21 | -------------------------------------------------------------------------------- /charmcraft.yaml: -------------------------------------------------------------------------------- 1 | type: charm 2 | 3 | parts: 4 | charm: 5 | plugin: dump 6 | source: . 7 | 8 | base: ubuntu@24.04 9 | platforms: 10 | amd64: 11 | build-on: amd64 12 | build-for: amd64 13 | arm64: 14 | build-on: arm64 15 | build-for: arm64 16 | ppc64el: 17 | build-on: ppc64el 18 | build-for: ppc64el 19 | s390x: 20 | build-on: s390x 21 | build-for: s390x 22 | -------------------------------------------------------------------------------- /charmhelpers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # Bootstrap charm-helpers, installing its dependencies if necessary using 16 | # only standard libraries. 17 | import functools 18 | import inspect 19 | import subprocess 20 | 21 | 22 | try: 23 | import yaml # NOQA:F401 24 | except ImportError: 25 | subprocess.check_call(['apt-get', 'install', '-y', 'python3-yaml']) 26 | import yaml # NOQA:F401 27 | 28 | 29 | # Holds a list of mapping of mangled function names that have been deprecated 30 | # using the @deprecate decorator below. This is so that the warning is only 31 | # printed once for each usage of the function. 32 | __deprecated_functions = {} 33 | 34 | 35 | def deprecate(warning, date=None, log=None): 36 | """Add a deprecation warning the first time the function is used. 37 | 38 | The date which is a string in semi-ISO8660 format indicates the year-month 39 | that the function is officially going to be removed. 40 | 41 | usage: 42 | 43 | @deprecate('use core/fetch/add_source() instead', '2017-04') 44 | def contributed_add_source_thing(...): 45 | ... 46 | 47 | And it then prints to the log ONCE that the function is deprecated. 48 | The reason for passing the logging function (log) is so that hookenv.log 49 | can be used for a charm if needed. 50 | 51 | :param warning: String to indicate what is to be used instead. 52 | :param date: Optional string in YYYY-MM format to indicate when the 53 | function will definitely (probably) be removed. 54 | :param log: The log function to call in order to log. If None, logs to 55 | stdout 56 | """ 57 | def wrap(f): 58 | 59 | @functools.wraps(f) 60 | def wrapped_f(*args, **kwargs): 61 | try: 62 | module = inspect.getmodule(f) 63 | file = inspect.getsourcefile(f) 64 | lines = inspect.getsourcelines(f) 65 | f_name = "{}-{}-{}..{}-{}".format( 66 | module.__name__, file, lines[0], lines[-1], f.__name__) 67 | except (IOError, TypeError): 68 | # assume it was local, so just use the name of the function 69 | f_name = f.__name__ 70 | if f_name not in __deprecated_functions: 71 | __deprecated_functions[f_name] = True 72 | s = "DEPRECATION WARNING: Function {} is being removed".format( 73 | f.__name__) 74 | if date: 75 | s = "{} on/around {}".format(s, date) 76 | if warning: 77 | s = "{} : {}".format(s, warning) 78 | if log: 79 | log(s) 80 | else: 81 | print(s) 82 | return f(*args, **kwargs) 83 | return wrapped_f 84 | return wrap 85 | -------------------------------------------------------------------------------- /charmhelpers/cli/benchmark.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from . import cmdline 16 | from charmhelpers.contrib.benchmark import Benchmark 17 | 18 | 19 | @cmdline.subcommand(command_name='benchmark-start') 20 | def start(): 21 | Benchmark.start() 22 | 23 | 24 | @cmdline.subcommand(command_name='benchmark-finish') 25 | def finish(): 26 | Benchmark.finish() 27 | 28 | 29 | @cmdline.subcommand_builder('benchmark-composite', description="Set the benchmark composite score") 30 | def service(subparser): 31 | subparser.add_argument("value", help="The composite score.") 32 | subparser.add_argument("units", help="The units the composite score represents, i.e., 'reads/sec'.") 33 | subparser.add_argument("direction", help="'asc' if a lower score is better, 'desc' if a higher score is better.") 34 | return Benchmark.set_composite_score 35 | -------------------------------------------------------------------------------- /charmhelpers/cli/commands.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """ 16 | This module loads sub-modules into the python runtime so they can be 17 | discovered via the inspect module. In order to prevent flake8 from (rightfully) 18 | telling us these are unused modules, throw a ' # noqa' at the end of each import 19 | so that the warning is suppressed. 20 | """ 21 | 22 | from . import CommandLine # noqa 23 | 24 | """ 25 | Import the sub-modules which have decorated subcommands to register with chlp. 26 | """ 27 | from . import host # noqa 28 | from . import benchmark # noqa 29 | from . import unitdata # noqa 30 | from . import hookenv # noqa 31 | -------------------------------------------------------------------------------- /charmhelpers/cli/hookenv.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from . import cmdline 16 | from charmhelpers.core import hookenv 17 | 18 | 19 | cmdline.subcommand('relation-id')(hookenv.relation_id._wrapped) 20 | cmdline.subcommand('service-name')(hookenv.service_name) 21 | cmdline.subcommand('remote-service-name')(hookenv.remote_service_name._wrapped) 22 | -------------------------------------------------------------------------------- /charmhelpers/cli/host.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from . import cmdline 16 | from charmhelpers.core import host 17 | 18 | 19 | @cmdline.subcommand() 20 | def mounts(): 21 | "List mounts" 22 | return host.mounts() 23 | 24 | 25 | @cmdline.subcommand_builder('service', description="Control system services") 26 | def service(subparser): 27 | subparser.add_argument("action", help="The action to perform (start, stop, etc...)") 28 | subparser.add_argument("service_name", help="Name of the service to control") 29 | return host.service 30 | -------------------------------------------------------------------------------- /charmhelpers/cli/unitdata.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from . import cmdline 16 | from charmhelpers.core import unitdata 17 | 18 | 19 | @cmdline.subcommand_builder('unitdata', description="Store and retrieve data") 20 | def unitdata_cmd(subparser): 21 | nested = subparser.add_subparsers() 22 | 23 | get_cmd = nested.add_parser('get', help='Retrieve data') 24 | get_cmd.add_argument('key', help='Key to retrieve the value of') 25 | get_cmd.set_defaults(action='get', value=None) 26 | 27 | getrange_cmd = nested.add_parser('getrange', help='Retrieve multiple data') 28 | getrange_cmd.add_argument('key', metavar='prefix', 29 | help='Prefix of the keys to retrieve') 30 | getrange_cmd.set_defaults(action='getrange', value=None) 31 | 32 | set_cmd = nested.add_parser('set', help='Store data') 33 | set_cmd.add_argument('key', help='Key to set') 34 | set_cmd.add_argument('value', help='Value to store') 35 | set_cmd.set_defaults(action='set') 36 | 37 | def _unitdata_cmd(action, key, value): 38 | if action == 'get': 39 | return unitdata.kv().get(key) 40 | elif action == 'getrange': 41 | return unitdata.kv().getrange(key) 42 | elif action == 'set': 43 | unitdata.kv().set(key, value) 44 | unitdata.kv().flush() 45 | return '' 46 | return _unitdata_cmd 47 | -------------------------------------------------------------------------------- /charmhelpers/contrib/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/charmsupport/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hahelpers/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hahelpers/apache.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # 16 | # Copyright 2012 Canonical Ltd. 17 | # 18 | # This file is sourced from lp:openstack-charm-helpers 19 | # 20 | # Authors: 21 | # James Page 22 | # Adam Gandelman 23 | # 24 | 25 | import os 26 | 27 | from charmhelpers.core import host 28 | from charmhelpers.core.hookenv import ( 29 | config as config_get, 30 | relation_get, 31 | relation_ids, 32 | related_units as relation_list, 33 | log, 34 | INFO, 35 | ) 36 | 37 | # This file contains the CA cert from the charms ssl_ca configuration 38 | # option, in future the file name should be updated reflect that. 39 | CONFIG_CA_CERT_FILE = 'keystone_juju_ca_cert' 40 | 41 | 42 | def get_cert(cn=None): 43 | # TODO: deal with multiple https endpoints via charm config 44 | cert = config_get('ssl_cert') 45 | key = config_get('ssl_key') 46 | if not (cert and key): 47 | log("Inspecting identity-service relations for SSL certificate.", 48 | level=INFO) 49 | cert = key = None 50 | if cn: 51 | ssl_cert_attr = 'ssl_cert_{}'.format(cn) 52 | ssl_key_attr = 'ssl_key_{}'.format(cn) 53 | else: 54 | ssl_cert_attr = 'ssl_cert' 55 | ssl_key_attr = 'ssl_key' 56 | for r_id in relation_ids('identity-service'): 57 | for unit in relation_list(r_id): 58 | if not cert: 59 | cert = relation_get(ssl_cert_attr, 60 | rid=r_id, unit=unit) 61 | if not key: 62 | key = relation_get(ssl_key_attr, 63 | rid=r_id, unit=unit) 64 | return (cert, key) 65 | 66 | 67 | def get_ca_cert(): 68 | ca_cert = config_get('ssl_ca') 69 | if ca_cert is None: 70 | log("Inspecting identity-service relations for CA SSL certificate.", 71 | level=INFO) 72 | for r_id in (relation_ids('identity-service') + 73 | relation_ids('identity-credentials')): 74 | for unit in relation_list(r_id): 75 | if ca_cert is None: 76 | ca_cert = relation_get('ca_cert', 77 | rid=r_id, unit=unit) 78 | return ca_cert 79 | 80 | 81 | def retrieve_ca_cert(cert_file): 82 | cert = None 83 | if os.path.isfile(cert_file): 84 | with open(cert_file, 'rb') as crt: 85 | cert = crt.read() 86 | return cert 87 | 88 | 89 | def install_ca_cert(ca_cert): 90 | host.install_ca_cert(ca_cert, CONFIG_CA_CERT_FILE) 91 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/README.hardening.md: -------------------------------------------------------------------------------- 1 | # Juju charm-helpers hardening library 2 | 3 | ## Description 4 | 5 | This library provides multiple implementations of system and application 6 | hardening that conform to the standards of http://hardening.io/. 7 | 8 | Current implementations include: 9 | 10 | * OS 11 | * SSH 12 | * MySQL 13 | * Apache 14 | 15 | ## Requirements 16 | 17 | * Juju Charms 18 | 19 | ## Usage 20 | 21 | 1. Synchronise this library into your charm and add the harden() decorator 22 | (from contrib.hardening.harden) to any functions or methods you want to use 23 | to trigger hardening of your application/system. 24 | 25 | 2. Add a config option called 'harden' to your charm config.yaml and set it to 26 | a space-delimited list of hardening modules you want to run e.g. "os ssh" 27 | 28 | 3. Override any config defaults (contrib.hardening.defaults) by adding a file 29 | called hardening.yaml to your charm root containing the name(s) of the 30 | modules whose settings you want override at root level and then any settings 31 | with overrides e.g. 32 | 33 | os: 34 | general: 35 | desktop_enable: True 36 | 37 | 4. Now just run your charm as usual and hardening will be applied each time the 38 | hook runs. 39 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/apache/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from os import path 16 | 17 | TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates') 18 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/apache/checks/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.core.hookenv import ( 16 | log, 17 | DEBUG, 18 | ) 19 | from charmhelpers.contrib.hardening.apache.checks import config 20 | 21 | 22 | def run_apache_checks(): 23 | log("Starting Apache hardening checks.", level=DEBUG) 24 | checks = config.get_audits() 25 | for check in checks: 26 | log("Running '%s' check" % (check.__class__.__name__), level=DEBUG) 27 | check.ensure_compliance() 28 | 29 | log("Apache hardening checks complete.", level=DEBUG) 30 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/apache/templates/99-hardening.conf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | 6 | 7 | 8 | # http://httpd.apache.org/docs/2.4/upgrading.html 9 | {% if apache_version > '2.2' -%} 10 | Require all granted 11 | {% else -%} 12 | Order Allow,Deny 13 | Deny from all 14 | {% endif %} 15 | 16 | 17 | 18 | 19 | Options -Indexes -FollowSymLinks 20 | AllowOverride None 21 | 22 | 23 | 24 | Options -Indexes -FollowSymLinks 25 | AllowOverride None 26 | 27 | 28 | TraceEnable {{ traceenable }} 29 | ServerTokens {{ servertokens }} 30 | 31 | SSLHonorCipherOrder {{ honor_cipher_order }} 32 | SSLCipherSuite {{ cipher_suite }} 33 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/apache/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-swift-proxy/b4aa3c978b8be71585d43475598394451dbad11c/charmhelpers/contrib/hardening/apache/templates/__init__.py -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/apache/templates/alias.conf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | 6 | # 7 | # Aliases: Add here as many aliases as you need (with no limit). The format is 8 | # Alias fakename realname 9 | # 10 | # Note that if you include a trailing / on fakename then the server will 11 | # require it to be present in the URL. So "/icons" isn't aliased in this 12 | # example, only "/icons/". If the fakename is slash-terminated, then the 13 | # realname must also be slash terminated, and if the fakename omits the 14 | # trailing slash, the realname must also omit it. 15 | # 16 | # We include the /icons/ alias for FancyIndexed directory listings. If 17 | # you do not use FancyIndexing, you may comment this out. 18 | # 19 | Alias /icons/ "{{ apache_icondir }}/" 20 | 21 | 22 | Options -Indexes -MultiViews -FollowSymLinks 23 | AllowOverride None 24 | {% if apache_version == '2.4' -%} 25 | Require all granted 26 | {% else -%} 27 | Order allow,deny 28 | Allow from all 29 | {% endif %} 30 | 31 | 32 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/audits/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | class BaseAudit(object): # NO-QA 17 | """Base class for hardening checks. 18 | 19 | The lifecycle of a hardening check is to first check to see if the system 20 | is in compliance for the specified check. If it is not in compliance, the 21 | check method will return a value which will be supplied to the. 22 | """ 23 | def __init__(self, *args, **kwargs): 24 | self.unless = kwargs.get('unless', None) 25 | super(BaseAudit, self).__init__() 26 | 27 | def ensure_compliance(self): 28 | """Checks to see if the current hardening check is in compliance or 29 | not. 30 | 31 | If the check that is performed is not in compliance, then an exception 32 | should be raised. 33 | """ 34 | pass 35 | 36 | def _take_action(self): 37 | """Determines whether to perform the action or not. 38 | 39 | Checks whether or not an action should be taken. This is determined by 40 | the truthy value for the unless parameter. If unless is a callback 41 | method, it will be invoked with no parameters in order to determine 42 | whether or not the action should be taken. Otherwise, the truthy value 43 | of the unless attribute will determine if the action should be 44 | performed. 45 | """ 46 | # Do the action if there isn't an unless override. 47 | if self.unless is None: 48 | return True 49 | 50 | # Invoke the callback if there is one. 51 | if hasattr(self.unless, '__call__'): 52 | return not self.unless() 53 | 54 | return not self.unless 55 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/audits/apache.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import re 16 | import subprocess 17 | 18 | from charmhelpers.core.hookenv import ( 19 | log, 20 | INFO, 21 | ERROR, 22 | ) 23 | 24 | from charmhelpers.contrib.hardening.audits import BaseAudit 25 | 26 | 27 | class DisabledModuleAudit(BaseAudit): 28 | """Audits Apache2 modules. 29 | 30 | Determines if the apache2 modules are enabled. If the modules are enabled 31 | then they are removed in the ensure_compliance. 32 | """ 33 | def __init__(self, modules): 34 | if modules is None: 35 | self.modules = [] 36 | elif isinstance(modules, str): 37 | self.modules = [modules] 38 | else: 39 | self.modules = modules 40 | 41 | def ensure_compliance(self): 42 | """Ensures that the modules are not loaded.""" 43 | if not self.modules: 44 | return 45 | 46 | try: 47 | loaded_modules = self._get_loaded_modules() 48 | non_compliant_modules = [] 49 | for module in self.modules: 50 | if module in loaded_modules: 51 | log("Module '%s' is enabled but should not be." % 52 | (module), level=INFO) 53 | non_compliant_modules.append(module) 54 | 55 | if len(non_compliant_modules) == 0: 56 | return 57 | 58 | for module in non_compliant_modules: 59 | self._disable_module(module) 60 | self._restart_apache() 61 | except subprocess.CalledProcessError as e: 62 | log('Error occurred auditing apache module compliance. ' 63 | 'This may have been already reported. ' 64 | 'Output is: %s' % e.output, level=ERROR) 65 | 66 | @staticmethod 67 | def _get_loaded_modules(): 68 | """Returns the modules which are enabled in Apache.""" 69 | output = subprocess.check_output(['apache2ctl', '-M']).decode('utf-8') 70 | modules = [] 71 | for line in output.splitlines(): 72 | # Each line of the enabled module output looks like: 73 | # module_name (static|shared) 74 | # Plus a header line at the top of the output which is stripped 75 | # out by the regex. 76 | matcher = re.search(r'^ (\S*)_module (\S*)', line) 77 | if matcher: 78 | modules.append(matcher.group(1)) 79 | return modules 80 | 81 | @staticmethod 82 | def _disable_module(module): 83 | """Disables the specified module in Apache.""" 84 | try: 85 | subprocess.check_call(['a2dismod', module]) 86 | except subprocess.CalledProcessError as e: 87 | # Note: catch error here to allow the attempt of disabling 88 | # multiple modules in one go rather than failing after the 89 | # first module fails. 90 | log('Error occurred disabling module %s. ' 91 | 'Output is: %s' % (module, e.output), level=ERROR) 92 | 93 | @staticmethod 94 | def _restart_apache(): 95 | """Restarts the apache process""" 96 | subprocess.check_output(['service', 'apache2', 'restart']) 97 | 98 | @staticmethod 99 | def is_ssl_enabled(): 100 | """Check if SSL module is enabled or not""" 101 | return 'ssl' in DisabledModuleAudit._get_loaded_modules() 102 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/audits/apt.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.fetch import ( 16 | apt_cache, 17 | apt_purge 18 | ) 19 | from charmhelpers.core.hookenv import ( 20 | log, 21 | DEBUG, 22 | WARNING, 23 | ) 24 | from charmhelpers.contrib.hardening.audits import BaseAudit 25 | from charmhelpers.fetch import ubuntu_apt_pkg as apt_pkg 26 | 27 | 28 | class AptConfig(BaseAudit): 29 | 30 | def __init__(self, config, **kwargs): 31 | self.config = config 32 | 33 | def verify_config(self): 34 | apt_pkg.init() 35 | for cfg in self.config: 36 | value = apt_pkg.config.get(cfg['key'], cfg.get('default', '')) 37 | if value and value != cfg['expected']: 38 | log("APT config '%s' has unexpected value '%s' " 39 | "(expected='%s')" % 40 | (cfg['key'], value, cfg['expected']), level=WARNING) 41 | 42 | def ensure_compliance(self): 43 | self.verify_config() 44 | 45 | 46 | class RestrictedPackages(BaseAudit): 47 | """Class used to audit restricted packages on the system.""" 48 | 49 | def __init__(self, pkgs, **kwargs): 50 | super(RestrictedPackages, self).__init__(**kwargs) 51 | if isinstance(pkgs, str) or not hasattr(pkgs, '__iter__'): 52 | self.pkgs = pkgs.split() 53 | else: 54 | self.pkgs = pkgs 55 | 56 | def ensure_compliance(self): 57 | cache = apt_cache() 58 | 59 | for p in self.pkgs: 60 | if p not in cache: 61 | continue 62 | 63 | pkg = cache[p] 64 | if not self.is_virtual_package(pkg): 65 | if not pkg.current_ver: 66 | log("Package '%s' is not installed." % pkg.name, 67 | level=DEBUG) 68 | continue 69 | else: 70 | log("Restricted package '%s' is installed" % pkg.name, 71 | level=WARNING) 72 | self.delete_package(cache, pkg) 73 | else: 74 | log("Checking restricted virtual package '%s' provides" % 75 | pkg.name, level=DEBUG) 76 | self.delete_package(cache, pkg) 77 | 78 | def delete_package(self, cache, pkg): 79 | """Deletes the package from the system. 80 | 81 | Deletes the package form the system, properly handling virtual 82 | packages. 83 | 84 | :param cache: the apt cache 85 | :param pkg: the package to remove 86 | """ 87 | if self.is_virtual_package(pkg): 88 | log("Package '%s' appears to be virtual - purging provides" % 89 | pkg.name, level=DEBUG) 90 | for _p in pkg.provides_list: 91 | self.delete_package(cache, _p[2].parent_pkg) 92 | elif not pkg.current_ver: 93 | log("Package '%s' not installed" % pkg.name, level=DEBUG) 94 | return 95 | else: 96 | log("Purging package '%s'" % pkg.name, level=DEBUG) 97 | apt_purge(pkg.name) 98 | 99 | def is_virtual_package(self, pkg): 100 | return (pkg.get('has_provides', False) and 101 | not pkg.get('has_versions', False)) 102 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/defaults/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-swift-proxy/b4aa3c978b8be71585d43475598394451dbad11c/charmhelpers/contrib/hardening/defaults/__init__.py -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/defaults/apache.yaml: -------------------------------------------------------------------------------- 1 | # NOTE: this file contains the default configuration for the 'apache' hardening 2 | # code. If you want to override any settings you must add them to a file 3 | # called hardening.yaml in the root directory of your charm using the 4 | # name 'apache' as the root key followed by any of the following with new 5 | # values. 6 | 7 | common: 8 | apache_dir: '/etc/apache2' 9 | 10 | hardening: 11 | traceenable: 'off' 12 | allowed_http_methods: "GET POST" 13 | modules_to_disable: [ cgi, cgid ] 14 | servertokens: 'Prod' 15 | honor_cipher_order: 'on' 16 | cipher_suite: 'ALL:+MEDIUM:+HIGH:!LOW:!MD5:!RC4:!eNULL:!aNULL:!3DES' 17 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/defaults/apache.yaml.schema: -------------------------------------------------------------------------------- 1 | # NOTE: this schema must contain all valid keys from it's associated defaults 2 | # file. It is used to validate user-provided overrides. 3 | common: 4 | apache_dir: 5 | traceenable: 6 | 7 | hardening: 8 | allowed_http_methods: 9 | modules_to_disable: 10 | servertokens: 11 | honor_cipher_order: 12 | cipher_suite: 13 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/defaults/mysql.yaml: -------------------------------------------------------------------------------- 1 | # NOTE: this file contains the default configuration for the 'mysql' hardening 2 | # code. If you want to override any settings you must add them to a file 3 | # called hardening.yaml in the root directory of your charm using the 4 | # name 'mysql' as the root key followed by any of the following with new 5 | # values. 6 | 7 | hardening: 8 | mysql-conf: /etc/mysql/my.cnf 9 | hardening-conf: /etc/mysql/conf.d/hardening.cnf 10 | 11 | security: 12 | # @see http://www.symantec.com/connect/articles/securing-mysql-step-step 13 | # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_chroot 14 | chroot: None 15 | 16 | # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_safe-user-create 17 | safe-user-create: 1 18 | 19 | # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_secure-auth 20 | secure-auth: 1 21 | 22 | # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_symbolic-links 23 | skip-symbolic-links: 1 24 | 25 | # @see http://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_skip-show-database 26 | skip-show-database: True 27 | 28 | # @see http://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_local_infile 29 | local-infile: 0 30 | 31 | # @see https://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_allow-suspicious-udfs 32 | allow-suspicious-udfs: 0 33 | 34 | # @see https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html#sysvar_automatic_sp_privileges 35 | automatic-sp-privileges: 0 36 | 37 | # @see https://dev.mysql.com/doc/refman/5.7/en/server-options.html#option_mysqld_secure-file-priv 38 | secure-file-priv: /tmp 39 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/defaults/mysql.yaml.schema: -------------------------------------------------------------------------------- 1 | # NOTE: this schema must contain all valid keys from it's associated defaults 2 | # file. It is used to validate user-provided overrides. 3 | hardening: 4 | mysql-conf: 5 | hardening-conf: 6 | security: 7 | chroot: 8 | safe-user-create: 9 | secure-auth: 10 | skip-symbolic-links: 11 | skip-show-database: 12 | local-infile: 13 | allow-suspicious-udfs: 14 | automatic-sp-privileges: 15 | secure-file-priv: 16 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/defaults/os.yaml: -------------------------------------------------------------------------------- 1 | # NOTE: this file contains the default configuration for the 'os' hardening 2 | # code. If you want to override any settings you must add them to a file 3 | # called hardening.yaml in the root directory of your charm using the 4 | # name 'os' as the root key followed by any of the following with new 5 | # values. 6 | 7 | general: 8 | desktop_enable: False # (type:boolean) 9 | 10 | environment: 11 | extra_user_paths: [] 12 | umask: 027 13 | root_path: / 14 | 15 | auth: 16 | pw_max_age: 60 17 | # discourage password cycling 18 | pw_min_age: 7 19 | retries: 5 20 | lockout_time: 600 21 | timeout: 60 22 | allow_homeless: False # (type:boolean) 23 | pam_passwdqc_enable: True # (type:boolean) 24 | pam_passwdqc_options: 'min=disabled,disabled,16,12,8' 25 | root_ttys: 26 | console 27 | tty1 28 | tty2 29 | tty3 30 | tty4 31 | tty5 32 | tty6 33 | uid_min: 1000 34 | gid_min: 1000 35 | sys_uid_min: 100 36 | sys_uid_max: 999 37 | sys_gid_min: 100 38 | sys_gid_max: 999 39 | chfn_restrict: 40 | 41 | security: 42 | users_allow: [] 43 | suid_sgid_enforce: True # (type:boolean) 44 | # user-defined blacklist and whitelist 45 | suid_sgid_blacklist: [] 46 | suid_sgid_whitelist: [] 47 | # if this is True, remove any suid/sgid bits from files that were not in the whitelist 48 | suid_sgid_dry_run_on_unknown: False # (type:boolean) 49 | suid_sgid_remove_from_unknown: False # (type:boolean) 50 | # remove packages with known issues 51 | packages_clean: True # (type:boolean) 52 | packages_list: 53 | xinetd 54 | inetd 55 | ypserv 56 | telnet-server 57 | rsh-server 58 | rsync 59 | kernel_enable_module_loading: True # (type:boolean) 60 | kernel_enable_core_dump: False # (type:boolean) 61 | ssh_tmout: 300 62 | 63 | sysctl: 64 | kernel_secure_sysrq: 244 # 4 + 16 + 32 + 64 + 128 65 | kernel_enable_sysrq: False # (type:boolean) 66 | forwarding: False # (type:boolean) 67 | ipv6_enable: False # (type:boolean) 68 | arp_restricted: True # (type:boolean) 69 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/defaults/os.yaml.schema: -------------------------------------------------------------------------------- 1 | # NOTE: this schema must contain all valid keys from it's associated defaults 2 | # file. It is used to validate user-provided overrides. 3 | general: 4 | desktop_enable: 5 | environment: 6 | extra_user_paths: 7 | umask: 8 | root_path: 9 | auth: 10 | pw_max_age: 11 | pw_min_age: 12 | retries: 13 | lockout_time: 14 | timeout: 15 | allow_homeless: 16 | pam_passwdqc_enable: 17 | pam_passwdqc_options: 18 | root_ttys: 19 | uid_min: 20 | gid_min: 21 | sys_uid_min: 22 | sys_uid_max: 23 | sys_gid_min: 24 | sys_gid_max: 25 | chfn_restrict: 26 | security: 27 | users_allow: 28 | suid_sgid_enforce: 29 | suid_sgid_blacklist: 30 | suid_sgid_whitelist: 31 | suid_sgid_dry_run_on_unknown: 32 | suid_sgid_remove_from_unknown: 33 | packages_clean: 34 | packages_list: 35 | kernel_enable_module_loading: 36 | kernel_enable_core_dump: 37 | ssh_tmout: 38 | sysctl: 39 | kernel_secure_sysrq: 40 | kernel_enable_sysrq: 41 | forwarding: 42 | ipv6_enable: 43 | arp_restricted: 44 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/defaults/ssh.yaml: -------------------------------------------------------------------------------- 1 | # NOTE: this file contains the default configuration for the 'ssh' hardening 2 | # code. If you want to override any settings you must add them to a file 3 | # called hardening.yaml in the root directory of your charm using the 4 | # name 'ssh' as the root key followed by any of the following with new 5 | # values. 6 | 7 | common: 8 | service_name: 'ssh' 9 | network_ipv6_enable: False # (type:boolean) 10 | ports: [22] 11 | remote_hosts: [] 12 | 13 | client: 14 | package: 'openssh-client' 15 | cbc_required: False # (type:boolean) 16 | weak_hmac: False # (type:boolean) 17 | weak_kex: False # (type:boolean) 18 | roaming: False 19 | password_authentication: 'no' 20 | 21 | server: 22 | host_key_files: ['/etc/ssh/ssh_host_rsa_key', '/etc/ssh/ssh_host_dsa_key', 23 | '/etc/ssh/ssh_host_ecdsa_key'] 24 | cbc_required: False # (type:boolean) 25 | weak_hmac: False # (type:boolean) 26 | weak_kex: False # (type:boolean) 27 | allow_root_with_key: False # (type:boolean) 28 | allow_tcp_forwarding: 'no' 29 | allow_agent_forwarding: 'no' 30 | allow_x11_forwarding: 'no' 31 | use_privilege_separation: 'sandbox' 32 | listen_to: ['0.0.0.0'] 33 | use_pam: 'no' 34 | package: 'openssh-server' 35 | password_authentication: 'no' 36 | alive_interval: '600' 37 | alive_count: '3' 38 | sftp_enable: False # (type:boolean) 39 | sftp_group: 'sftponly' 40 | sftp_chroot: '/home/%u' 41 | deny_users: [] 42 | allow_users: [] 43 | deny_groups: [] 44 | allow_groups: [] 45 | print_motd: 'no' 46 | print_last_log: 'no' 47 | use_dns: 'no' 48 | max_auth_tries: 2 49 | max_sessions: 10 50 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/defaults/ssh.yaml.schema: -------------------------------------------------------------------------------- 1 | # NOTE: this schema must contain all valid keys from it's associated defaults 2 | # file. It is used to validate user-provided overrides. 3 | common: 4 | service_name: 5 | network_ipv6_enable: 6 | ports: 7 | remote_hosts: 8 | client: 9 | package: 10 | cbc_required: 11 | weak_hmac: 12 | weak_kex: 13 | roaming: 14 | password_authentication: 15 | server: 16 | host_key_files: 17 | cbc_required: 18 | weak_hmac: 19 | weak_kex: 20 | allow_root_with_key: 21 | allow_tcp_forwarding: 22 | allow_agent_forwarding: 23 | allow_x11_forwarding: 24 | use_privilege_separation: 25 | listen_to: 26 | use_pam: 27 | package: 28 | password_authentication: 29 | alive_interval: 30 | alive_count: 31 | sftp_enable: 32 | sftp_group: 33 | sftp_chroot: 34 | deny_users: 35 | allow_users: 36 | deny_groups: 37 | allow_groups: 38 | print_motd: 39 | print_last_log: 40 | use_dns: 41 | max_auth_tries: 42 | max_sessions: 43 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from os import path 16 | 17 | TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates') 18 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/checks/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.core.hookenv import ( 16 | log, 17 | DEBUG, 18 | ) 19 | from charmhelpers.contrib.hardening.host.checks import ( 20 | apt, 21 | limits, 22 | login, 23 | minimize_access, 24 | pam, 25 | profile, 26 | securetty, 27 | suid_sgid, 28 | sysctl 29 | ) 30 | 31 | 32 | def run_os_checks(): 33 | log("Starting OS hardening checks.", level=DEBUG) 34 | checks = apt.get_audits() 35 | checks.extend(limits.get_audits()) 36 | checks.extend(login.get_audits()) 37 | checks.extend(minimize_access.get_audits()) 38 | checks.extend(pam.get_audits()) 39 | checks.extend(profile.get_audits()) 40 | checks.extend(securetty.get_audits()) 41 | checks.extend(suid_sgid.get_audits()) 42 | checks.extend(sysctl.get_audits()) 43 | 44 | for check in checks: 45 | log("Running '%s' check" % (check.__class__.__name__), level=DEBUG) 46 | check.ensure_compliance() 47 | 48 | log("OS hardening checks complete.", level=DEBUG) 49 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/checks/apt.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.contrib.hardening.utils import get_settings 16 | from charmhelpers.contrib.hardening.audits.apt import ( 17 | AptConfig, 18 | RestrictedPackages, 19 | ) 20 | 21 | 22 | def get_audits(): 23 | """Get OS hardening apt audits. 24 | 25 | :returns: dictionary of audits 26 | """ 27 | audits = [AptConfig([{'key': 'APT::Get::AllowUnauthenticated', 28 | 'expected': 'false'}])] 29 | 30 | settings = get_settings('os') 31 | clean_packages = settings['security']['packages_clean'] 32 | if clean_packages: 33 | security_packages = settings['security']['packages_list'] 34 | if security_packages: 35 | audits.append(RestrictedPackages(security_packages)) 36 | 37 | return audits 38 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/checks/limits.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.contrib.hardening.audits.file import ( 16 | DirectoryPermissionAudit, 17 | TemplatedFile, 18 | ) 19 | from charmhelpers.contrib.hardening.host import TEMPLATES_DIR 20 | from charmhelpers.contrib.hardening import utils 21 | 22 | 23 | def get_audits(): 24 | """Get OS hardening security limits audits. 25 | 26 | :returns: dictionary of audits 27 | """ 28 | audits = [] 29 | settings = utils.get_settings('os') 30 | 31 | # Ensure that the /etc/security/limits.d directory is only writable 32 | # by the root user, but others can execute and read. 33 | audits.append(DirectoryPermissionAudit('/etc/security/limits.d', 34 | user='root', group='root', 35 | mode=0o755)) 36 | 37 | # If core dumps are not enabled, then don't allow core dumps to be 38 | # created as they may contain sensitive information. 39 | if not settings['security']['kernel_enable_core_dump']: 40 | audits.append(TemplatedFile('/etc/security/limits.d/10.hardcore.conf', 41 | SecurityLimitsContext(), 42 | template_dir=TEMPLATES_DIR, 43 | user='root', group='root', mode=0o0440)) 44 | return audits 45 | 46 | 47 | class SecurityLimitsContext(object): 48 | 49 | def __call__(self): 50 | settings = utils.get_settings('os') 51 | ctxt = {'disable_core_dump': 52 | not settings['security']['kernel_enable_core_dump']} 53 | return ctxt 54 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/checks/login.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.contrib.hardening.audits.file import TemplatedFile 16 | from charmhelpers.contrib.hardening.host import TEMPLATES_DIR 17 | from charmhelpers.contrib.hardening import utils 18 | 19 | 20 | def get_audits(): 21 | """Get OS hardening login.defs audits. 22 | 23 | :returns: dictionary of audits 24 | """ 25 | audits = [TemplatedFile('/etc/login.defs', LoginContext(), 26 | template_dir=TEMPLATES_DIR, 27 | user='root', group='root', mode=0o0444)] 28 | return audits 29 | 30 | 31 | class LoginContext(object): 32 | 33 | def __call__(self): 34 | settings = utils.get_settings('os') 35 | 36 | # Octal numbers in yaml end up being turned into decimal, 37 | # so check if the umask is entered as a string (e.g. '027') 38 | # or as an octal umask as we know it (e.g. 002). If its not 39 | # a string assume it to be octal and turn it into an octal 40 | # string. 41 | umask = settings['environment']['umask'] 42 | if not isinstance(umask, str): 43 | umask = '%s' % oct(umask) 44 | 45 | ctxt = { 46 | 'additional_user_paths': 47 | settings['environment']['extra_user_paths'], 48 | 'umask': umask, 49 | 'pwd_max_age': settings['auth']['pw_max_age'], 50 | 'pwd_min_age': settings['auth']['pw_min_age'], 51 | 'uid_min': settings['auth']['uid_min'], 52 | 'sys_uid_min': settings['auth']['sys_uid_min'], 53 | 'sys_uid_max': settings['auth']['sys_uid_max'], 54 | 'gid_min': settings['auth']['gid_min'], 55 | 'sys_gid_min': settings['auth']['sys_gid_min'], 56 | 'sys_gid_max': settings['auth']['sys_gid_max'], 57 | 'login_retries': settings['auth']['retries'], 58 | 'login_timeout': settings['auth']['timeout'], 59 | 'chfn_restrict': settings['auth']['chfn_restrict'], 60 | 'allow_login_without_home': settings['auth']['allow_homeless'] 61 | } 62 | 63 | return ctxt 64 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/checks/minimize_access.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.contrib.hardening.audits.file import ( 16 | FilePermissionAudit, 17 | ReadOnly, 18 | ) 19 | from charmhelpers.contrib.hardening import utils 20 | 21 | 22 | def get_audits(): 23 | """Get OS hardening access audits. 24 | 25 | :returns: dictionary of audits 26 | """ 27 | audits = [] 28 | settings = utils.get_settings('os') 29 | 30 | # Remove write permissions from $PATH folders for all regular users. 31 | # This prevents changing system-wide commands from normal users. 32 | path_folders = {'/usr/local/sbin', 33 | '/usr/local/bin', 34 | '/usr/sbin', 35 | '/usr/bin', 36 | '/bin'} 37 | extra_user_paths = settings['environment']['extra_user_paths'] 38 | path_folders.update(extra_user_paths) 39 | audits.append(ReadOnly(path_folders)) 40 | 41 | # Only allow the root user to have access to the shadow file. 42 | audits.append(FilePermissionAudit('/etc/shadow', 'root', 'root', 0o0600)) 43 | 44 | if 'change_user' not in settings['security']['users_allow']: 45 | # su should only be accessible to user and group root, unless it is 46 | # expressly defined to allow users to change to root via the 47 | # security_users_allow config option. 48 | audits.append(FilePermissionAudit('/bin/su', 'root', 'root', 0o750)) 49 | 50 | return audits 51 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/checks/profile.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.contrib.hardening.audits.file import TemplatedFile 16 | from charmhelpers.contrib.hardening.host import TEMPLATES_DIR 17 | from charmhelpers.contrib.hardening import utils 18 | 19 | 20 | def get_audits(): 21 | """Get OS hardening profile audits. 22 | 23 | :returns: dictionary of audits 24 | """ 25 | audits = [] 26 | 27 | settings = utils.get_settings('os') 28 | # If core dumps are not enabled, then don't allow core dumps to be 29 | # created as they may contain sensitive information. 30 | if not settings['security']['kernel_enable_core_dump']: 31 | audits.append(TemplatedFile('/etc/profile.d/pinerolo_profile.sh', 32 | ProfileContext(), 33 | template_dir=TEMPLATES_DIR, 34 | mode=0o0755, user='root', group='root')) 35 | if settings['security']['ssh_tmout']: 36 | audits.append(TemplatedFile('/etc/profile.d/99-hardening.sh', 37 | ProfileContext(), 38 | template_dir=TEMPLATES_DIR, 39 | mode=0o0644, user='root', group='root')) 40 | return audits 41 | 42 | 43 | class ProfileContext(object): 44 | 45 | def __call__(self): 46 | settings = utils.get_settings('os') 47 | ctxt = {'ssh_tmout': 48 | settings['security']['ssh_tmout']} 49 | return ctxt 50 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/checks/securetty.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.contrib.hardening.audits.file import TemplatedFile 16 | from charmhelpers.contrib.hardening.host import TEMPLATES_DIR 17 | from charmhelpers.contrib.hardening import utils 18 | 19 | 20 | def get_audits(): 21 | """Get OS hardening Secure TTY audits. 22 | 23 | :returns: dictionary of audits 24 | """ 25 | audits = [] 26 | audits.append(TemplatedFile('/etc/securetty', SecureTTYContext(), 27 | template_dir=TEMPLATES_DIR, 28 | mode=0o0400, user='root', group='root')) 29 | return audits 30 | 31 | 32 | class SecureTTYContext(object): 33 | 34 | def __call__(self): 35 | settings = utils.get_settings('os') 36 | ctxt = {'ttys': settings['auth']['root_ttys']} 37 | return ctxt 38 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/templates/10.hardcore.conf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | {% if disable_core_dump -%} 6 | # Prevent core dumps for all users. These are usually only needed by developers and may contain sensitive information. 7 | * hard core 0 8 | {% endif %} -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/templates/99-hardening.sh: -------------------------------------------------------------------------------- 1 | TMOUT={{ tmout }} 2 | readonly TMOUT 3 | export TMOUT 4 | 5 | readonly HISTFILE 6 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | {% for key, value in sysctl_settings -%} 6 | {{ key }}={{ value }} 7 | {% endfor -%} 8 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-swift-proxy/b4aa3c978b8be71585d43475598394451dbad11c/charmhelpers/contrib/hardening/host/templates/__init__.py -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/templates/modules: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | # /etc/modules: kernel modules to load at boot time. 6 | # 7 | # This file contains the names of kernel modules that should be loaded 8 | # at boot time, one per line. Lines beginning with "#" are ignored. 9 | # Parameters can be specified after the module name. 10 | 11 | # Arch 12 | # ---- 13 | # 14 | # Modules for certains builds, contains support modules and some CPU-specific optimizations. 15 | 16 | {% if arch == "x86_64" -%} 17 | # Optimize for x86_64 cryptographic features 18 | twofish-x86_64-3way 19 | twofish-x86_64 20 | aes-x86_64 21 | salsa20-x86_64 22 | blowfish-x86_64 23 | {% endif -%} 24 | 25 | {% if cpuVendor == "intel" -%} 26 | # Intel-specific optimizations 27 | ghash-clmulni-intel 28 | aesni-intel 29 | kvm-intel 30 | {% endif -%} 31 | 32 | {% if cpuVendor == "amd" -%} 33 | # AMD-specific optimizations 34 | kvm-amd 35 | {% endif -%} 36 | 37 | kvm 38 | 39 | 40 | # Crypto 41 | # ------ 42 | 43 | # Some core modules which comprise strong cryptography. 44 | blowfish_common 45 | blowfish_generic 46 | ctr 47 | cts 48 | lrw 49 | lzo 50 | rmd160 51 | rmd256 52 | rmd320 53 | serpent 54 | sha512_generic 55 | twofish_common 56 | twofish_generic 57 | xts 58 | zlib 59 | 60 | 61 | # Drivers 62 | # ------- 63 | 64 | # Basics 65 | lp 66 | rtc 67 | loop 68 | 69 | # Filesystems 70 | ext2 71 | btrfs 72 | 73 | {% if desktop_enable -%} 74 | # Desktop 75 | psmouse 76 | snd 77 | snd_ac97_codec 78 | snd_intel8x0 79 | snd_page_alloc 80 | snd_pcm 81 | snd_timer 82 | soundcore 83 | usbhid 84 | {% endif -%} 85 | 86 | # Lib 87 | # --- 88 | xz 89 | 90 | 91 | # Net 92 | # --- 93 | 94 | # All packets needed for netfilter rules (ie iptables, ebtables). 95 | ip_tables 96 | x_tables 97 | iptable_filter 98 | iptable_nat 99 | 100 | # Targets 101 | ipt_LOG 102 | ipt_REJECT 103 | 104 | # Modules 105 | xt_connlimit 106 | xt_tcpudp 107 | xt_recent 108 | xt_limit 109 | xt_conntrack 110 | nf_conntrack 111 | nf_conntrack_ipv4 112 | nf_defrag_ipv4 113 | xt_state 114 | nf_nat 115 | 116 | # Addons 117 | xt_pknock -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/templates/passwdqc.conf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | Name: passwdqc password strength enforcement 6 | Default: yes 7 | Priority: 1024 8 | Conflicts: cracklib 9 | Password-Type: Primary 10 | Password: 11 | requisite pam_passwdqc.so {{ auth_pam_passwdqc_options }} 12 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | # Disable core dumps via soft limits for all users. Compliance to this setting 6 | # is voluntary and can be modified by users up to a hard limit. This setting is 7 | # a sane default. 8 | ulimit -S -c 0 > /dev/null 2>&1 9 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/templates/securetty: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | # A list of TTYs, from which root can log in 6 | # see `man securetty` for reference 7 | {% if ttys -%} 8 | {% for tty in ttys -%} 9 | {{ tty }} 10 | {% endfor -%} 11 | {% endif -%} 12 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/host/templates/tally2: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | Name: tally2 lockout after failed attempts enforcement 6 | Default: yes 7 | Priority: 1024 8 | Conflicts: cracklib 9 | Auth-Type: Primary 10 | Auth-Initial: 11 | required pam_tally2.so deny={{ auth_retries }} onerr=fail unlock_time={{ auth_lockout_time }} 12 | Account-Type: Primary 13 | Account-Initial: 14 | required pam_tally2.so 15 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/mysql/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from os import path 16 | 17 | TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates') 18 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/mysql/checks/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.core.hookenv import ( 16 | log, 17 | DEBUG, 18 | ) 19 | from charmhelpers.contrib.hardening.mysql.checks import config 20 | 21 | 22 | def run_mysql_checks(): 23 | log("Starting MySQL hardening checks.", level=DEBUG) 24 | checks = config.get_audits() 25 | for check in checks: 26 | log("Running '%s' check" % (check.__class__.__name__), level=DEBUG) 27 | check.ensure_compliance() 28 | 29 | log("MySQL hardening checks complete.", level=DEBUG) 30 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/mysql/checks/config.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import subprocess 16 | 17 | from charmhelpers.core.hookenv import ( 18 | log, 19 | WARNING, 20 | ) 21 | from charmhelpers.contrib.hardening.audits.file import ( 22 | FilePermissionAudit, 23 | DirectoryPermissionAudit, 24 | TemplatedFile, 25 | ) 26 | from charmhelpers.contrib.hardening.mysql import TEMPLATES_DIR 27 | from charmhelpers.contrib.hardening import utils 28 | 29 | 30 | def get_audits(): 31 | """Get MySQL hardening config audits. 32 | 33 | :returns: dictionary of audits 34 | """ 35 | if subprocess.call(['which', 'mysql'], stdout=subprocess.PIPE) != 0: 36 | log("MySQL does not appear to be installed on this node - " 37 | "skipping mysql hardening", level=WARNING) 38 | return [] 39 | 40 | settings = utils.get_settings('mysql') 41 | hardening_settings = settings['hardening'] 42 | my_cnf = hardening_settings['mysql-conf'] 43 | 44 | audits = [ 45 | FilePermissionAudit(paths=[my_cnf], user='root', 46 | group='root', mode=0o0600), 47 | 48 | TemplatedFile(hardening_settings['hardening-conf'], 49 | MySQLConfContext(), 50 | TEMPLATES_DIR, 51 | mode=0o0750, 52 | user='mysql', 53 | group='root', 54 | service_actions=[{'service': 'mysql', 55 | 'actions': ['restart']}]), 56 | 57 | # MySQL and Percona charms do not allow configuration of the 58 | # data directory, so use the default. 59 | DirectoryPermissionAudit('/var/lib/mysql', 60 | user='mysql', 61 | group='mysql', 62 | recursive=False, 63 | mode=0o755), 64 | 65 | DirectoryPermissionAudit('/etc/mysql', 66 | user='root', 67 | group='root', 68 | recursive=False, 69 | mode=0o700), 70 | ] 71 | 72 | return audits 73 | 74 | 75 | class MySQLConfContext(object): 76 | """Defines the set of key/value pairs to set in a mysql config file. 77 | 78 | This context, when called, will return a dictionary containing the 79 | key/value pairs of setting to specify in the 80 | /etc/mysql/conf.d/hardening.cnf file. 81 | """ 82 | def __call__(self): 83 | settings = utils.get_settings('mysql') 84 | return { 85 | 'mysql_settings': [(k, v) for k, v in settings['security'].items()] 86 | } 87 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/mysql/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-swift-proxy/b4aa3c978b8be71585d43475598394451dbad11c/charmhelpers/contrib/hardening/mysql/templates/__init__.py -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/mysql/templates/hardening.cnf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | [mysqld] 6 | {% for setting, value in mysql_settings -%} 7 | {% if value == 'True' -%} 8 | {{ setting }} 9 | {% elif value != 'None' and value != None -%} 10 | {{ setting }} = {{ value }} 11 | {% endif -%} 12 | {% endfor -%} 13 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/ssh/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from os import path 16 | 17 | TEMPLATES_DIR = path.join(path.dirname(__file__), 'templates') 18 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/ssh/checks/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from charmhelpers.core.hookenv import ( 16 | log, 17 | DEBUG, 18 | ) 19 | from charmhelpers.contrib.hardening.ssh.checks import config 20 | 21 | 22 | def run_ssh_checks(): 23 | log("Starting SSH hardening checks.", level=DEBUG) 24 | checks = config.get_audits() 25 | for check in checks: 26 | log("Running '%s' check" % (check.__class__.__name__), level=DEBUG) 27 | check.ensure_compliance() 28 | 29 | log("SSH hardening checks complete.", level=DEBUG) 30 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/ssh/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-swift-proxy/b4aa3c978b8be71585d43475598394451dbad11c/charmhelpers/contrib/hardening/ssh/templates/__init__.py -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/ssh/templates/ssh_config: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # WARNING: This configuration file is maintained by Juju. Local changes may 3 | # be overwritten. 4 | ############################################################################### 5 | # This is the ssh client system-wide configuration file. See 6 | # ssh_config(5) for more information. This file provides defaults for 7 | # users, and the values can be changed in per-user configuration files 8 | # or on the command line. 9 | 10 | # Configuration data is parsed as follows: 11 | # 1. command line options 12 | # 2. user-specific file 13 | # 3. system-wide file 14 | # Any configuration value is only changed the first time it is set. 15 | # Thus, host-specific definitions should be at the beginning of the 16 | # configuration file, and defaults at the end. 17 | 18 | # Site-wide defaults for some commonly used options. For a comprehensive 19 | # list of available options, their meanings and defaults, please see the 20 | # ssh_config(5) man page. 21 | 22 | # Restrict the following configuration to be limited to this Host. 23 | {% if remote_hosts -%} 24 | Host {{ ' '.join(remote_hosts) }} 25 | {% endif %} 26 | ForwardAgent no 27 | ForwardX11 no 28 | ForwardX11Trusted yes 29 | RhostsRSAAuthentication no 30 | RSAAuthentication yes 31 | PasswordAuthentication {{ password_auth_allowed }} 32 | HostbasedAuthentication no 33 | GSSAPIAuthentication no 34 | GSSAPIDelegateCredentials no 35 | GSSAPIKeyExchange no 36 | GSSAPITrustDNS no 37 | BatchMode no 38 | CheckHostIP yes 39 | AddressFamily {{ addr_family }} 40 | ConnectTimeout 0 41 | StrictHostKeyChecking ask 42 | IdentityFile ~/.ssh/identity 43 | IdentityFile ~/.ssh/id_rsa 44 | IdentityFile ~/.ssh/id_dsa 45 | # The port at the destination should be defined 46 | {% for port in ports -%} 47 | Port {{ port }} 48 | {% endfor %} 49 | Protocol 2 50 | Cipher 3des 51 | {% if ciphers -%} 52 | Ciphers {{ ciphers }} 53 | {%- endif %} 54 | {% if macs -%} 55 | MACs {{ macs }} 56 | {%- endif %} 57 | {% if kexs -%} 58 | KexAlgorithms {{ kexs }} 59 | {%- endif %} 60 | EscapeChar ~ 61 | Tunnel no 62 | TunnelDevice any:any 63 | PermitLocalCommand no 64 | VisualHostKey no 65 | RekeyLimit 1G 1h 66 | SendEnv LANG LC_* 67 | HashKnownHosts yes 68 | {% if roaming -%} 69 | UseRoaming {{ roaming }} 70 | {% endif %} 71 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardening/templating.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | 17 | from charmhelpers.core.hookenv import ( 18 | log, 19 | DEBUG, 20 | WARNING, 21 | ) 22 | 23 | try: 24 | from jinja2 import FileSystemLoader, Environment 25 | except ImportError: 26 | from charmhelpers.fetch import apt_install 27 | from charmhelpers.fetch import apt_update 28 | apt_update(fatal=True) 29 | apt_install('python3-jinja2', fatal=True) 30 | from jinja2 import FileSystemLoader, Environment 31 | 32 | 33 | # NOTE: function separated from main rendering code to facilitate easier 34 | # mocking in unit tests. 35 | def write(path, data): 36 | with open(path, 'wb') as out: 37 | out.write(data) 38 | 39 | 40 | def get_template_path(template_dir, path): 41 | """Returns the template file which would be used to render the path. 42 | 43 | The path to the template file is returned. 44 | :param template_dir: the directory the templates are located in 45 | :param path: the file path to be written to. 46 | :returns: path to the template file 47 | """ 48 | return os.path.join(template_dir, os.path.basename(path)) 49 | 50 | 51 | def render_and_write(template_dir, path, context): 52 | """Renders the specified template into the file. 53 | 54 | :param template_dir: the directory to load the template from 55 | :param path: the path to write the templated contents to 56 | :param context: the parameters to pass to the rendering engine 57 | """ 58 | env = Environment(loader=FileSystemLoader(template_dir)) 59 | template_file = os.path.basename(path) 60 | template = env.get_template(template_file) 61 | log('Rendering from template: %s' % template.name, level=DEBUG) 62 | rendered_content = template.render(context) 63 | if not rendered_content: 64 | log("Render returned None - skipping '%s'" % path, 65 | level=WARNING) 66 | return 67 | 68 | write(path, rendered_content.encode('utf-8').strip()) 69 | log('Wrote template %s' % path, level=DEBUG) 70 | -------------------------------------------------------------------------------- /charmhelpers/contrib/hardware/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2022 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/network/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/alternatives.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | ''' Helper for managing alternatives for file conflict resolution ''' 16 | 17 | import subprocess 18 | import shutil 19 | import os 20 | 21 | 22 | def install_alternative(name, target, source, priority=50): 23 | ''' Install alternative configuration ''' 24 | if (os.path.exists(target) and not os.path.islink(target)): 25 | # Move existing file/directory away before installing 26 | shutil.move(target, '{}.bak'.format(target)) 27 | cmd = [ 28 | 'update-alternatives', '--force', '--install', 29 | target, name, source, str(priority) 30 | ] 31 | subprocess.check_call(cmd) 32 | 33 | 34 | def remove_alternative(name, source): 35 | """Remove an installed alternative configuration file 36 | 37 | :param name: string name of the alternative to remove 38 | :param source: string full path to alternative to remove 39 | """ 40 | cmd = [ 41 | 'update-alternatives', '--remove', 42 | name, source 43 | ] 44 | subprocess.check_call(cmd) 45 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | class OSContextError(Exception): 17 | """Raised when an error occurs during context generation. 18 | 19 | This exception is principally used in contrib.openstack.context 20 | """ 21 | pass 22 | 23 | 24 | class ServiceActionError(Exception): 25 | """Raised when a service action (stop/start/ etc) failed.""" 26 | pass 27 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/files/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # dummy __init__.py to fool syncer into thinking this is a syncable python 16 | # module 17 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/files/check_haproxy.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #-------------------------------------------- 3 | # This file is managed by Juju 4 | #-------------------------------------------- 5 | # 6 | # Copyright 2009,2012 Canonical Ltd. 7 | # Author: Tom Haddon 8 | 9 | CRITICAL=0 10 | NOTACTIVE='' 11 | LOGFILE=/var/log/nagios/check_haproxy.log 12 | AUTH=$(grep -r "stats auth" /etc/haproxy/haproxy.cfg | awk 'NR=1{print $3}') 13 | 14 | typeset -i N_INSTANCES=0 15 | for appserver in $(awk '/^\s+server/{print $2}' /etc/haproxy/haproxy.cfg) 16 | do 17 | N_INSTANCES=N_INSTANCES+1 18 | output=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -u '/;csv' --regex=",${appserver},.*,UP.*" -e ' 200 OK') 19 | if [ $? != 0 ]; then 20 | date >> $LOGFILE 21 | echo $output >> $LOGFILE 22 | /usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -u '/;csv' -v | grep ",${appserver}," >> $LOGFILE 2>&1 23 | CRITICAL=1 24 | NOTACTIVE="${NOTACTIVE} $appserver" 25 | fi 26 | done 27 | 28 | if [ $CRITICAL = 1 ]; then 29 | echo "CRITICAL:${NOTACTIVE}" 30 | exit 2 31 | fi 32 | 33 | echo "OK: All haproxy instances ($N_INSTANCES) looking good" 34 | exit 0 35 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | #-------------------------------------------- 3 | # This file is managed by Juju 4 | #-------------------------------------------- 5 | # 6 | # Copyright 2009,2012 Canonical Ltd. 7 | # Author: Tom Haddon 8 | 9 | # These should be config options at some stage 10 | CURRQthrsh=0 11 | MAXQthrsh=100 12 | 13 | AUTH=$(grep -r "stats auth" /etc/haproxy/haproxy.cfg | awk 'NR=1{print $3}') 14 | 15 | HAPROXYSTATS=$(/usr/lib/nagios/plugins/check_http -a ${AUTH} -I 127.0.0.1 -p 8888 -u '/;csv' -v) 16 | 17 | for BACKEND in $(echo $HAPROXYSTATS| xargs -n1 | grep BACKEND | awk -F , '{print $1}') 18 | do 19 | CURRQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 3) 20 | MAXQ=$(echo "$HAPROXYSTATS" | grep $BACKEND | grep BACKEND | cut -d , -f 4) 21 | 22 | if [[ $CURRQ -gt $CURRQthrsh || $MAXQ -gt $MAXQthrsh ]] ; then 23 | echo "CRITICAL: queue depth for $BACKEND - CURRENT:$CURRQ MAX:$MAXQ" 24 | exit 2 25 | fi 26 | done 27 | 28 | echo "OK: All haproxy queue depths looking good" 29 | exit 0 30 | 31 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/ha/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # dummy __init__.py to fool syncer into thinking this is a syncable python 16 | # module 17 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/ceph.conf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # [ WARNING ] 3 | # ceph configuration file maintained by Juju 4 | # local changes may be overwritten. 5 | ############################################################################### 6 | [global] 7 | {% if auth -%} 8 | auth_supported = {{ auth }} 9 | keyring = /etc/ceph/$cluster.$name.keyring 10 | mon host = {{ mon_hosts }} 11 | {% endif -%} 12 | log to syslog = {{ use_syslog }} 13 | err to syslog = {{ use_syslog }} 14 | clog to syslog = {{ use_syslog }} 15 | {% if rbd_features %} 16 | rbd default features = {{ rbd_features }} 17 | {% endif %} 18 | 19 | [client] 20 | {% if rbd_client_cache_settings -%} 21 | {% for key, value in rbd_client_cache_settings.items() -%} 22 | {{ key }} = {{ value }} 23 | {% endfor -%} 24 | {%- endif %} 25 | 26 | {% if rbd_default_data_pool -%} 27 | rbd default data pool = {{ rbd_default_data_pool }} 28 | {% endif %} 29 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/git.upstart: -------------------------------------------------------------------------------- 1 | description "{{ service_description }}" 2 | author "Juju {{ service_name }} Charm " 3 | 4 | start on runlevel [2345] 5 | stop on runlevel [!2345] 6 | 7 | respawn 8 | 9 | exec start-stop-daemon --start --chuid {{ user_name }} \ 10 | --chdir {{ start_dir }} --name {{ process_name }} \ 11 | --exec {{ executable_name }} -- \ 12 | {% for config_file in config_files -%} 13 | --config-file={{ config_file }} \ 14 | {% endfor -%} 15 | {% if log_file -%} 16 | --log-file={{ log_file }} 17 | {% endif -%} 18 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/haproxy.cfg: -------------------------------------------------------------------------------- 1 | global 2 | # NOTE: on startup haproxy chroot's to /var/lib/haproxy. 3 | # 4 | # Unfortunately the program will open some files prior to the call to 5 | # chroot never to reopen them, and some after. So looking at the on-disk 6 | # layout of haproxy resources you will find some resources relative to / 7 | # such as the admin socket, and some relative to /var/lib/haproxy such as 8 | # the log socket. 9 | # 10 | # The logging socket is (re-)opened after the chroot and must be relative 11 | # to /var/lib/haproxy. 12 | log /dev/log local0 13 | log /dev/log local1 notice 14 | maxconn 20000 15 | user haproxy 16 | group haproxy 17 | spread-checks 0 18 | # The admin socket is opened prior to the chroot never to be reopened, so 19 | # it lives outside the chroot directory in the filesystem. 20 | stats socket /var/run/haproxy/admin.sock mode 600 level admin 21 | stats timeout 2m 22 | 23 | defaults 24 | log global 25 | mode tcp 26 | option tcplog 27 | option dontlognull 28 | retries 3 29 | {%- if haproxy_queue_timeout %} 30 | timeout queue {{ haproxy_queue_timeout }} 31 | {%- else %} 32 | timeout queue 9000 33 | {%- endif %} 34 | {%- if haproxy_connect_timeout %} 35 | timeout connect {{ haproxy_connect_timeout }} 36 | {%- else %} 37 | timeout connect 9000 38 | {%- endif %} 39 | {%- if haproxy_client_timeout %} 40 | timeout client {{ haproxy_client_timeout }} 41 | {%- else %} 42 | timeout client 90000 43 | {%- endif %} 44 | {%- if haproxy_server_timeout %} 45 | timeout server {{ haproxy_server_timeout }} 46 | {%- else %} 47 | timeout server 90000 48 | {%- endif %} 49 | 50 | listen stats 51 | bind {{ local_host }}:{{ stat_port }} 52 | {%- if stats_exporter_host and stats_exporter_port %} 53 | bind {{ stats_exporter_host }}:{{ stats_exporter_port }} 54 | option http-use-htx 55 | http-request use-service prometheus-exporter if { path /metrics } 56 | {%- endif %} 57 | mode http 58 | stats enable 59 | stats hide-version 60 | stats realm Haproxy\ Statistics 61 | stats uri / 62 | stats auth admin:{{ stat_password }} 63 | 64 | {% if frontends -%} 65 | {% for service, ports in service_ports.items() -%} 66 | frontend tcp-in_{{ service }} 67 | bind *:{{ ports[0] }} 68 | {% if ipv6_enabled -%} 69 | bind :::{{ ports[0] }} 70 | {% endif -%} 71 | {% for frontend in frontends -%} 72 | acl net_{{ frontend }} dst {{ frontends[frontend]['network'] }} 73 | use_backend {{ service }}_{{ frontend }} if net_{{ frontend }} 74 | {% endfor -%} 75 | default_backend {{ service }}_{{ default_backend }} 76 | 77 | {% for frontend in frontends -%} 78 | backend {{ service }}_{{ frontend }} 79 | balance leastconn 80 | {% if backend_options -%} 81 | {% if backend_options[service] -%} 82 | {% for option in backend_options[service] -%} 83 | {% for key, value in option.items() -%} 84 | {{ key }} {{ value }} 85 | {% endfor -%} 86 | {% endfor -%} 87 | {% endif -%} 88 | {% endif -%} 89 | {% for unit, address in frontends[frontend]['backends'].items() -%} 90 | {% if https -%} 91 | server {{ unit }} {{ address }}:{{ ports[1] }} check check-ssl verify none 92 | {% else -%} 93 | server {{ unit }} {{ address }}:{{ ports[1] }} check 94 | {% endif -%} 95 | {% endfor %} 96 | {% endfor -%} 97 | {% endfor -%} 98 | {% endif -%} 99 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/logrotate: -------------------------------------------------------------------------------- 1 | /var/log/{{ logrotate_logs_location }}/*.log { 2 | {{ logrotate_interval }} 3 | {{ logrotate_count }} 4 | compress 5 | delaycompress 6 | missingok 7 | notifempty 8 | copytruncate 9 | } 10 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/memcached.conf: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # [ WARNING ] 3 | # memcached configuration file maintained by Juju 4 | # local changes may be overwritten. 5 | ############################################################################### 6 | 7 | # memcached default config file 8 | # 2003 - Jay Bonci 9 | # This configuration file is read by the start-memcached script provided as 10 | # part of the Debian GNU/Linux distribution. 11 | 12 | # Run memcached as a daemon. This command is implied, and is not needed for the 13 | # daemon to run. See the README.Debian that comes with this package for more 14 | # information. 15 | -d 16 | 17 | # Log memcached's output to /var/log/memcached 18 | logfile /var/log/memcached.log 19 | 20 | # Be verbose 21 | # -v 22 | 23 | # Be even more verbose (print client commands as well) 24 | # -vv 25 | 26 | # Start with a cap of 64 megs of memory. It's reasonable, and the daemon default 27 | # Note that the daemon will grow to this size, but does not start out holding this much 28 | # memory 29 | -m 64 30 | 31 | # Default connection port is 11211 32 | -p {{ memcache_port }} 33 | 34 | # Run the daemon as root. The start-memcached will default to running as root if no 35 | # -u command is present in this config file 36 | -u memcache 37 | 38 | # Specify which IP address to listen on. The default is to listen on all IP addresses 39 | # This parameter is one of the only security measures that memcached has, so make sure 40 | # it's listening on a firewalled interface. 41 | -l {{ memcache_server }} 42 | 43 | # Limit the number of simultaneous incoming connections. The daemon default is 1024 44 | # -c 1024 45 | 46 | # Lock down all paged memory. Consult with the README and homepage before you do this 47 | # -k 48 | 49 | # Return error when memory is exhausted (rather than removing items) 50 | # -M 51 | 52 | # Maximize core file limit 53 | # -r 54 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/openstack_https_frontend: -------------------------------------------------------------------------------- 1 | {% if endpoints -%} 2 | {% for ext_port in ext_ports -%} 3 | Listen {{ ext_port }} 4 | {% endfor -%} 5 | {% for address, endpoint, ext, int in endpoints -%} 6 | 7 | ServerName {{ endpoint }} 8 | SSLEngine on 9 | 10 | # This section is based on Mozilla's recommendation 11 | # as the "intermediate" profile as of July 7th, 2020. 12 | # https://wiki.mozilla.org/Security/Server_Side_TLS 13 | SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 14 | SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 15 | SSLHonorCipherOrder off 16 | 17 | SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} 18 | # See LP 1484489 - this is to support <= 2.4.7 and >= 2.4.8 19 | SSLCertificateChainFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} 20 | SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key_{{ endpoint }} 21 | ProxyPass / http://localhost:{{ int }}/ 22 | ProxyPassReverse / http://localhost:{{ int }}/ 23 | ProxyPreserveHost on 24 | RequestHeader set X-Forwarded-Proto "https" 25 | KeepAliveTimeout 75 26 | MaxKeepAliveRequests 1000 27 | 28 | {% endfor -%} 29 | 30 | Order deny,allow 31 | Allow from all 32 | 33 | 34 | Order allow,deny 35 | Allow from all 36 | 37 | {% endif -%} 38 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf: -------------------------------------------------------------------------------- 1 | {% if endpoints -%} 2 | {% for ext_port in ext_ports -%} 3 | Listen {{ ext_port }} 4 | {% endfor -%} 5 | {% for address, endpoint, ext, int in endpoints -%} 6 | 7 | ServerName {{ endpoint }} 8 | SSLEngine on 9 | 10 | # This section is based on Mozilla's recommendation 11 | # as the "intermediate" profile as of July 7th, 2020. 12 | # https://wiki.mozilla.org/Security/Server_Side_TLS 13 | SSLProtocol all -SSLv3 -TLSv1 -TLSv1.1 14 | SSLCipherSuite ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384 15 | SSLHonorCipherOrder off 16 | 17 | SSLCertificateFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} 18 | # See LP 1484489 - this is to support <= 2.4.7 and >= 2.4.8 19 | SSLCertificateChainFile /etc/apache2/ssl/{{ namespace }}/cert_{{ endpoint }} 20 | SSLCertificateKeyFile /etc/apache2/ssl/{{ namespace }}/key_{{ endpoint }} 21 | ProxyPass / http://localhost:{{ int }}/ 22 | ProxyPassReverse / http://localhost:{{ int }}/ 23 | ProxyPreserveHost on 24 | RequestHeader set X-Forwarded-Proto "https" 25 | KeepAliveTimeout 75 26 | MaxKeepAliveRequests 1000 27 | 28 | {% endfor -%} 29 | 30 | Order deny,allow 31 | Allow from all 32 | 33 | 34 | Order allow,deny 35 | Allow from all 36 | 37 | {% endif -%} 38 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-audit-middleware-notifications: -------------------------------------------------------------------------------- 1 | {% if audit_middleware -%} 2 | [audit_middleware_notifications] 3 | driver = log 4 | {% endif -%} -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-ceph-bluestore-compression: -------------------------------------------------------------------------------- 1 | {# section header omitted as options can belong to multiple sections #} 2 | {% if bluestore_compression_algorithm -%} 3 | bluestore compression algorithm = {{ bluestore_compression_algorithm }} 4 | {% endif -%} 5 | {% if bluestore_compression_mode -%} 6 | bluestore compression mode = {{ bluestore_compression_mode }} 7 | {% endif -%} 8 | {% if bluestore_compression_required_ratio -%} 9 | bluestore compression required ratio = {{ bluestore_compression_required_ratio }} 10 | {% endif -%} 11 | {% if bluestore_compression_min_blob_size -%} 12 | bluestore compression min blob size = {{ bluestore_compression_min_blob_size }} 13 | {% endif -%} 14 | {% if bluestore_compression_min_blob_size_hdd -%} 15 | bluestore compression min blob size hdd = {{ bluestore_compression_min_blob_size_hdd }} 16 | {% endif -%} 17 | {% if bluestore_compression_min_blob_size_ssd -%} 18 | bluestore compression min blob size ssd = {{ bluestore_compression_min_blob_size_ssd }} 19 | {% endif -%} 20 | {% if bluestore_compression_max_blob_size -%} 21 | bluestore compression max blob size = {{ bluestore_compression_max_blob_size }} 22 | {% endif -%} 23 | {% if bluestore_compression_max_blob_size_hdd -%} 24 | bluestore compression max blob size hdd = {{ bluestore_compression_max_blob_size_hdd }} 25 | {% endif -%} 26 | {% if bluestore_compression_max_blob_size_ssd -%} 27 | bluestore compression max blob size ssd = {{ bluestore_compression_max_blob_size_ssd }} 28 | {% endif -%} 29 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-filter-audit: -------------------------------------------------------------------------------- 1 | {% if audit_middleware and service_name -%} 2 | [filter:audit] 3 | paste.filter_factory = keystonemiddleware.audit:filter_factory 4 | audit_map_file = /etc/{{ service_name }}/api_audit_map.conf 5 | service_name = {{ service_name }} 6 | {% endif -%} -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-keystone-authtoken: -------------------------------------------------------------------------------- 1 | {% if auth_host -%} 2 | [keystone_authtoken] 3 | auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }} 4 | auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} 5 | auth_plugin = password 6 | project_domain_id = default 7 | user_domain_id = default 8 | project_name = {{ admin_tenant_name }} 9 | username = {{ admin_user }} 10 | password = {{ admin_password }} 11 | signing_dir = {{ signing_dir }} 12 | {% if service_type -%} 13 | service_type = {{ service_type }} 14 | {% endif -%} 15 | {% if admin_role -%} 16 | service_token_roles = {{ admin_role }} 17 | service_token_roles_required = True 18 | {% endif -%} 19 | {% endif -%} 20 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-keystone-authtoken-legacy: -------------------------------------------------------------------------------- 1 | {% if auth_host -%} 2 | [keystone_authtoken] 3 | # Juno specific config (Bug #1557223) 4 | auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/{{ service_admin_prefix }} 5 | identity_uri = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} 6 | admin_tenant_name = {{ admin_tenant_name }} 7 | admin_user = {{ admin_user }} 8 | admin_password = {{ admin_password }} 9 | signing_dir = {{ signing_dir }} 10 | {% endif -%} 11 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka: -------------------------------------------------------------------------------- 1 | {% if auth_host -%} 2 | [keystone_authtoken] 3 | auth_type = password 4 | {% if api_version == "3" -%} 5 | auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v3 6 | auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }}/v3 7 | project_domain_name = {{ admin_domain_name }} 8 | user_domain_name = {{ admin_domain_name }} 9 | {% if service_type -%} 10 | service_type = {{ service_type }} 11 | {% endif -%} 12 | {% else -%} 13 | auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }} 14 | auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} 15 | project_domain_name = default 16 | user_domain_name = default 17 | {% endif -%} 18 | project_name = {{ admin_tenant_name }} 19 | username = {{ admin_user }} 20 | password = {{ admin_password }} 21 | signing_dir = {{ signing_dir }} 22 | {% if use_memcache == true %} 23 | memcached_servers = {{ memcache_url }} 24 | {% endif -%} 25 | {% if admin_role -%} 26 | service_token_roles = {{ admin_role }} 27 | service_token_roles_required = True 28 | {% endif -%} 29 | {% endif -%} 30 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-keystone-authtoken-v3only: -------------------------------------------------------------------------------- 1 | {% if auth_host -%} 2 | [keystone_authtoken] 3 | {% for option_name, option_value in keystone_authtoken.items() -%} 4 | {{ option_name }} = {{ option_value }} 5 | {% endfor -%} 6 | {% if use_memcache == true %} 7 | memcached_servers = {{ memcache_url }} 8 | {% endif -%} 9 | {% endif -%} 10 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-oslo-cache: -------------------------------------------------------------------------------- 1 | [cache] 2 | {% if memcache_url %} 3 | enabled = true 4 | backend = oslo_cache.memcache_pool 5 | memcache_servers = {{ memcache_url }} 6 | {% endif %} 7 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-oslo-messaging-rabbit: -------------------------------------------------------------------------------- 1 | [oslo_messaging_rabbit] 2 | {% if rabbitmq_ha_queues -%} 3 | rabbit_ha_queues = True 4 | {% endif -%} 5 | {% if rabbit_ssl_port -%} 6 | ssl = True 7 | {% endif -%} 8 | {% if rabbit_ssl_ca -%} 9 | ssl_ca_file = {{ rabbit_ssl_ca }} 10 | {% endif -%} 11 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-oslo-messaging-rabbit-ocata: -------------------------------------------------------------------------------- 1 | [oslo_messaging_rabbit] 2 | {% if rabbitmq_ha_queues -%} 3 | rabbit_ha_queues = True 4 | {% endif -%} 5 | {% if rabbit_ssl_port -%} 6 | rabbit_use_ssl = True 7 | {% endif -%} 8 | {% if rabbit_ssl_ca -%} 9 | ssl_ca_file = {{ rabbit_ssl_ca }} 10 | {% endif -%} 11 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-oslo-middleware: -------------------------------------------------------------------------------- 1 | [oslo_middleware] 2 | 3 | # Bug #1758675 4 | enable_proxy_headers_parsing = true 5 | 6 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-oslo-notifications: -------------------------------------------------------------------------------- 1 | {% if transport_url -%} 2 | [oslo_messaging_notifications] 3 | driver = {{ oslo_messaging_driver }} 4 | transport_url = {{ transport_url }} 5 | {% if send_notifications_to_logs %} 6 | driver = log 7 | {% endif %} 8 | {% if notification_topics -%} 9 | topics = {{ notification_topics }} 10 | {% endif -%} 11 | {% if notification_format -%} 12 | [notifications] 13 | notification_format = {{ notification_format }} 14 | {% endif -%} 15 | {% endif -%} 16 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-placement: -------------------------------------------------------------------------------- 1 | [placement] 2 | {% if auth_host -%} 3 | auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} 4 | auth_type = password 5 | {% if api_version == "3" -%} 6 | project_domain_name = {{ admin_domain_name }} 7 | user_domain_name = {{ admin_domain_name }} 8 | {% else -%} 9 | project_domain_name = default 10 | user_domain_name = default 11 | {% endif -%} 12 | project_name = {{ admin_tenant_name }} 13 | username = {{ admin_user }} 14 | password = {{ admin_password }} 15 | {% endif -%} 16 | {% if region -%} 17 | os_region_name = {{ region }} 18 | region_name = {{ region }} 19 | {% endif -%} 20 | randomize_allocation_candidates = true 21 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo: -------------------------------------------------------------------------------- 1 | {% if rabbitmq_host or rabbitmq_hosts -%} 2 | [oslo_messaging_rabbit] 3 | rabbit_userid = {{ rabbitmq_user }} 4 | rabbit_virtual_host = {{ rabbitmq_virtual_host }} 5 | rabbit_password = {{ rabbitmq_password }} 6 | {% if rabbitmq_hosts -%} 7 | rabbit_hosts = {{ rabbitmq_hosts }} 8 | {% if rabbitmq_ha_queues -%} 9 | rabbit_ha_queues = True 10 | rabbit_durable_queues = False 11 | {% endif -%} 12 | {% else -%} 13 | rabbit_host = {{ rabbitmq_host }} 14 | {% endif -%} 15 | {% if rabbit_ssl_port -%} 16 | rabbit_use_ssl = True 17 | rabbit_port = {{ rabbit_ssl_port }} 18 | {% if rabbit_ssl_ca -%} 19 | kombu_ssl_ca_certs = {{ rabbit_ssl_ca }} 20 | {% endif -%} 21 | {% endif -%} 22 | {% endif -%} 23 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-service-user: -------------------------------------------------------------------------------- 1 | {% if auth_host -%} 2 | [service_user] 3 | send_service_user_token = true 4 | auth_type = password 5 | auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} 6 | project_domain_name = service_domain 7 | user_domain_name = service_domain 8 | project_name = {{ admin_tenant_name }} 9 | username = {{ admin_user }} 10 | password = {{ admin_password }} 11 | {% endif -%} 12 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/section-zeromq: -------------------------------------------------------------------------------- 1 | {% if zmq_host -%} 2 | # ZeroMQ configuration (restart-nonce: {{ zmq_nonce }}) 3 | rpc_backend = zmq 4 | rpc_zmq_host = {{ zmq_host }} 5 | {% if zmq_redis_address -%} 6 | rpc_zmq_matchmaker = redis 7 | matchmaker_heartbeat_freq = 15 8 | matchmaker_heartbeat_ttl = 30 9 | [matchmaker_redis] 10 | host = {{ zmq_redis_address }} 11 | {% else -%} 12 | rpc_zmq_matchmaker = ring 13 | {% endif -%} 14 | {% endif -%} 15 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/vendor_data.json: -------------------------------------------------------------------------------- 1 | {{ vendor_data_json }} -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf: -------------------------------------------------------------------------------- 1 | # Configuration file maintained by Juju. Local changes may be overwritten. 2 | 3 | {% if port -%} 4 | Listen {{ port }} 5 | {% endif -%} 6 | 7 | {% if admin_port -%} 8 | Listen {{ admin_port }} 9 | {% endif -%} 10 | 11 | {% if public_port -%} 12 | Listen {{ public_port }} 13 | {% endif -%} 14 | 15 | {% if wsgi_socket_rotation -%} 16 | WSGISocketRotation On 17 | {% else -%} 18 | WSGISocketRotation Off 19 | {% endif -%} 20 | 21 | {% if port -%} 22 | 23 | WSGIDaemonProcess {{ service_name }} processes={{ processes }} threads={{ threads }} user={{ user }} group={{ group }} \ 24 | display-name=%{GROUP} lang=C.UTF-8 locale=C.UTF-8 25 | WSGIProcessGroup {{ service_name }} 26 | WSGIScriptAlias / {{ script }} 27 | WSGIApplicationGroup %{GLOBAL} 28 | WSGIPassAuthorization On 29 | KeepAliveTimeout 75 30 | MaxKeepAliveRequests 1000 31 | = 2.4> 32 | ErrorLogFormat "%{cu}t %M" 33 | 34 | ErrorLog /var/log/apache2/{{ service_name }}_error.log 35 | CustomLog /var/log/apache2/{{ service_name }}_access.log combined 36 | 37 | 38 | = 2.4> 39 | Require all granted 40 | 41 | 42 | Order allow,deny 43 | Allow from all 44 | 45 | 46 | 47 | {% endif -%} 48 | 49 | {% if admin_port -%} 50 | 51 | WSGIDaemonProcess {{ service_name }}-admin processes={{ admin_processes }} threads={{ threads }} user={{ user }} group={{ group }} \ 52 | display-name=%{GROUP} lang=C.UTF-8 locale=C.UTF-8 53 | WSGIProcessGroup {{ service_name }}-admin 54 | WSGIScriptAlias / {{ admin_script }} 55 | WSGIApplicationGroup %{GLOBAL} 56 | WSGIPassAuthorization On 57 | KeepAliveTimeout 75 58 | MaxKeepAliveRequests 1000 59 | = 2.4> 60 | ErrorLogFormat "%{cu}t %M" 61 | 62 | ErrorLog /var/log/apache2/{{ service_name }}_error.log 63 | CustomLog /var/log/apache2/{{ service_name }}_access.log combined 64 | 65 | 66 | = 2.4> 67 | Require all granted 68 | 69 | 70 | Order allow,deny 71 | Allow from all 72 | 73 | 74 | 75 | {% endif -%} 76 | 77 | {% if public_port -%} 78 | 79 | WSGIDaemonProcess {{ service_name }}-public processes={{ public_processes }} threads={{ threads }} user={{ user }} group={{ group }} \ 80 | display-name=%{GROUP} lang=C.UTF-8 locale=C.UTF-8 81 | WSGIProcessGroup {{ service_name }}-public 82 | WSGIScriptAlias / {{ public_script }} 83 | WSGIApplicationGroup %{GLOBAL} 84 | WSGIPassAuthorization On 85 | KeepAliveTimeout 75 86 | MaxKeepAliveRequests 1000 87 | = 2.4> 88 | ErrorLogFormat "%{cu}t %M" 89 | 90 | ErrorLog /var/log/apache2/{{ service_name }}_error.log 91 | CustomLog /var/log/apache2/{{ service_name }}_access.log combined 92 | 93 | 94 | = 2.4> 95 | Require all granted 96 | 97 | 98 | Order allow,deny 99 | Allow from all 100 | 101 | 102 | 103 | {% endif -%} 104 | -------------------------------------------------------------------------------- /charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf: -------------------------------------------------------------------------------- 1 | # Configuration file maintained by Juju. Local changes may be overwritten. 2 | 3 | {% if port -%} 4 | Listen {{ port }} 5 | {% endif -%} 6 | 7 | {% if admin_port -%} 8 | Listen {{ admin_port }} 9 | {% endif -%} 10 | 11 | {% if public_port -%} 12 | Listen {{ public_port }} 13 | {% endif -%} 14 | 15 | {% if wsgi_socket_rotation -%} 16 | WSGISocketRotation On 17 | {% else -%} 18 | WSGISocketRotation Off 19 | {% endif -%} 20 | 21 | {% if port -%} 22 | 23 | WSGIDaemonProcess {{ service_name }} processes={{ processes }} threads={{ threads }} user={{ user }} group={{ group }} \ 24 | display-name=%{GROUP} lang=C.UTF-8 locale=C.UTF-8 25 | WSGIProcessGroup {{ service_name }} 26 | WSGIScriptAlias / {{ script }} 27 | WSGIApplicationGroup %{GLOBAL} 28 | WSGIPassAuthorization On 29 | KeepAliveTimeout 75 30 | MaxKeepAliveRequests 1000 31 | = 2.4> 32 | ErrorLogFormat "%{cu}t %M" 33 | 34 | ErrorLog /var/log/apache2/{{ service_name }}_error.log 35 | CustomLog /var/log/apache2/{{ service_name }}_access.log combined 36 | 37 | 38 | = 2.4> 39 | Require all granted 40 | 41 | 42 | Order allow,deny 43 | Allow from all 44 | 45 | 46 | 47 | {% endif -%} 48 | 49 | {% if admin_port -%} 50 | 51 | WSGIDaemonProcess {{ service_name }}-admin processes={{ admin_processes }} threads={{ threads }} user={{ user }} group={{ group }} \ 52 | display-name=%{GROUP} lang=C.UTF-8 locale=C.UTF-8 53 | WSGIProcessGroup {{ service_name }}-admin 54 | WSGIScriptAlias / {{ admin_script }} 55 | WSGIApplicationGroup %{GLOBAL} 56 | WSGIPassAuthorization On 57 | KeepAliveTimeout 75 58 | MaxKeepAliveRequests 1000 59 | = 2.4> 60 | ErrorLogFormat "%{cu}t %M" 61 | 62 | ErrorLog /var/log/apache2/{{ service_name }}_error.log 63 | CustomLog /var/log/apache2/{{ service_name }}_access.log combined 64 | 65 | 66 | = 2.4> 67 | Require all granted 68 | 69 | 70 | Order allow,deny 71 | Allow from all 72 | 73 | 74 | 75 | {% endif -%} 76 | 77 | {% if public_port -%} 78 | 79 | WSGIDaemonProcess {{ service_name }}-public processes={{ public_processes }} threads={{ threads }} user={{ user }} group={{ group }} \ 80 | display-name=%{GROUP} lang=C.UTF-8 locale=C.UTF-8 81 | WSGIProcessGroup {{ service_name }}-public 82 | WSGIScriptAlias / {{ public_script }} 83 | WSGIApplicationGroup %{GLOBAL} 84 | WSGIPassAuthorization On 85 | KeepAliveTimeout 75 86 | MaxKeepAliveRequests 1000 87 | = 2.4> 88 | ErrorLogFormat "%{cu}t %M" 89 | 90 | ErrorLog /var/log/apache2/{{ service_name }}_error.log 91 | CustomLog /var/log/apache2/{{ service_name }}_access.log combined 92 | 93 | 94 | = 2.4> 95 | Require all granted 96 | 97 | 98 | Order allow,deny 99 | Allow from all 100 | 101 | 102 | 103 | {% endif -%} 104 | -------------------------------------------------------------------------------- /charmhelpers/contrib/python.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2019 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # deprecated aliases for backwards compatibility 16 | from charmhelpers.fetch.python import debug # noqa 17 | from charmhelpers.fetch.python import packages # noqa 18 | from charmhelpers.fetch.python import rpdb # noqa 19 | from charmhelpers.fetch.python import version # noqa 20 | -------------------------------------------------------------------------------- /charmhelpers/contrib/storage/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/storage/linux/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/contrib/storage/linux/bcache.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import os 15 | import json 16 | 17 | from charmhelpers.core.hookenv import log 18 | 19 | stats_intervals = ['stats_day', 'stats_five_minute', 20 | 'stats_hour', 'stats_total'] 21 | 22 | SYSFS = '/sys' 23 | 24 | 25 | class Bcache(object): 26 | """Bcache behaviour 27 | """ 28 | 29 | def __init__(self, cachepath): 30 | self.cachepath = cachepath 31 | 32 | @classmethod 33 | def fromdevice(cls, devname): 34 | return cls('{}/block/{}/bcache'.format(SYSFS, devname)) 35 | 36 | def __str__(self): 37 | return self.cachepath 38 | 39 | def get_stats(self, interval): 40 | """Get cache stats 41 | """ 42 | intervaldir = 'stats_{}'.format(interval) 43 | path = "{}/{}".format(self.cachepath, intervaldir) 44 | out = dict() 45 | for elem in os.listdir(path): 46 | out[elem] = open('{}/{}'.format(path, elem)).read().strip() 47 | return out 48 | 49 | 50 | def get_bcache_fs(): 51 | """Return all cache sets 52 | """ 53 | cachesetroot = "{}/fs/bcache".format(SYSFS) 54 | try: 55 | dirs = os.listdir(cachesetroot) 56 | except OSError: 57 | log("No bcache fs found") 58 | return [] 59 | cacheset = set([Bcache('{}/{}'.format(cachesetroot, d)) for d in dirs if not d.startswith('register')]) 60 | return cacheset 61 | 62 | 63 | def get_stats_action(cachespec, interval): 64 | """Action for getting bcache statistics for a given cachespec. 65 | Cachespec can either be a device name, eg. 'sdb', which will retrieve 66 | cache stats for the given device, or 'global', which will retrieve stats 67 | for all cachesets 68 | """ 69 | if cachespec == 'global': 70 | caches = get_bcache_fs() 71 | else: 72 | caches = [Bcache.fromdevice(cachespec)] 73 | res = dict((c.cachepath, c.get_stats(interval)) for c in caches) 74 | return json.dumps(res, indent=4, separators=(',', ': ')) 75 | -------------------------------------------------------------------------------- /charmhelpers/contrib/storage/linux/loopback.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import re 17 | from subprocess import ( 18 | check_call, 19 | check_output, 20 | ) 21 | 22 | 23 | ################################################## 24 | # loopback device helpers. 25 | ################################################## 26 | def loopback_devices(): 27 | ''' 28 | Parse through 'losetup -a' output to determine currently mapped 29 | loopback devices. Output is expected to look like: 30 | 31 | /dev/loop0: [0807]:961814 (/tmp/my.img) 32 | 33 | or: 34 | 35 | /dev/loop0: [0807]:961814 (/tmp/my.img (deleted)) 36 | 37 | :returns: dict: a dict mapping {loopback_dev: backing_file} 38 | ''' 39 | loopbacks = {} 40 | cmd = ['losetup', '-a'] 41 | output = check_output(cmd).decode('utf-8') 42 | devs = [d.strip().split(' ', 2) for d in output.splitlines() if d != ''] 43 | for dev, _, f in devs: 44 | loopbacks[dev.replace(':', '')] = re.search(r'\((.+)\)', f).groups()[0] 45 | return loopbacks 46 | 47 | 48 | def create_loopback(file_path): 49 | ''' 50 | Create a loopback device for a given backing file. 51 | 52 | :returns: str: Full path to new loopback device (eg, /dev/loop0) 53 | ''' 54 | file_path = os.path.abspath(file_path) 55 | check_call(['losetup', '--find', file_path]) 56 | for d, f in loopback_devices().items(): 57 | if f == file_path: 58 | return d 59 | 60 | 61 | def ensure_loopback_device(path, size): 62 | ''' 63 | Ensure a loopback device exists for a given backing file path and size. 64 | If it a loopback device is not mapped to file, a new one will be created. 65 | 66 | TODO: Confirm size of found loopback device. 67 | 68 | :returns: str: Full path to the ensured loopback device (eg, /dev/loop0) 69 | ''' 70 | for d, f in loopback_devices().items(): 71 | if f == path: 72 | return d 73 | 74 | if not os.path.exists(path): 75 | cmd = ['truncate', '--size', size, path] 76 | check_call(cmd) 77 | 78 | return create_loopback(path) 79 | 80 | 81 | def is_mapped_loopback_device(device): 82 | """ 83 | Checks if a given device name is an existing/mapped loopback device. 84 | :param device: str: Full path to the device (eg, /dev/loop1). 85 | :returns: str: Path to the backing file if is a loopback device 86 | empty string otherwise 87 | """ 88 | return loopback_devices().get(device, "") 89 | -------------------------------------------------------------------------------- /charmhelpers/core/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/core/decorators.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | # 16 | # Copyright 2014 Canonical Ltd. 17 | # 18 | # Authors: 19 | # Edward Hope-Morley 20 | # 21 | 22 | import time 23 | 24 | from charmhelpers.core.hookenv import ( 25 | log, 26 | INFO, 27 | ) 28 | 29 | 30 | def retry_on_exception(num_retries, base_delay=0, exc_type=Exception): 31 | """If the decorated function raises exception exc_type, allow num_retries 32 | retry attempts before raise the exception. 33 | """ 34 | def _retry_on_exception_inner_1(f): 35 | def _retry_on_exception_inner_2(*args, **kwargs): 36 | retries = num_retries 37 | multiplier = 1 38 | while True: 39 | try: 40 | return f(*args, **kwargs) 41 | except exc_type: 42 | if not retries: 43 | raise 44 | 45 | delay = base_delay * multiplier 46 | multiplier += 1 47 | log("Retrying '%s' %d more times (delay=%s)" % 48 | (f.__name__, retries, delay), level=INFO) 49 | retries -= 1 50 | if delay: 51 | time.sleep(delay) 52 | 53 | return _retry_on_exception_inner_2 54 | 55 | return _retry_on_exception_inner_1 56 | 57 | 58 | def retry_on_predicate(num_retries, predicate_fun, base_delay=0): 59 | """Retry based on return value 60 | 61 | The return value of the decorated function is passed to the given predicate_fun. If the 62 | result of the predicate is False, retry the decorated function up to num_retries times 63 | 64 | An exponential backoff up to base_delay^num_retries seconds can be introduced by setting 65 | base_delay to a nonzero value. The default is to run with a zero (i.e. no) delay 66 | 67 | :param num_retries: Max. number of retries to perform 68 | :type num_retries: int 69 | :param predicate_fun: Predicate function to determine if a retry is necessary 70 | :type predicate_fun: callable 71 | :param base_delay: Starting value in seconds for exponential delay, defaults to 0 (no delay) 72 | :type base_delay: float 73 | """ 74 | def _retry_on_pred_inner_1(f): 75 | def _retry_on_pred_inner_2(*args, **kwargs): 76 | retries = num_retries 77 | multiplier = 1 78 | delay = base_delay 79 | while True: 80 | result = f(*args, **kwargs) 81 | if predicate_fun(result) or retries <= 0: 82 | return result 83 | delay *= multiplier 84 | multiplier += 1 85 | log("Result {}, retrying '{}' {} more times (delay={})".format( 86 | result, f.__name__, retries, delay), level=INFO) 87 | retries -= 1 88 | if delay: 89 | time.sleep(delay) 90 | 91 | return _retry_on_pred_inner_2 92 | 93 | return _retry_on_pred_inner_1 94 | -------------------------------------------------------------------------------- /charmhelpers/core/files.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright 2014-2015 Canonical Limited. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | __author__ = 'Jorge Niedbalski ' 19 | 20 | import os 21 | import subprocess 22 | 23 | 24 | def sed(filename, before, after, flags='g'): 25 | """ 26 | Search and replaces the given pattern on filename. 27 | 28 | :param filename: relative or absolute file path. 29 | :param before: expression to be replaced (see 'man sed') 30 | :param after: expression to replace with (see 'man sed') 31 | :param flags: sed-compatible regex flags in example, to make 32 | the search and replace case insensitive, specify ``flags="i"``. 33 | The ``g`` flag is always specified regardless, so you do not 34 | need to remember to include it when overriding this parameter. 35 | :returns: If the sed command exit code was zero then return, 36 | otherwise raise CalledProcessError. 37 | """ 38 | expression = r's/{0}/{1}/{2}'.format(before, 39 | after, flags) 40 | 41 | return subprocess.check_call(["sed", "-i", "-r", "-e", 42 | expression, 43 | os.path.expanduser(filename)]) 44 | -------------------------------------------------------------------------------- /charmhelpers/core/host_factory/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-swift-proxy/b4aa3c978b8be71585d43475598394451dbad11c/charmhelpers/core/host_factory/__init__.py -------------------------------------------------------------------------------- /charmhelpers/core/host_factory/centos.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import yum 3 | import os 4 | 5 | from charmhelpers.core.strutils import BasicStringComparator 6 | 7 | 8 | class CompareHostReleases(BasicStringComparator): 9 | """Provide comparisons of Host releases. 10 | 11 | Use in the form of 12 | 13 | if CompareHostReleases(release) > 'trusty': 14 | # do something with mitaka 15 | """ 16 | 17 | def __init__(self, item): 18 | raise NotImplementedError( 19 | "CompareHostReleases() is not implemented for CentOS") 20 | 21 | 22 | def service_available(service_name): 23 | # """Determine whether a system service is available.""" 24 | if os.path.isdir('/run/systemd/system'): 25 | cmd = ['systemctl', 'is-enabled', service_name] 26 | else: 27 | cmd = ['service', service_name, 'is-enabled'] 28 | return subprocess.call(cmd) == 0 29 | 30 | 31 | def add_new_group(group_name, system_group=False, gid=None): 32 | cmd = ['groupadd'] 33 | if gid: 34 | cmd.extend(['--gid', str(gid)]) 35 | if system_group: 36 | cmd.append('-r') 37 | cmd.append(group_name) 38 | subprocess.check_call(cmd) 39 | 40 | 41 | def lsb_release(): 42 | """Return /etc/os-release in a dict.""" 43 | d = {} 44 | with open('/etc/os-release', 'r') as lsb: 45 | for l in lsb: 46 | s = l.split('=') 47 | if len(s) != 2: 48 | continue 49 | d[s[0].strip()] = s[1].strip() 50 | return d 51 | 52 | 53 | def cmp_pkgrevno(package, revno, pkgcache=None): 54 | """Compare supplied revno with the revno of the installed package. 55 | 56 | * 1 => Installed revno is greater than supplied arg 57 | * 0 => Installed revno is the same as supplied arg 58 | * -1 => Installed revno is less than supplied arg 59 | 60 | This function imports YumBase function if the pkgcache argument 61 | is None. 62 | """ 63 | if not pkgcache: 64 | y = yum.YumBase() 65 | packages = y.doPackageLists() 66 | pkgcache = {i.Name: i.version for i in packages['installed']} 67 | pkg = pkgcache[package] 68 | if pkg > revno: 69 | return 1 70 | if pkg < revno: 71 | return -1 72 | return 0 73 | -------------------------------------------------------------------------------- /charmhelpers/core/host_factory/ubuntu.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | from charmhelpers.core.hookenv import cached 4 | from charmhelpers.core.strutils import BasicStringComparator 5 | 6 | 7 | UBUNTU_RELEASES = ( 8 | 'lucid', 9 | 'maverick', 10 | 'natty', 11 | 'oneiric', 12 | 'precise', 13 | 'quantal', 14 | 'raring', 15 | 'saucy', 16 | 'trusty', 17 | 'utopic', 18 | 'vivid', 19 | 'wily', 20 | 'xenial', 21 | 'yakkety', 22 | 'zesty', 23 | 'artful', 24 | 'bionic', 25 | 'cosmic', 26 | 'disco', 27 | 'eoan', 28 | 'focal', 29 | 'groovy', 30 | 'hirsute', 31 | 'impish', 32 | 'jammy', 33 | 'kinetic', 34 | 'lunar', 35 | 'mantic', 36 | 'noble', 37 | 'oracular', 38 | ) 39 | 40 | 41 | class CompareHostReleases(BasicStringComparator): 42 | """Provide comparisons of Ubuntu releases. 43 | 44 | Use in the form of 45 | 46 | if CompareHostReleases(release) > 'trusty': 47 | # do something with mitaka 48 | """ 49 | _list = UBUNTU_RELEASES 50 | 51 | 52 | def service_available(service_name): 53 | """Determine whether a system service is available""" 54 | try: 55 | subprocess.check_output( 56 | ['service', service_name, 'status'], 57 | stderr=subprocess.STDOUT).decode('UTF-8') 58 | except subprocess.CalledProcessError as e: 59 | return b'unrecognized service' not in e.output 60 | else: 61 | return True 62 | 63 | 64 | def add_new_group(group_name, system_group=False, gid=None): 65 | cmd = ['addgroup'] 66 | if gid: 67 | cmd.extend(['--gid', str(gid)]) 68 | if system_group: 69 | cmd.append('--system') 70 | else: 71 | cmd.extend([ 72 | '--group', 73 | ]) 74 | cmd.append(group_name) 75 | subprocess.check_call(cmd) 76 | 77 | 78 | def lsb_release(): 79 | """Return /etc/lsb-release in a dict""" 80 | d = {} 81 | with open('/etc/lsb-release', 'r') as lsb: 82 | for l in lsb: 83 | k, v = l.split('=') 84 | d[k.strip()] = v.strip() 85 | return d 86 | 87 | 88 | def get_distrib_codename(): 89 | """Return the codename of the distribution 90 | :returns: The codename 91 | :rtype: str 92 | """ 93 | return lsb_release()['DISTRIB_CODENAME'].lower() 94 | 95 | 96 | def cmp_pkgrevno(package, revno, pkgcache=None): 97 | """Compare supplied revno with the revno of the installed package. 98 | 99 | * 1 => Installed revno is greater than supplied arg 100 | * 0 => Installed revno is the same as supplied arg 101 | * -1 => Installed revno is less than supplied arg 102 | 103 | This function imports apt_cache function from charmhelpers.fetch if 104 | the pkgcache argument is None. Be sure to add charmhelpers.fetch if 105 | you call this function, or pass an apt_pkg.Cache() instance. 106 | """ 107 | from charmhelpers.fetch import apt_pkg, get_installed_version 108 | if not pkgcache: 109 | current_ver = get_installed_version(package) 110 | else: 111 | pkg = pkgcache[package] 112 | current_ver = pkg.current_ver 113 | 114 | return apt_pkg.version_compare(current_ver.ver_str, revno) 115 | 116 | 117 | @cached 118 | def arch(): 119 | """Return the package architecture as a string. 120 | 121 | :returns: the architecture 122 | :rtype: str 123 | :raises: subprocess.CalledProcessError if dpkg command fails 124 | """ 125 | return subprocess.check_output( 126 | ['dpkg', '--print-architecture'] 127 | ).rstrip().decode('UTF-8') 128 | -------------------------------------------------------------------------------- /charmhelpers/core/hugepage.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | # Copyright 2014-2015 Canonical Limited. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import yaml 18 | from charmhelpers.core import fstab 19 | from charmhelpers.core import sysctl 20 | from charmhelpers.core.host import ( 21 | add_group, 22 | add_user_to_group, 23 | fstab_mount, 24 | mkdir, 25 | ) 26 | from charmhelpers.core.strutils import bytes_from_string 27 | from subprocess import check_output 28 | 29 | 30 | def hugepage_support(user, group='hugetlb', nr_hugepages=256, 31 | max_map_count=65536, mnt_point='/run/hugepages/kvm', 32 | pagesize='2MB', mount=True, set_shmmax=False): 33 | """Enable hugepages on system. 34 | 35 | Args: 36 | user (str) -- Username to allow access to hugepages to 37 | group (str) -- Group name to own hugepages 38 | nr_hugepages (int) -- Number of pages to reserve 39 | max_map_count (int) -- Number of Virtual Memory Areas a process can own 40 | mnt_point (str) -- Directory to mount hugepages on 41 | pagesize (str) -- Size of hugepages 42 | mount (bool) -- Whether to Mount hugepages 43 | """ 44 | group_info = add_group(group) 45 | gid = group_info.gr_gid 46 | add_user_to_group(user, group) 47 | if max_map_count < 2 * nr_hugepages: 48 | max_map_count = 2 * nr_hugepages 49 | sysctl_settings = { 50 | 'vm.nr_hugepages': nr_hugepages, 51 | 'vm.max_map_count': max_map_count, 52 | 'vm.hugetlb_shm_group': gid, 53 | } 54 | if set_shmmax: 55 | shmmax_current = int(check_output(['sysctl', '-n', 'kernel.shmmax'])) 56 | shmmax_minsize = bytes_from_string(pagesize) * nr_hugepages 57 | if shmmax_minsize > shmmax_current: 58 | sysctl_settings['kernel.shmmax'] = shmmax_minsize 59 | sysctl.create(yaml.dump(sysctl_settings), '/etc/sysctl.d/10-hugepage.conf') 60 | mkdir(mnt_point, owner='root', group='root', perms=0o755, force=False) 61 | lfstab = fstab.Fstab() 62 | fstab_entry = lfstab.get_entry_by_attr('mountpoint', mnt_point) 63 | if fstab_entry: 64 | lfstab.remove_entry(fstab_entry) 65 | entry = lfstab.Entry('nodev', mnt_point, 'hugetlbfs', 66 | 'mode=1770,gid={},pagesize={}'.format(gid, pagesize), 0, 0) 67 | lfstab.add_entry(entry) 68 | if mount: 69 | fstab_mount(mnt_point) 70 | -------------------------------------------------------------------------------- /charmhelpers/core/kernel.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright 2014-2015 Canonical Limited. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import re 19 | import subprocess 20 | 21 | from charmhelpers.osplatform import get_platform 22 | from charmhelpers.core.hookenv import ( 23 | log, 24 | INFO 25 | ) 26 | 27 | __platform__ = get_platform() 28 | if __platform__ == "ubuntu": 29 | from charmhelpers.core.kernel_factory.ubuntu import ( # NOQA:F401 30 | persistent_modprobe, 31 | update_initramfs, 32 | ) # flake8: noqa -- ignore F401 for this import 33 | elif __platform__ == "centos": 34 | from charmhelpers.core.kernel_factory.centos import ( # NOQA:F401 35 | persistent_modprobe, 36 | update_initramfs, 37 | ) # flake8: noqa -- ignore F401 for this import 38 | 39 | __author__ = "Jorge Niedbalski " 40 | 41 | 42 | def modprobe(module, persist=True): 43 | """Load a kernel module and configure for auto-load on reboot.""" 44 | cmd = ['modprobe', module] 45 | 46 | log('Loading kernel module %s' % module, level=INFO) 47 | 48 | subprocess.check_call(cmd) 49 | if persist: 50 | persistent_modprobe(module) 51 | 52 | 53 | def rmmod(module, force=False): 54 | """Remove a module from the linux kernel""" 55 | cmd = ['rmmod'] 56 | if force: 57 | cmd.append('-f') 58 | cmd.append(module) 59 | log('Removing kernel module %s' % module, level=INFO) 60 | return subprocess.check_call(cmd) 61 | 62 | 63 | def lsmod(): 64 | """Shows what kernel modules are currently loaded""" 65 | return subprocess.check_output(['lsmod'], 66 | universal_newlines=True) 67 | 68 | 69 | def is_module_loaded(module): 70 | """Checks if a kernel module is already loaded""" 71 | matches = re.findall('^%s[ ]+' % module, lsmod(), re.M) 72 | return len(matches) > 0 73 | -------------------------------------------------------------------------------- /charmhelpers/core/kernel_factory/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-swift-proxy/b4aa3c978b8be71585d43475598394451dbad11c/charmhelpers/core/kernel_factory/__init__.py -------------------------------------------------------------------------------- /charmhelpers/core/kernel_factory/centos.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | import os 3 | 4 | 5 | def persistent_modprobe(module): 6 | """Load a kernel module and configure for auto-load on reboot.""" 7 | if not os.path.exists('/etc/rc.modules'): 8 | open('/etc/rc.modules', 'a') 9 | os.chmod('/etc/rc.modules', 111) 10 | with open('/etc/rc.modules', 'r+') as modules: 11 | if module not in modules.read(): 12 | modules.write('modprobe %s\n' % module) 13 | 14 | 15 | def update_initramfs(version='all'): 16 | """Updates an initramfs image.""" 17 | return subprocess.check_call(["dracut", "-f", version]) 18 | -------------------------------------------------------------------------------- /charmhelpers/core/kernel_factory/ubuntu.py: -------------------------------------------------------------------------------- 1 | import subprocess 2 | 3 | 4 | def persistent_modprobe(module): 5 | """Load a kernel module and configure for auto-load on reboot.""" 6 | with open('/etc/modules', 'r+') as modules: 7 | if module not in modules.read(): 8 | modules.write(module + "\n") 9 | 10 | 11 | def update_initramfs(version='all'): 12 | """Updates an initramfs image.""" 13 | return subprocess.check_call(["update-initramfs", "-k", version, "-u"]) 14 | -------------------------------------------------------------------------------- /charmhelpers/core/services/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from .base import * # NOQA 16 | from .helpers import * # NOQA 17 | -------------------------------------------------------------------------------- /charmhelpers/core/sysctl.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | 4 | # Copyright 2014-2015 Canonical Limited. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import yaml 19 | 20 | from subprocess import check_call, CalledProcessError 21 | 22 | from charmhelpers.core.hookenv import ( 23 | log, 24 | DEBUG, 25 | ERROR, 26 | WARNING, 27 | ) 28 | 29 | from charmhelpers.core.host import is_container 30 | 31 | __author__ = 'Jorge Niedbalski R. ' 32 | 33 | 34 | def create(sysctl_dict, sysctl_file, ignore=False): 35 | """Creates a sysctl.conf file from a YAML associative array 36 | 37 | :param sysctl_dict: a dict or YAML-formatted string of sysctl 38 | options eg "{ 'kernel.max_pid': 1337 }" 39 | :type sysctl_dict: str 40 | :param sysctl_file: path to the sysctl file to be saved 41 | :type sysctl_file: str or unicode 42 | :param ignore: If True, ignore "unknown variable" errors. 43 | :type ignore: bool 44 | :returns: None 45 | """ 46 | if type(sysctl_dict) is not dict: 47 | try: 48 | sysctl_dict_parsed = yaml.safe_load(sysctl_dict) 49 | except yaml.YAMLError: 50 | log("Error parsing YAML sysctl_dict: {}".format(sysctl_dict), 51 | level=ERROR) 52 | return 53 | else: 54 | sysctl_dict_parsed = sysctl_dict 55 | 56 | with open(sysctl_file, "w") as fd: 57 | for key, value in sysctl_dict_parsed.items(): 58 | fd.write("{}={}\n".format(key, value)) 59 | 60 | log("Updating sysctl_file: {} values: {}".format(sysctl_file, 61 | sysctl_dict_parsed), 62 | level=DEBUG) 63 | 64 | call = ["sysctl", "-p", sysctl_file] 65 | if ignore: 66 | call.append("-e") 67 | 68 | try: 69 | check_call(call) 70 | except CalledProcessError as e: 71 | if is_container(): 72 | log("Error setting some sysctl keys in this container: {}".format(e.output), 73 | level=WARNING) 74 | else: 75 | raise e 76 | -------------------------------------------------------------------------------- /charmhelpers/core/templating.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | 17 | from charmhelpers.core import host 18 | from charmhelpers.core import hookenv 19 | 20 | 21 | def render(source, target, context, owner='root', group='root', 22 | perms=0o444, templates_dir=None, encoding='UTF-8', 23 | template_loader=None, config_template=None): 24 | """ 25 | Render a template. 26 | 27 | The `source` path, if not absolute, is relative to the `templates_dir`. 28 | 29 | The `target` path should be absolute. It can also be `None`, in which 30 | case no file will be written. 31 | 32 | The context should be a dict containing the values to be replaced in the 33 | template. 34 | 35 | config_template may be provided to render from a provided template instead 36 | of loading from a file. 37 | 38 | The `owner`, `group`, and `perms` options will be passed to `write_file`. 39 | 40 | If omitted, `templates_dir` defaults to the `templates` folder in the charm. 41 | 42 | The rendered template will be written to the file as well as being returned 43 | as a string. 44 | 45 | Note: Using this requires python3-jinja2; if it is not installed, calling 46 | this will attempt to use charmhelpers.fetch.apt_install to install it. 47 | """ 48 | try: 49 | from jinja2 import FileSystemLoader, Environment, exceptions 50 | except ImportError: 51 | try: 52 | from charmhelpers.fetch import apt_install 53 | except ImportError: 54 | hookenv.log('Could not import jinja2, and could not import ' 55 | 'charmhelpers.fetch to install it', 56 | level=hookenv.ERROR) 57 | raise 58 | apt_install('python3-jinja2', fatal=True) 59 | from jinja2 import FileSystemLoader, Environment, exceptions 60 | 61 | if template_loader: 62 | template_env = Environment(loader=template_loader) 63 | else: 64 | if templates_dir is None: 65 | templates_dir = os.path.join(hookenv.charm_dir(), 'templates') 66 | template_env = Environment(loader=FileSystemLoader(templates_dir)) 67 | 68 | # load from a string if provided explicitly 69 | if config_template is not None: 70 | template = template_env.from_string(config_template) 71 | else: 72 | try: 73 | source = source 74 | template = template_env.get_template(source) 75 | except exceptions.TemplateNotFound as e: 76 | hookenv.log('Could not load template %s from %s.' % 77 | (source, templates_dir), 78 | level=hookenv.ERROR) 79 | raise e 80 | content = template.render(context) 81 | if target is not None: 82 | target_dir = os.path.dirname(target) 83 | if not os.path.exists(target_dir): 84 | # This is a terrible default directory permission, as the file 85 | # or its siblings will often contain secrets. 86 | host.mkdir(os.path.dirname(target), owner, group, perms=0o755) 87 | host.write_file(target, content.encode(encoding), owner, group, perms) 88 | return content 89 | -------------------------------------------------------------------------------- /charmhelpers/fetch/bzrurl.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | from subprocess import STDOUT, check_output 17 | from charmhelpers.fetch import ( 18 | BaseFetchHandler, 19 | UnhandledSource, 20 | filter_installed_packages, 21 | install, 22 | ) 23 | from charmhelpers.core.host import mkdir 24 | 25 | 26 | if filter_installed_packages(['bzr']) != []: 27 | install(['bzr']) 28 | if filter_installed_packages(['bzr']) != []: 29 | raise NotImplementedError('Unable to install bzr') 30 | 31 | 32 | class BzrUrlFetchHandler(BaseFetchHandler): 33 | """Handler for bazaar branches via generic and lp URLs.""" 34 | 35 | def can_handle(self, source): 36 | url_parts = self.parse_url(source) 37 | if url_parts.scheme not in ('bzr+ssh', 'lp', ''): 38 | return False 39 | elif not url_parts.scheme: 40 | return os.path.exists(os.path.join(source, '.bzr')) 41 | else: 42 | return True 43 | 44 | def branch(self, source, dest, revno=None): 45 | if not self.can_handle(source): 46 | raise UnhandledSource("Cannot handle {}".format(source)) 47 | cmd_opts = [] 48 | if revno: 49 | cmd_opts += ['-r', str(revno)] 50 | if os.path.exists(dest): 51 | cmd = ['bzr', 'pull'] 52 | cmd += cmd_opts 53 | cmd += ['--overwrite', '-d', dest, source] 54 | else: 55 | cmd = ['bzr', 'branch'] 56 | cmd += cmd_opts 57 | cmd += [source, dest] 58 | check_output(cmd, stderr=STDOUT) 59 | 60 | def install(self, source, dest=None, revno=None): 61 | url_parts = self.parse_url(source) 62 | branch_name = url_parts.path.strip("/").split("/")[-1] 63 | if dest: 64 | dest_dir = os.path.join(dest, branch_name) 65 | else: 66 | dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", 67 | branch_name) 68 | 69 | if dest and not os.path.exists(dest): 70 | mkdir(dest, perms=0o755) 71 | 72 | try: 73 | self.branch(source, dest_dir, revno) 74 | except OSError as e: 75 | raise UnhandledSource(e.strerror) 76 | return dest_dir 77 | -------------------------------------------------------------------------------- /charmhelpers/fetch/giturl.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | from subprocess import check_output, CalledProcessError, STDOUT 17 | from charmhelpers.fetch import ( 18 | BaseFetchHandler, 19 | UnhandledSource, 20 | filter_installed_packages, 21 | install, 22 | ) 23 | 24 | if filter_installed_packages(['git']) != []: 25 | install(['git']) 26 | if filter_installed_packages(['git']) != []: 27 | raise NotImplementedError('Unable to install git') 28 | 29 | 30 | class GitUrlFetchHandler(BaseFetchHandler): 31 | """Handler for git branches via generic and github URLs.""" 32 | 33 | def can_handle(self, source): 34 | url_parts = self.parse_url(source) 35 | # TODO (mattyw) no support for ssh git@ yet 36 | if url_parts.scheme not in ('http', 'https', 'git', ''): 37 | return False 38 | elif not url_parts.scheme: 39 | return os.path.exists(os.path.join(source, '.git')) 40 | else: 41 | return True 42 | 43 | def clone(self, source, dest, branch="master", depth=None): 44 | if not self.can_handle(source): 45 | raise UnhandledSource("Cannot handle {}".format(source)) 46 | 47 | if os.path.exists(dest): 48 | cmd = ['git', '-C', dest, 'pull', source, branch] 49 | else: 50 | cmd = ['git', 'clone', source, dest, '--branch', branch] 51 | if depth: 52 | cmd.extend(['--depth', depth]) 53 | check_output(cmd, stderr=STDOUT) 54 | 55 | def install(self, source, branch="master", dest=None, depth=None): 56 | url_parts = self.parse_url(source) 57 | branch_name = url_parts.path.strip("/").split("/")[-1] 58 | if dest: 59 | dest_dir = os.path.join(dest, branch_name) 60 | else: 61 | dest_dir = os.path.join(os.environ.get('CHARM_DIR'), "fetched", 62 | branch_name) 63 | try: 64 | self.clone(source, dest_dir, branch, depth) 65 | except CalledProcessError as e: 66 | raise UnhandledSource(e) 67 | except OSError as e: 68 | raise UnhandledSource(e.strerror) 69 | return dest_dir 70 | -------------------------------------------------------------------------------- /charmhelpers/fetch/python/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2019 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /charmhelpers/fetch/python/debug.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright 2014-2015 Canonical Limited. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import atexit 19 | import sys 20 | 21 | from charmhelpers.fetch.python.rpdb import Rpdb 22 | from charmhelpers.core.hookenv import ( 23 | open_port, 24 | close_port, 25 | ERROR, 26 | log 27 | ) 28 | 29 | __author__ = "Jorge Niedbalski " 30 | 31 | DEFAULT_ADDR = "0.0.0.0" 32 | DEFAULT_PORT = 4444 33 | 34 | 35 | def _error(message): 36 | log(message, level=ERROR) 37 | 38 | 39 | def set_trace(addr=DEFAULT_ADDR, port=DEFAULT_PORT): 40 | """ 41 | Set a trace point using the remote debugger 42 | """ 43 | atexit.register(close_port, port) 44 | try: 45 | log("Starting a remote python debugger session on %s:%s" % (addr, 46 | port)) 47 | open_port(port) 48 | debugger = Rpdb(addr=addr, port=port) 49 | debugger.set_trace(sys._getframe().f_back) 50 | except Exception: 51 | _error("Cannot start a remote debug session on %s:%s" % (addr, 52 | port)) 53 | -------------------------------------------------------------------------------- /charmhelpers/fetch/python/rpdb.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """Remote Python Debugger (pdb wrapper).""" 16 | 17 | import pdb 18 | import socket 19 | import sys 20 | 21 | __author__ = "Bertrand Janin " 22 | __version__ = "0.1.3" 23 | 24 | 25 | class Rpdb(pdb.Pdb): 26 | 27 | def __init__(self, addr="127.0.0.1", port=4444): 28 | """Initialize the socket and initialize pdb.""" 29 | 30 | # Backup stdin and stdout before replacing them by the socket handle 31 | self.old_stdout = sys.stdout 32 | self.old_stdin = sys.stdin 33 | 34 | # Open a 'reusable' socket to let the webapp reload on the same port 35 | self.skt = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 36 | self.skt.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) 37 | self.skt.bind((addr, port)) 38 | self.skt.listen(1) 39 | (clientsocket, address) = self.skt.accept() 40 | handle = clientsocket.makefile('rw') 41 | pdb.Pdb.__init__(self, completekey='tab', stdin=handle, stdout=handle) 42 | sys.stdout = sys.stdin = handle 43 | 44 | def shutdown(self): 45 | """Revert stdin and stdout, close the socket.""" 46 | sys.stdout = self.old_stdout 47 | sys.stdin = self.old_stdin 48 | self.skt.close() 49 | self.set_continue() 50 | 51 | def do_continue(self, arg): 52 | """Stop all operation on ``continue``.""" 53 | self.shutdown() 54 | return 1 55 | 56 | do_EOF = do_quit = do_exit = do_c = do_cont = do_continue 57 | -------------------------------------------------------------------------------- /charmhelpers/fetch/python/version.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # coding: utf-8 3 | 4 | # Copyright 2014-2015 Canonical Limited. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | 18 | import sys 19 | 20 | __author__ = "Jorge Niedbalski " 21 | 22 | 23 | def current_version(): 24 | """Current system python version""" 25 | return sys.version_info 26 | 27 | 28 | def current_version_string(): 29 | """Current system python version as string major.minor.micro""" 30 | return "{0}.{1}.{2}".format(sys.version_info.major, 31 | sys.version_info.minor, 32 | sys.version_info.micro) 33 | -------------------------------------------------------------------------------- /charmhelpers/osplatform.py: -------------------------------------------------------------------------------- 1 | import platform 2 | import os 3 | 4 | 5 | def get_platform(): 6 | """Return the current OS platform. 7 | 8 | For example: if current os platform is Ubuntu then a string "ubuntu" 9 | will be returned (which is the name of the module). 10 | This string is used to decide which platform module should be imported. 11 | """ 12 | current_platform = _get_current_platform() 13 | 14 | if "Ubuntu" in current_platform: 15 | return "ubuntu" 16 | elif "CentOS" in current_platform: 17 | return "centos" 18 | elif "debian" in current_platform or "Debian" in current_platform: 19 | # Stock Python does not detect Ubuntu and instead returns debian. 20 | # Or at least it does in some build environments like Travis CI 21 | return "ubuntu" 22 | elif "elementary" in current_platform: 23 | # ElementaryOS fails to run tests locally without this. 24 | return "ubuntu" 25 | elif "Pop!_OS" in current_platform: 26 | # Pop!_OS also fails to run tests locally without this. 27 | return "ubuntu" 28 | else: 29 | raise RuntimeError("This module is not supported on {}." 30 | .format(current_platform)) 31 | 32 | 33 | def _get_current_platform(): 34 | """Return the current platform information for the OS. 35 | 36 | Attempts to lookup linux distribution information from the platform 37 | module for releases of python < 3.7. For newer versions of python, 38 | the platform is determined from the /etc/os-release file. 39 | """ 40 | # linux_distribution is deprecated and will be removed in Python 3.7 41 | # Warnings *not* disabled, as we certainly need to fix this. 42 | if hasattr(platform, 'linux_distribution'): 43 | tuple_platform = platform.linux_distribution() 44 | current_platform = tuple_platform[0] 45 | else: 46 | current_platform = _get_platform_from_fs() 47 | 48 | return current_platform 49 | 50 | 51 | def _get_platform_from_fs(): 52 | """Get Platform from /etc/os-release.""" 53 | with open(os.path.join(os.sep, 'etc', 'os-release')) as fin: 54 | content = dict( 55 | line.split('=', 1) 56 | for line in fin.read().splitlines() 57 | if '=' in line 58 | ) 59 | for k, v in content.items(): 60 | content[k] = v.strip('"') 61 | return content["NAME"] 62 | -------------------------------------------------------------------------------- /charmhelpers/payload/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014-2015 Canonical Limited. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | "Tools for working with files injected into a charm just before deployment." 16 | -------------------------------------------------------------------------------- /charmhelpers/payload/execd.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # Copyright 2014-2015 Canonical Limited. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | import os 18 | import sys 19 | import subprocess 20 | from charmhelpers.core import hookenv 21 | 22 | 23 | def default_execd_dir(): 24 | return os.path.join(os.environ['CHARM_DIR'], 'exec.d') 25 | 26 | 27 | def execd_module_paths(execd_dir=None): 28 | """Generate a list of full paths to modules within execd_dir.""" 29 | if not execd_dir: 30 | execd_dir = default_execd_dir() 31 | 32 | if not os.path.exists(execd_dir): 33 | return 34 | 35 | for subpath in os.listdir(execd_dir): 36 | module = os.path.join(execd_dir, subpath) 37 | if os.path.isdir(module): 38 | yield module 39 | 40 | 41 | def execd_submodule_paths(command, execd_dir=None): 42 | """Generate a list of full paths to the specified command within exec_dir. 43 | """ 44 | for module_path in execd_module_paths(execd_dir): 45 | path = os.path.join(module_path, command) 46 | if os.access(path, os.X_OK) and os.path.isfile(path): 47 | yield path 48 | 49 | 50 | def execd_run(command, execd_dir=None, die_on_error=True, stderr=subprocess.STDOUT): 51 | """Run command for each module within execd_dir which defines it.""" 52 | for submodule_path in execd_submodule_paths(command, execd_dir): 53 | try: 54 | subprocess.check_output(submodule_path, stderr=stderr, 55 | universal_newlines=True) 56 | except subprocess.CalledProcessError as e: 57 | hookenv.log("Error ({}) running {}. Output: {}".format( 58 | e.returncode, e.cmd, e.output)) 59 | if die_on_error: 60 | sys.exit(e.returncode) 61 | 62 | 63 | def execd_preinstall(execd_dir=None): 64 | """Run charm-pre-install for each module within execd_dir.""" 65 | execd_run('charm-pre-install', execd_dir=execd_dir) 66 | -------------------------------------------------------------------------------- /copyright: -------------------------------------------------------------------------------- 1 | Format: http://dep.debian.net/deps/dep5/ 2 | 3 | Files: * 4 | Copyright: Copyright 2012, Canonical Ltd., All Rights Reserved. 5 | License: Apache-2.0 6 | Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | not use this file except in compliance with the License. You may obtain 8 | a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | License for the specific language governing permissions and limitations 16 | under the License. 17 | -------------------------------------------------------------------------------- /files/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-swift-proxy/b4aa3c978b8be71585d43475598394451dbad11c/files/.gitkeep -------------------------------------------------------------------------------- /hardening.yaml: -------------------------------------------------------------------------------- 1 | # Overrides file for contrib.hardening. See README.hardening in 2 | # contrib.hardening for info on how to use this file. 3 | ssh: 4 | server: 5 | use_pam: 'yes' # juju requires this 6 | -------------------------------------------------------------------------------- /hooks/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | -------------------------------------------------------------------------------- /hooks/amqp-relation-broken: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/amqp-relation-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/amqp-relation-departed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/amqp-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/certificates-relation-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/certificates-relation-departed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/certificates-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/cluster-relation-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/cluster-relation-departed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/cluster-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/config-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/ha-relation-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/ha-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/identity-service-relation-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/identity-service-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/install: -------------------------------------------------------------------------------- 1 | #!/bin/bash -e 2 | # Wrapper to deal with newer Ubuntu versions that don't have py2 installed 3 | # by default. 4 | 5 | declare -a DEPS=('apt' 'netaddr' 'netifaces' 'pip' 'yaml' 'dnspython') 6 | 7 | check_and_install() { 8 | pkg="${1}-${2}" 9 | if ! dpkg -s ${pkg} 2>&1 > /dev/null; then 10 | apt-get -y install ${pkg} 11 | fi 12 | } 13 | 14 | PYTHON="python3" 15 | 16 | for dep in ${DEPS[@]}; do 17 | check_and_install ${PYTHON} ${dep} 18 | done 19 | 20 | exec ./hooks/install.real 21 | -------------------------------------------------------------------------------- /hooks/install.real: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/leader-deposed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/leader-elected: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/leader-settings-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/nrpe-external-master-relation-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/nrpe-external-master-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/object-store-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/post-series-upgrade: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/pre-series-upgrade: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/rings-consumer-relation-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/rings-consumer-relation-departed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/rings-consumer-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/rings-distributor-relation-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/rings-distributor-relation-departed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/rings-distributor-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/start: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/stop: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/swift-storage-relation-broken: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/swift-storage-relation-changed: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/swift-storage-relation-joined: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/update-status: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /hooks/upgrade-charm: -------------------------------------------------------------------------------- 1 | swift_hooks.py -------------------------------------------------------------------------------- /lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-swift-proxy/b4aa3c978b8be71585d43475598394451dbad11c/lib/__init__.py -------------------------------------------------------------------------------- /metadata.yaml: -------------------------------------------------------------------------------- 1 | name: swift-proxy 2 | summary: OpenStack Object Storage - Swift proxy service 3 | maintainer: OpenStack Charmers 4 | description: | 5 | OpenStack Object Storage (code-named Swift) is open source software for 6 | creating redundant, scalable object storage using clusters of standardized 7 | servers to store petabytes of accessible data. It is not a file system or 8 | real-time data storage system, but rather a long-term storage system for a 9 | more permanent type of static data that can be retrieved, leveraged, and 10 | then updated if necessary. Primary examples of data that best fit this 11 | type of storage model are virtual machine images, photo storage, email 12 | storage and backup archiving. Having no central "brain" or master point of 13 | control provides greater scalability, redundancy and permanence. 14 | . 15 | This charm deploys the Swift proxy service, providing HTTP based access 16 | onto underlying Swift storage services. 17 | tags: 18 | - openstack 19 | - cache-proxy 20 | series: 21 | - jammy 22 | extra-bindings: 23 | public: 24 | admin: 25 | internal: 26 | provides: 27 | nrpe-external-master: 28 | interface: nrpe-external-master 29 | scope: container 30 | object-store: 31 | interface: swift-proxy 32 | rings-distributor: 33 | interface: swift-global-cluster 34 | requires: 35 | swift-storage: 36 | interface: swift 37 | identity-service: 38 | interface: keystone 39 | ha: 40 | interface: hacluster 41 | scope: container 42 | amqp: 43 | interface: rabbitmq 44 | certificates: 45 | interface: tls-certificates 46 | rings-consumer: 47 | interface: swift-global-cluster 48 | peers: 49 | cluster: 50 | interface: swift-ha 51 | resources: 52 | policyd-override: 53 | type: file 54 | filename: policyd-override.zip 55 | description: The policy.d overrides file 56 | -------------------------------------------------------------------------------- /osci.yaml: -------------------------------------------------------------------------------- 1 | - project: 2 | templates: 3 | - charm-unit-jobs-py310 4 | check: 5 | jobs: 6 | - test-s3api-noble-caracal 7 | vars: 8 | needs_charm_build: true 9 | charm_build_name: swift-proxy 10 | build_type: charmcraft 11 | charmcraft_channel: 3.x/beta 12 | 13 | - job: 14 | name: test-s3api-noble-caracal 15 | parent: func-target 16 | dependencies: 17 | - charm-build 18 | - osci-lint 19 | - name: tox-py310 20 | soft: true 21 | vars: 22 | tox_extra_args: '-- test-s3api:noble-caracal' 23 | -------------------------------------------------------------------------------- /rename.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | charm=$(grep "charm_build_name" osci.yaml | awk '{print $2}') 3 | echo "renaming ${charm}_*.charm to ${charm}.charm" 4 | echo -n "pwd: " 5 | pwd 6 | ls -al 7 | echo "Removing bad downloaded charm maybe?" 8 | if [[ -e "${charm}.charm" ]]; 9 | then 10 | rm "${charm}.charm" 11 | fi 12 | echo "Renaming charm here." 13 | mv ${charm}_*.charm ${charm}.charm 14 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # This file is managed centrally by release-tools and should not be modified 2 | # within individual charm repos. See the 'global' dir contents for available 3 | # choices of *requirements.txt files for OpenStack Charms: 4 | # https://github.com/openstack-charmers/release-tools 5 | # 6 | # TODO: Distill the func test requirements from the lint/unit test 7 | # requirements. They are intertwined. Also, Zaza itself should specify 8 | # all of its own requirements and if it doesn't, fix it there. 9 | # 10 | pbr==5.6.0 11 | simplejson>=2.2.0 12 | netifaces>=0.10.4 13 | 14 | # NOTE: newer versions of cryptography require a Rust compiler to build, 15 | # see 16 | # * https://github.com/openstack-charmers/zaza/issues/421 17 | # * https://mail.python.org/pipermail/cryptography-dev/2021-January/001003.html 18 | # 19 | cryptography<3.4 20 | 21 | # Strange import error with newer netaddr: 22 | netaddr>0.7.16,<0.8.0 23 | 24 | Jinja2>=2.6 # BSD License (3 clause) 25 | six>=1.9.0 26 | 27 | dnspython 28 | 29 | psutil>=1.1.1,<2.0.0 30 | -------------------------------------------------------------------------------- /revision: -------------------------------------------------------------------------------- 1 | 147 2 | -------------------------------------------------------------------------------- /scripts/add_to_cluster: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | service corosync start || /bin/true 3 | sleep 2 4 | while ! service pacemaker start; do 5 | echo "Attempting to start pacemaker" 6 | sleep 1; 7 | done; 8 | crm node online 9 | sleep 2 10 | while crm status | egrep -q 'Stopped$'; do 11 | echo "Waiting for nodes to come online" 12 | sleep 1 13 | done 14 | -------------------------------------------------------------------------------- /scripts/remove_from_cluster: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | crm node standby 3 | service pacemaker stop 4 | service corosync stop 5 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = charm-swift-proxy 3 | summary = Charm module for OpenStack Swift Proxy 4 | description-file = 5 | README.md 6 | author = OpenStack 7 | author-email = openstack-discuss@lists.openstack.org 8 | home-page = https://docs.openstack.org/charm-guide/latest/ 9 | classifier = 10 | Intended Audience :: Developers 11 | Intended Audience :: System Administrators 12 | License :: OSI Approved :: Apache Software License 13 | Operating System :: POSIX :: Linux 14 | Programming Language :: Python 15 | Programming Language :: Python :: 3 16 | Programming Language :: Python :: 3.5 17 | Programming Language :: Python :: 3.6 18 | Programming Language :: Python :: 3.7 19 | 20 | [nosetests] 21 | verbosity=1 22 | with-coverage=1 23 | cover-erase=1 24 | cover-package=lib,swift_hooks 25 | -------------------------------------------------------------------------------- /swift_manager/.stestr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_path=./ 3 | top_dir=./ 4 | -------------------------------------------------------------------------------- /templates/memcached.conf: -------------------------------------------------------------------------------- 1 | # memcached default config file 2 | # 2003 - Jay Bonci 3 | # This configuration file is read by the start-memcached script provided as 4 | # part of the Debian GNU/Linux distribution. 5 | 6 | # Run memcached as a daemon. This command is implied, and is not needed for the 7 | # daemon to run. See the README.Debian that comes with this package for more 8 | # information. 9 | -d 10 | 11 | # Log memcached's output to /var/log/memcached 12 | logfile /var/log/memcached.log 13 | 14 | # Be verbose 15 | # -v 16 | 17 | # Be even more verbose (print client commands as well) 18 | # -vv 19 | 20 | # Start with a cap of 64 megs of memory. It's reasonable, and the daemon default 21 | # Note that the daemon will grow to this size, but does not start out holding this much 22 | # memory 23 | -m 64 24 | 25 | # Default connection port is 11211 26 | -p 11211 27 | 28 | # Run the daemon as root. The start-memcached will default to running as root if no 29 | # -u command is present in this config file 30 | -u memcache 31 | 32 | # Specify which IP address to listen on. The default is to listen on all IP addresses 33 | # This parameter is one of the only security measures that memcached has, so make sure 34 | # it's listening on a firewalled interface. 35 | -l {{ memcached_ip }} 36 | 37 | # Limit the number of simultaneous incoming connections. The daemon default is 1024 38 | # -c 1024 39 | 40 | # Lock down all paged memory. Consult with the README and homepage before you do this 41 | # -k 42 | 43 | # Return error when memory is exhausted (rather than removing items) 44 | # -M 45 | 46 | # Maximize core file limit 47 | # -r 48 | -------------------------------------------------------------------------------- /templates/mitaka/dispersion.conf: -------------------------------------------------------------------------------- 1 | [dispersion] 2 | auth_url = {{ auth_protocol }}://{{ keystone_host }}:{{ service_port }}/v2.0 3 | auth_user = {{ service_tenant }}:{{ service_user }} 4 | auth_key = {{ service_password }} 5 | endpoint_type = internalURL 6 | auth_version = 2 7 | -------------------------------------------------------------------------------- /templates/ports.conf: -------------------------------------------------------------------------------- 1 | # File written by Juju: don't open default ports on SSL environments (see LP 1845665). 2 | 3 | Listen 80 4 | 5 | -------------------------------------------------------------------------------- /templates/queens/dispersion.conf: -------------------------------------------------------------------------------- 1 | [dispersion] 2 | auth_url = {{ auth_protocol }}://{{ keystone_host }}:{{ auth_port }} 3 | auth_user = {{ service_user }} 4 | auth_key = {{ service_password }} 5 | endpoint_type = internalURL 6 | auth_version = 3 7 | project_domain_name = {{ admin_domain_name }} 8 | user_domain_name = {{ admin_domain_name }} 9 | project_name = {{ admin_tenant_name }} 10 | -------------------------------------------------------------------------------- /templates/swift-rings: -------------------------------------------------------------------------------- 1 | 2 | Order deny,allow 3 | {% for host in allowed_hosts %} 4 | Allow from {{ host }} 5 | {% endfor %} 6 | Deny from all 7 | 8 | -------------------------------------------------------------------------------- /templates/swift-rings.conf: -------------------------------------------------------------------------------- 1 | swift-rings -------------------------------------------------------------------------------- /templates/swift.conf: -------------------------------------------------------------------------------- 1 | [swift-hash] 2 | # random unique string that can never change (DO NOT LOSE) 3 | swift_hash_path_suffix = {{ swift_hash }} 4 | 5 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | # This file is managed centrally by release-tools and should not be modified 2 | # within individual charm repos. See the 'global' dir contents for available 3 | # choices of *requirements.txt files for OpenStack Charms: 4 | # https://github.com/openstack-charmers/release-tools 5 | # 6 | # TODO: Distill the func test requirements from the lint/unit test 7 | # requirements. They are intertwined. Also, Zaza itself should specify 8 | # all of its own requirements and if it doesn't, fix it there. 9 | # 10 | pyparsing<3.0.0 # aodhclient is pinned in zaza and needs pyparsing < 3.0.0, but cffi also needs it, so pin here. 11 | 12 | requests>=2.18.4 13 | 14 | stestr>=2.2.0 15 | 16 | # Dependency of stestr. Workaround for 17 | # https://github.com/mtreinish/stestr/issues/145 18 | cliff<3.0.0 19 | 20 | coverage>=4.5.2 21 | pyudev # for ceph-* charm unit tests (need to fix the ceph-* charm unit tests/mocking) 22 | git+https://github.com/openstack-charmers/zaza.git#egg=zaza 23 | git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack 24 | 25 | # Needed for charm-glance: 26 | git+https://opendev.org/openstack/tempest.git#egg=tempest 27 | 28 | croniter # needed for charm-rabbitmq-server unit tests 29 | psutil 30 | -------------------------------------------------------------------------------- /tests/bundles/noble-caracal.yaml: -------------------------------------------------------------------------------- 1 | variables: 2 | openstack-origin: &openstack-origin distro 3 | 4 | series: noble 5 | 6 | comment: 7 | - 'machines section to decide order of deployment. database sooner = faster' 8 | machines: 9 | '0': 10 | constraints: mem=3072M 11 | '1': 12 | constraints: mem=3072M 13 | '2': 14 | constraints: mem=3072M 15 | '3': 16 | '4': 17 | '5': 18 | '6': 19 | 20 | applications: 21 | 22 | keystone-mysql-router: 23 | charm: ch:mysql-router 24 | channel: latest/edge 25 | glance-mysql-router: 26 | charm: ch:mysql-router 27 | channel: latest/edge 28 | 29 | mysql-innodb-cluster: 30 | charm: ch:mysql-innodb-cluster 31 | num_units: 3 32 | options: 33 | source: *openstack-origin 34 | to: 35 | - '0' 36 | - '1' 37 | - '2' 38 | channel: latest/edge 39 | 40 | keystone: 41 | expose: True 42 | charm: ch:keystone 43 | num_units: 1 44 | options: 45 | openstack-origin: *openstack-origin 46 | to: 47 | - '3' 48 | channel: latest/edge 49 | 50 | swift-proxy: 51 | charm: ../../swift-proxy.charm 52 | num_units: 1 53 | options: 54 | zone-assignment: manual 55 | replicas: 1 56 | swift-hash: fdfef9d4-8b06-11e2-8ac0-531c923c8fae 57 | openstack-origin: *openstack-origin 58 | to: 59 | - '4' 60 | 61 | glance: 62 | expose: True 63 | charm: ch:glance 64 | num_units: 1 65 | options: 66 | openstack-origin: *openstack-origin 67 | to: 68 | - '5' 69 | channel: latest/edge 70 | 71 | swift-storage: 72 | charm: ch:swift-storage 73 | num_units: 1 74 | storage: 75 | block-devices: 'cinder,2G,2' 76 | options: 77 | zone: 1 78 | openstack-origin: *openstack-origin 79 | to: 80 | - '6' 81 | channel: latest/edge 82 | 83 | relations: 84 | 85 | - - 'keystone:shared-db' 86 | - 'keystone-mysql-router:shared-db' 87 | - - 'keystone-mysql-router:db-router' 88 | - 'mysql-innodb-cluster:db-router' 89 | 90 | - - 'glance:shared-db' 91 | - 'glance-mysql-router:shared-db' 92 | - - 'glance-mysql-router:db-router' 93 | - 'mysql-innodb-cluster:db-router' 94 | 95 | - - 'glance:identity-service' 96 | - 'keystone:identity-service' 97 | 98 | - - 'swift-proxy:identity-service' 99 | - 'keystone:identity-service' 100 | 101 | - - 'swift-storage:swift-storage' 102 | - 'swift-proxy:swift-storage' 103 | 104 | - - 'glance:object-store' 105 | - 'swift-proxy:object-store' 106 | -------------------------------------------------------------------------------- /tests/bundles/overlays/noble-caracal-gr-r1.yaml.j2: -------------------------------------------------------------------------------- 1 | applications: 2 | keystone: 3 | offers: 4 | keystone: 5 | endpoints: 6 | - identity-service 7 | swift-proxy-region1: 8 | charm: {{ charm_location }} 9 | offers: 10 | swift-proxy-region1: 11 | endpoints: 12 | - swift-storage 13 | - rings-distributor 14 | swift-storage-region1-zone1: 15 | offers: 16 | swift-storage-region1-zone1: 17 | endpoints: 18 | - swift-storage 19 | swift-storage-region1-zone2: 20 | offers: 21 | swift-storage-region1-zone2: 22 | endpoints: 23 | - swift-storage 24 | swift-storage-region1-zone3: 25 | offers: 26 | swift-storage-region1-zone3: 27 | endpoints: 28 | - swift-storage 29 | -------------------------------------------------------------------------------- /tests/bundles/overlays/noble-caracal-gr-r2.yaml.j2: -------------------------------------------------------------------------------- 1 | applications: 2 | swift-proxy-region2: 3 | charm: {{ charm_location }} 4 | relations: 5 | - - swift-proxy-region2:identity-service 6 | - keystone:identity-service 7 | - - swift-proxy-region2:swift-storage 8 | - swift-storage-region1-zone1:swift-storage 9 | - - swift-proxy-region2:swift-storage 10 | - swift-storage-region1-zone2:swift-storage 11 | - - swift-proxy-region2:swift-storage 12 | - swift-storage-region1-zone3:swift-storage 13 | - - swift-storage-region2-zone1:swift-storage 14 | - swift-proxy-region1:swift-storage 15 | - - swift-storage-region2-zone2:swift-storage 16 | - swift-proxy-region1:swift-storage 17 | - - swift-storage-region2-zone3:swift-storage 18 | - swift-proxy-region1:swift-storage 19 | - - swift-proxy-region2:rings-consumer 20 | - swift-proxy-region1:rings-distributor 21 | saas: 22 | keystone: 23 | url: admin/{{ swift_gr_region1 }}.keystone 24 | swift-proxy-region1: 25 | url: admin/{{ swift_gr_region1 }}.swift-proxy-region1 26 | swift-storage-region1-zone1: 27 | url: admin/{{ swift_gr_region1 }}.swift-storage-region1-zone1 28 | swift-storage-region1-zone2: 29 | url: admin/{{ swift_gr_region1 }}.swift-storage-region1-zone2 30 | swift-storage-region1-zone3: 31 | url: admin/{{ swift_gr_region1 }}.swift-storage-region1-zone3 32 | -------------------------------------------------------------------------------- /tests/tests.yaml: -------------------------------------------------------------------------------- 1 | charm_name: swift-proxy 2 | 3 | gate_bundles: 4 | - noble-caracal 5 | dev_bundles: 6 | - noble-caracal 7 | smoke_bundles: 8 | # Use no s3api test for smoke 9 | - noble-caracal 10 | configure: 11 | - swift_gr_region1: 12 | - zaza.openstack.charm_tests.swift.setup.wait_for_region2 13 | 14 | tests: 15 | - zaza.openstack.charm_tests.swift.tests.SwiftImageCreateTest 16 | - zaza.openstack.charm_tests.swift.tests.SwiftProxyTests 17 | - test-s3api: 18 | - zaza.openstack.charm_tests.swift.tests.SwiftImageCreateTest 19 | - zaza.openstack.charm_tests.swift.tests.SwiftProxyTests 20 | - zaza.openstack.charm_tests.swift.tests.S3APITest 21 | - swift_gr_region1: 22 | - zaza.openstack.charm_tests.swift.tests.SwiftGlobalReplicationTests 23 | - zaza.openstack.charm_tests.swift.tests.SwiftProxyMultiZoneTests 24 | 25 | tests_options: 26 | policyd: 27 | - service: swift 28 | -------------------------------------------------------------------------------- /unit_tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | 18 | from unittest.mock import patch 19 | 20 | 21 | _path = os.path.dirname(os.path.realpath(__file__)) 22 | _parent = os.path.abspath(os.path.join(_path, '..')) 23 | 24 | 25 | def _add_path(path): 26 | if path not in sys.path: 27 | sys.path.insert(1, path) 28 | 29 | 30 | _add_path(_parent) 31 | 32 | # Patch out lsb_release() and get_platform() as unit tests should be fully 33 | # insulated from the underlying platform. Unit tests assume that the system is 34 | # ubuntu jammy. 35 | patch( 36 | 'charmhelpers.osplatform.get_platform', return_value='ubuntu' 37 | ).start() 38 | patch( 39 | 'charmhelpers.core.host.lsb_release', 40 | return_value={ 41 | 'DISTRIB_CODENAME': 'jammy' 42 | }).start() 43 | -------------------------------------------------------------------------------- /unit_tests/test_actions_openstack_upgrade.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import sys 17 | 18 | import unittest 19 | 20 | from unittest.mock import patch, MagicMock 21 | 22 | os.environ['JUJU_UNIT_NAME'] = 'swift-proxy' 23 | 24 | # python-apt is not installed as part of test-requirements but is imported by 25 | # some charmhelpers modules so create a fake import. 26 | sys.modules['apt'] = MagicMock() 27 | sys.modules['apt_pkg'] = MagicMock() 28 | 29 | with patch('charmhelpers.contrib.hardening.harden.harden') as mock_dec, \ 30 | patch('lib.swift_utils.register_configs') as configs: 31 | mock_dec.side_effect = (lambda *dargs, **dkwargs: lambda f: 32 | lambda *args, **kwargs: f(*args, **kwargs)) 33 | import actions.openstack_upgrade as openstack_upgrade 34 | 35 | 36 | TO_PATCH = [ 37 | 'config_changed', 38 | 'do_openstack_upgrade', 39 | ] 40 | 41 | 42 | class CharmTestCase(unittest.TestCase): 43 | 44 | def setUp(self, obj, patches): 45 | super(CharmTestCase, self).setUp() 46 | self.patches = patches 47 | self.obj = obj 48 | self.patch_all() 49 | 50 | def patch(self, method): 51 | _m = patch.object(self.obj, method) 52 | mocked = _m.start() 53 | self.addCleanup(_m.stop) 54 | return mocked 55 | 56 | def patch_all(self): 57 | for method in self.patches: 58 | setattr(self, method, self.patch(method)) 59 | 60 | 61 | class TestSwiftUpgradeActions(CharmTestCase): 62 | 63 | def setUp(self): 64 | super(TestSwiftUpgradeActions, self).setUp(openstack_upgrade, 65 | TO_PATCH) 66 | 67 | @patch('charmhelpers.contrib.openstack.utils.config') 68 | @patch('charmhelpers.contrib.openstack.utils.action_set') 69 | @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') 70 | def test_openstack_upgrade_true(self, upgrade_avail, 71 | action_set, config): 72 | upgrade_avail.return_value = True 73 | config.return_value = True 74 | 75 | openstack_upgrade.openstack_upgrade() 76 | 77 | self.assertTrue(self.do_openstack_upgrade.called) 78 | self.assertTrue(self.config_changed.called) 79 | 80 | @patch('charmhelpers.contrib.openstack.utils.config') 81 | @patch('charmhelpers.contrib.openstack.utils.action_set') 82 | @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') 83 | def test_openstack_upgrade_false(self, upgrade_avail, 84 | action_set, config): 85 | upgrade_avail.return_value = True 86 | config.return_value = False 87 | 88 | openstack_upgrade.openstack_upgrade() 89 | 90 | self.assertFalse(self.do_openstack_upgrade.called) 91 | self.assertFalse(self.config_changed.called) 92 | -------------------------------------------------------------------------------- /unit_tests/test_templates.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Canonical Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from unittest import mock 16 | import unittest 17 | 18 | from jinja2 import Environment 19 | 20 | from charmhelpers.contrib.openstack.templating import get_loader 21 | 22 | 23 | class ProxyServerTemplateTestCase(unittest.TestCase): 24 | 25 | @mock.patch('charmhelpers.contrib.openstack.templating.log') 26 | def get_template_for_release(self, os_release, mock_log): 27 | loader = get_loader('./templates', os_release) 28 | env = Environment(loader=loader) 29 | 30 | return env.get_template('proxy-server.conf') 31 | 32 | def test_statsd_config_for_all_releases(self): 33 | """The configs contain statsd settings if statsd-host is set.""" 34 | for release in ('mitaka'): 35 | template = self.get_template_for_release(release) 36 | 37 | result = template.render(statsd_host='127.0.0.1') 38 | 39 | self.assertTrue("log_statsd_host" in result) 40 | self.assertTrue("log_statsd_port" in result) 41 | self.assertTrue("log_statsd_default_sample_rate" in result) 42 | 43 | result = template.render() 44 | 45 | self.assertFalse("log_statsd_host" in result) 46 | self.assertFalse("log_statsd_port" in result) 47 | self.assertFalse("log_statsd_default_sample_rate" in result) 48 | 49 | def test_keystonemiddleware_config_for_newer_release(self): 50 | """Test template should follow newer keystone middleware config.""" 51 | release = 'yoga' 52 | template = self.get_template_for_release(release) 53 | result = template.render(auth_type='keystone') 54 | self.assertFalse('signing_dir' in result) 55 | self.assertFalse('hash_algorithms' in result) 56 | 57 | self.assertTrue('filter:authtoken' in result) 58 | self.assertTrue('www_authenticate_uri' in result) 59 | self.assertTrue('auth_url' in result) 60 | --------------------------------------------------------------------------------