├── .gitignore ├── .gitreview ├── .project ├── .pydevproject ├── .stestr.conf ├── .zuul.yaml ├── LICENSE ├── Makefile ├── README.md ├── actions.yaml ├── actions ├── actions.py ├── openstack-upgrade ├── openstack_upgrade.py ├── pause ├── resume ├── security-checklist └── security_checklist.py ├── bindep.txt ├── charm-helpers-hooks.yaml ├── charmcraft.yaml ├── config.yaml ├── copyright ├── files ├── midokura.key └── midonet.key ├── hardening.yaml ├── hooks ├── amqp-relation-broken ├── amqp-relation-changed ├── amqp-relation-departed ├── amqp-relation-joined ├── certificates-relation-changed ├── certificates-relation-departed ├── certificates-relation-joined ├── 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 │ │ │ └── ovs │ │ │ │ ├── __init__.py │ │ │ │ ├── ovn.py │ │ │ │ ├── ovsdb.py │ │ │ │ └── utils.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 │ │ ├── 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 │ │ ├── archive.py │ │ └── execd.py ├── cluster-relation-changed ├── cluster-relation-departed ├── cluster-relation-joined ├── config-changed ├── etcd-proxy-relation-broken ├── etcd-proxy-relation-changed ├── etcd-proxy-relation-departed ├── etcd-proxy-relation-joined ├── external-dns-relation-broken ├── external-dns-relation-changed ├── external-dns-relation-departed ├── external-dns-relation-joined ├── ha-relation-changed ├── ha-relation-joined ├── identity-service-relation-broken ├── identity-service-relation-changed ├── identity-service-relation-joined ├── infoblox-neutron-relation-broken ├── infoblox-neutron-relation-changed ├── infoblox-neutron-relation-departed ├── install ├── midonet-relation-broken ├── midonet-relation-changed ├── midonet-relation-joined ├── neutron-api-relation-broken ├── neutron-api-relation-changed ├── neutron-api-relation-joined ├── neutron-load-balancer-relation-changed ├── neutron-load-balancer-relation-joined ├── neutron-plugin-api-relation-joined ├── neutron-plugin-api-subordinate-relation-changed ├── neutron-plugin-api-subordinate-relation-departed ├── neutron-plugin-api-subordinate-relation-joined ├── neutron_api_context.py ├── neutron_api_hooks.py ├── neutron_api_utils.py ├── nrpe-external-master-relation-changed ├── nrpe-external-master-relation-joined ├── post-series-upgrade ├── pre-series-upgrade ├── shared-db-relation-broken ├── shared-db-relation-changed ├── shared-db-relation-joined ├── start ├── stop ├── update-status ├── upgrade-charm ├── vsd-rest-api-relation-changed └── vsd-rest-api-relation-joined ├── icon.svg ├── lib └── .keep ├── metadata.yaml ├── osci.yaml ├── rename.sh ├── requirements.txt ├── setup.cfg ├── templates ├── api-paste.ini ├── etcd ├── icehouse │ ├── etcd.conf │ ├── ml2_conf.ini │ ├── neutron-server │ ├── neutron.conf │ ├── nsx.ini │ ├── nuage_plugin.ini │ └── plumgrid.ini ├── juno │ ├── midonet.ini │ └── neutron.conf ├── kilo │ ├── api-paste.ini │ ├── ml2_conf.ini │ ├── ml2_conf_sriov.ini │ ├── neutron.conf │ └── nuage_plugin.ini ├── liberty │ ├── neutron.conf │ ├── neutron_lbaas.conf │ └── neutron_vpnaas.conf ├── mitaka │ ├── api-paste.ini │ ├── ml2_conf.ini │ └── neutron.conf ├── newton │ ├── api-paste.ini │ └── neutron.conf ├── ocata │ └── neutron.conf ├── parts │ ├── database │ ├── rabbitmq │ ├── section-database │ ├── section-designate │ ├── section-infoblox │ ├── section-nova │ └── section-placement ├── pike │ └── neutron.conf ├── ports.conf ├── queens │ ├── ml2_conf.ini │ └── neutron.conf ├── rocky │ ├── api-paste.ini │ └── neutron.conf ├── train │ └── neutron.conf ├── ussuri │ └── neutron.conf └── yoga │ ├── api-paste.ini │ ├── api_audit_map.conf │ └── neutron.conf ├── test-requirements.txt ├── tests ├── bundles │ └── noble-caracal.yaml └── tests.yaml ├── tox.ini └── unit_tests ├── __init__.py ├── test_actions.py ├── test_actions_openstack_upgrade.py ├── test_neutron_api_context.py ├── test_neutron_api_hooks.py ├── test_neutron_api_utils.py └── test_utils.py /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | .coverage 3 | .testrepository 4 | .tox 5 | tags 6 | *.sw[nop] 7 | *.pyc 8 | .unit-state.db 9 | trusty/ 10 | xenial/ 11 | .stestr 12 | __pycache__ 13 | func-results.json 14 | .idea 15 | *.charm 16 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/charm-neutron-api.git 5 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | neutron-api 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 | /neutron-api/hooks 7 | /neutron-api/unit_tests 8 | 9 | 10 | -------------------------------------------------------------------------------- /.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 | #!/usr/bin/make 2 | PYTHON := /usr/bin/env python3 3 | 4 | lint: 5 | @tox -e pep8 6 | 7 | test: 8 | @echo Starting unit tests... 9 | @tox -e py27 10 | 11 | functional_test: 12 | @echo Starting Amulet tests... 13 | @tox -e func27 14 | 15 | bin/charm_helpers_sync.py: 16 | @mkdir -p bin 17 | @curl -o bin/charm_helpers_sync.py https://raw.githubusercontent.com/juju/charm-helpers/master/tools/charm_helpers_sync/charm_helpers_sync.py 18 | 19 | 20 | sync: bin/charm_helpers_sync.py 21 | @$(PYTHON) bin/charm_helpers_sync.py -c charm-helpers-hooks.yaml 22 | 23 | publish: lint test 24 | bzr push lp:charms/neutron-api 25 | bzr push lp:charms/trusty/neutron-api 26 | -------------------------------------------------------------------------------- /actions.yaml: -------------------------------------------------------------------------------- 1 | openstack-upgrade: 2 | description: Perform openstack upgrades. Config option action-managed-upgrade must be set to True. 3 | pause: 4 | description: | 5 | Pause neutron-api services. 6 | If the neutron-api deployment is clustered using the hacluster charm, the 7 | corresponding hacluster unit on the node must first be paused as well. 8 | Not doing so may lead to an interruption of service. 9 | resume: 10 | description: | 11 | Resume neutron-api services. 12 | If the neutron-api deployment is clustered using the hacluster charm, the 13 | corresponding hacluster unit on the node must be resumed as well. 14 | security-checklist: 15 | description: Validate the running configuration against the OpenStack security guides checklist 16 | -------------------------------------------------------------------------------- /actions/actions.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 | sys.path.append('hooks/') 21 | 22 | from charmhelpers.core.hookenv import action_fail 23 | from neutron_api_utils import ( 24 | pause_unit_helper, 25 | resume_unit_helper, 26 | register_configs, 27 | ) 28 | 29 | 30 | def pause(args): 31 | """Pause the Ceilometer services. 32 | @raises Exception should the service fail to stop. 33 | """ 34 | pause_unit_helper(register_configs()) 35 | 36 | 37 | def resume(args): 38 | """Resume the Ceilometer services. 39 | @raises Exception should the service fail to start.""" 40 | resume_unit_helper(register_configs()) 41 | 42 | 43 | # A dictionary of all the defined actions to callables (which take 44 | # parsed arguments). 45 | ACTIONS = {"pause": pause, "resume": resume} 46 | 47 | 48 | def main(args): 49 | action_name = os.path.basename(args[0]) 50 | try: 51 | action = ACTIONS[action_name] 52 | except KeyError: 53 | return "Action %s undefined" % action_name 54 | else: 55 | try: 56 | action(args) 57 | except Exception as e: 58 | action_fail(str(e)) 59 | 60 | 61 | if __name__ == "__main__": 62 | sys.exit(main(sys.argv)) 63 | -------------------------------------------------------------------------------- /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 | 17 | import sys 18 | 19 | sys.path.append('hooks/') 20 | 21 | from charmhelpers.contrib.openstack.utils import ( 22 | do_action_openstack_upgrade, 23 | ) 24 | 25 | from neutron_api_hooks import ( 26 | config_changed, 27 | CONFIGS, 28 | ) 29 | 30 | from neutron_api_utils import ( 31 | do_openstack_upgrade, 32 | ) 33 | 34 | 35 | def openstack_upgrade(): 36 | """Upgrade packages to config-set Openstack version. 37 | 38 | If the charm was installed from source we cannot upgrade it. 39 | For backwards compatibility a config flag must be set for this 40 | code to run, otherwise a full service level upgrade will fire 41 | on config-changed.""" 42 | 43 | if (do_action_openstack_upgrade('neutron-common', 44 | do_openstack_upgrade, 45 | CONFIGS)): 46 | config_changed() 47 | 48 | 49 | if __name__ == '__main__': 50 | openstack_upgrade() 51 | -------------------------------------------------------------------------------- /actions/pause: -------------------------------------------------------------------------------- 1 | actions.py -------------------------------------------------------------------------------- /actions/resume: -------------------------------------------------------------------------------- 1 | actions.py -------------------------------------------------------------------------------- /actions/security-checklist: -------------------------------------------------------------------------------- 1 | security_checklist.py -------------------------------------------------------------------------------- /actions/security_checklist.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # 3 | # Copyright 2019 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 configparser 18 | import sys 19 | 20 | sys.path.append('hooks') 21 | 22 | import charmhelpers.contrib.openstack.audits as audits 23 | from charmhelpers.contrib.openstack.audits import ( 24 | openstack_security_guide, 25 | ) 26 | 27 | 28 | # Via the openstack_security_guide above, we are running the following 29 | # security assertions automatically: 30 | # 31 | # - Check-Neutron-01 - validate-file-ownership 32 | # - Check-Neutron-02 - validate-file-permissions 33 | # - Check-Neutron-03 - validate-uses-keystone 34 | # - Check-Neutron-04 - validate-uses-tls-for-keystone 35 | 36 | @audits.audit(audits.is_audit_type(audits.AuditType.OpenStackSecurityGuide)) 37 | def validate_enables_tls(audit_options): 38 | """Verify that TLS is enabled on Neutron. 39 | 40 | Security Guide Check Name: Check-Neutron-05 41 | 42 | :param audit_options: Dictionary of options for audit configuration 43 | :type audit_options: Dict 44 | :raises: AssertionError if the assertion fails. 45 | """ 46 | section = audit_options['neutron_config']['DEFAULT'] 47 | assert section.get('use_ssl') == "True", \ 48 | "SSL should be enabled on neutron-api" 49 | 50 | 51 | def main(): 52 | config = { 53 | 'config_path': '/etc/neutron', 54 | 'config_file': 'neutron.conf', 55 | 'audit_type': audits.AuditType.OpenStackSecurityGuide, 56 | 'files': openstack_security_guide.FILE_ASSERTIONS['neutron-api'], 57 | 'excludes': [ 58 | 'validate-uses-tls-for-glance', 59 | ], 60 | } 61 | conf = configparser.ConfigParser(strict=False) 62 | conf.read("/etc/neutron/neutron.conf") 63 | config['neutron_config'] = dict(conf) 64 | return audits.action_parse_results(audits.run(config)) 65 | 66 | 67 | if __name__ == "__main__": 68 | sys.exit(main()) 69 | -------------------------------------------------------------------------------- /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: hooks/charmhelpers 3 | include: 4 | - core 5 | - osplatform 6 | - cli 7 | - fetch 8 | - contrib.openstack|inc=* 9 | - contrib.hardware.pci 10 | - contrib.hahelpers 11 | - contrib.network.ovs 12 | - contrib.storage.linux 13 | - payload.execd 14 | - payload.archive 15 | - contrib.network.ip 16 | - contrib.python 17 | - contrib.charmsupport 18 | - contrib.hardening|inc=* 19 | - contrib.openstack.policyd 20 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /copyright: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0 2 | 3 | Files: * 4 | Copyright: 2012, Canonical Ltd. 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/midokura.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: SKS 1.1.5 3 | Comment: Hostname: keyserver.ubuntu.com 4 | 5 | mI0ETb6aOgEEAMVw8Vnwk+zpDtsc0gSW10JEe48zKr2vpl9tQgWAFOPgOA1NglYMw/xT6Rns 6 | 7CrYxPR0cb3DeMFtFdMkfWXO0R6x4yHrozMDY/DpvwgYQclIIbcYYe0p83nlBp793D2dSq60 7 | HWuXJu3oi0wQQuR0/jTmOnjxzCzu5jKdJeXihl95ABEBAAG0Jk1pZG9rdXJhIChNaWRva3Vy 8 | YSkgPGluZm9AbWlkb2t1cmEuanA+iLgEEwECACIFAk2+mjoCGwMGCwkIBwMCBhUIAgkKCwQW 9 | AgMBAh4BAheAAAoJEGezjToFQxTNAp0D/2c+PLnRFzEXCztXT+05xoO1mPzpm3x2p5ecVPGH 10 | R8IxhozlN9DDGDdnvNfMOhi6nv/G2l86+9Fj8Dz01ne0RZzZHSS1DF/zb6dMYrPJqiT1DXKH 11 | 0Y73OL/+M7rsutEq0B/DKhjdBfFPutk3gerEUZPNfIhScE3tnwCnVGJKPQbFuI0ETb6aOgEE 12 | ANLJK3gmXrsp1VKnt663RoxZgoFQgQ6wHaZZWhULTteafjoThX9tj7FidR2+7qJLwpa57M9d 13 | rib4OlbW+rE4PW199/Uqfy86gLv76Q2GZMpzaYB1ZZow0Ny1RTCwh7apkhR/8fCUpq37aODQ 14 | 4YwBpZC54iXVKfcntpdJFoObIqXtABEBAAGInwQYAQIACQUCTb6aOgIbDAAKCRBns406BUMU 15 | zfzOBACKx4jChKTAl6HfldOxVN7o8DQpd5rgkHIEj062ym4Zq5t2v3oaz0H0P2WV66MAhOuj 16 | gX0V1duZi8fKHdIsdk0nvEa/mV0QS6pEAeZh+dbLkKyu1J4MSi5l+L+te5XjYBGpoRa3ZGrI 17 | R3CkA0oQDCOh312SrcH6Tn9RBPChVSigzg== 18 | =zF5K 19 | -----END PGP PUBLIC KEY BLOCK----- 20 | -------------------------------------------------------------------------------- /files/midonet.key: -------------------------------------------------------------------------------- 1 | -----BEGIN PGP PUBLIC KEY BLOCK----- 2 | Version: SKS 1.1.5 3 | Comment: Hostname: keyserver.ubuntu.com 4 | 5 | mQGiBFRF760RBADwIz8rK0K1qiXbq0/cda42oKZW+jvcqUIDsNeKYSWI5YRfS4NX+jRJM/rL 6 | PzQD+JZtLeFHeLK0qxpr5b2acxleOgNfw3hzxz4DNQsVbkts3n1s7YT80DwNdYPBbFCAOOkJ 7 | jH7CL2PN4fTs5eP/0QpMOZT+s5DTqozPDgTYWhhVrwCgoc5H2zYVW86Ok3c0fOru9VZPJxsE 8 | AIexw3sBv9FZyvVr6rehiVlzOtwfHwSEQZQ511so46H5GdLqQFwThKmWVvxyDAGshtpjLJbs 9 | KHilEJVkfzhs/xyCjwAPl8VbIYgUy/FlhKtFWWxkQLlUwuC5iOPLdzhoL1PEzZuCYmMkSr0E 10 | XASN83nbzIIqdQgvEZrAUGUzTf68A/9eSiry88vEIIGL7hT2rw/NockwCGsc2xrjqrfs9PpU 11 | rg7ksTPua1UQUkMeWoKEoYpGppGd5XXEvo6287i8eAB4B9qr4AGbz8NKmVI2lC9V641Ecu3/ 12 | kmhaGQRBmCZgcW3B987rTyY5lsLc66+tNai6L72y1fj5nBtY2MVYg6ixZrRNTWlkb2t1cmEg 13 | UlBNICYgREVCIFJlcG9zaXRvcnkgQXV0b21hdGljIFNpZ25pbmcgS2V5ICgyMDE0KSA8b3Bz 14 | QG1pZG9rdXJhLmNvbT6IYgQTEQIAIgUCVEXvrQIbAwYLCQgHAwIGFQgCCQoLBBYCAwECHgEC 15 | F4AACgkQ/ONA0lDxj8+EZgCdEqXQrZ8PiemTgQK7044Deed5OpsAnjL8Cw4GnGJZHmW24pKQ 16 | IkzTvb1SuQENBFRF760QBADJD9aLzLYAehNITLlrNao+bZsSAAeKRYWjtyf6Z620/WiQw6a+ 17 | 01/ZwrthvXNUcnIVIKKokPVpXR29AUV0T7npDMTkQCXfw24X+9NedBCquJpHnBS/YySHP+EJ 18 | vNeLc0xdwnOcGehe7IeaDN0eEWx+HVhEfC/lXa/Pebs4OpIo/wADBwQAwyCTQ7Vod3jO7JFP 19 | 6R6mS5gX3Sx6s3ocE4laDFBmh9d8GPWpdzHwzPzrVaIw/xSaxP2zXeWFcDoY3zT7PH3dm+xj 20 | cEG23iTqvmMAr7EXirKu9pL/DKuBPncNMTS6JoltGGVzZLoJF2OJZX81c+U1mGyLpWg/hK3K 21 | t9VpzNjFGc2ISQQYEQIACQUCVEXvrQIbDAAKCRD840DSUPGPz0PiAJ9QXGyMCTymuuCdYyEX 22 | 3D94y0Z8FgCaAwYNRahgGw/HN04xkqfrN7bXajE= 23 | =hLRg 24 | -----END PGP PUBLIC KEY BLOCK----- 25 | -------------------------------------------------------------------------------- /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/amqp-relation-broken: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/amqp-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/amqp-relation-departed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/amqp-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/certificates-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/certificates-relation-departed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/certificates-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/hardening/apache/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-neutron-api/6d5021e58d06e8fc112000b9e9867b4a898222fb/hooks/charmhelpers/contrib/hardening/apache/templates/__init__.py -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/hardening/defaults/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-neutron-api/6d5021e58d06e8fc112000b9e9867b4a898222fb/hooks/charmhelpers/contrib/hardening/defaults/__init__.py -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 %} -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/hardening/host/templates/99-hardening.sh: -------------------------------------------------------------------------------- 1 | TMOUT={{ tmout }} 2 | readonly TMOUT 3 | export TMOUT 4 | 5 | readonly HISTFILE 6 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/hardening/host/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-neutron-api/6d5021e58d06e8fc112000b9e9867b4a898222fb/hooks/charmhelpers/contrib/hardening/host/templates/__init__.py -------------------------------------------------------------------------------- /hooks/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 -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/hardening/mysql/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-neutron-api/6d5021e58d06e8fc112000b9e9867b4a898222fb/hooks/charmhelpers/contrib/hardening/mysql/templates/__init__.py -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/hardening/ssh/templates/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-neutron-api/6d5021e58d06e8fc112000b9e9867b4a898222fb/hooks/charmhelpers/contrib/hardening/ssh/templates/__init__.py -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/network/ovs/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 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 | import subprocess 15 | 16 | 17 | def _run(*args): 18 | """Run a process, check result, capture decoded output from STDOUT. 19 | 20 | :param args: Command and arguments to run 21 | :type args: Tuple[str, ...] 22 | :returns: Information about the completed process 23 | :rtype: str 24 | :raises subprocess.CalledProcessError 25 | """ 26 | return subprocess.check_output(args, universal_newlines=True) 27 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/openstack/templates/section-audit-middleware-notifications: -------------------------------------------------------------------------------- 1 | {% if audit_middleware -%} 2 | [audit_middleware_notifications] 3 | driver = log 4 | {% endif -%} -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 -%} -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/openstack/templates/section-oslo-middleware: -------------------------------------------------------------------------------- 1 | [oslo_middleware] 2 | 3 | # Bug #1758675 4 | enable_proxy_headers_parsing = true 5 | 6 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/contrib/openstack/templates/vendor_data.json: -------------------------------------------------------------------------------- 1 | {{ vendor_data_json }} -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/core/host_factory/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-neutron-api/6d5021e58d06e8fc112000b9e9867b4a898222fb/hooks/charmhelpers/core/host_factory/__init__.py -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/core/kernel_factory/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/charm-neutron-api/6d5021e58d06e8fc112000b9e9867b4a898222fb/hooks/charmhelpers/core/kernel_factory/__init__.py -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/charmhelpers/payload/archive.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 tarfile 17 | import zipfile 18 | from charmhelpers.core import ( 19 | host, 20 | hookenv, 21 | ) 22 | 23 | 24 | class ArchiveError(Exception): 25 | pass 26 | 27 | 28 | def get_archive_handler(archive_name): 29 | if os.path.isfile(archive_name): 30 | if tarfile.is_tarfile(archive_name): 31 | return extract_tarfile 32 | elif zipfile.is_zipfile(archive_name): 33 | return extract_zipfile 34 | else: 35 | # look at the file name 36 | for ext in ('.tar', '.tar.gz', '.tgz', 'tar.bz2', '.tbz2', '.tbz'): 37 | if archive_name.endswith(ext): 38 | return extract_tarfile 39 | for ext in ('.zip', '.jar'): 40 | if archive_name.endswith(ext): 41 | return extract_zipfile 42 | 43 | 44 | def archive_dest_default(archive_name): 45 | archive_file = os.path.basename(archive_name) 46 | return os.path.join(hookenv.charm_dir(), "archives", archive_file) 47 | 48 | 49 | def extract(archive_name, destpath=None): 50 | handler = get_archive_handler(archive_name) 51 | if handler: 52 | if not destpath: 53 | destpath = archive_dest_default(archive_name) 54 | if not os.path.isdir(destpath): 55 | host.mkdir(destpath) 56 | handler(archive_name, destpath) 57 | return destpath 58 | else: 59 | raise ArchiveError("No handler for archive") 60 | 61 | 62 | def extract_tarfile(archive_name, destpath): 63 | "Unpack a tar archive, optionally compressed" 64 | archive = tarfile.open(archive_name) 65 | archive.extractall(destpath) 66 | 67 | 68 | def extract_zipfile(archive_name, destpath): 69 | "Unpack a zip file" 70 | archive = zipfile.ZipFile(archive_name) 71 | archive.extractall(destpath) 72 | -------------------------------------------------------------------------------- /hooks/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 | -------------------------------------------------------------------------------- /hooks/cluster-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/cluster-relation-departed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/cluster-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/config-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/etcd-proxy-relation-broken: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/etcd-proxy-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/etcd-proxy-relation-departed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/etcd-proxy-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/external-dns-relation-broken: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/external-dns-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/external-dns-relation-departed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/external-dns-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/ha-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/ha-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/identity-service-relation-broken: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/identity-service-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/identity-service-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/infoblox-neutron-relation-broken: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/infoblox-neutron-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/infoblox-neutron-relation-departed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/install: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/midonet-relation-broken: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/midonet-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/midonet-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/neutron-api-relation-broken: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/neutron-api-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/neutron-api-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/neutron-load-balancer-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/neutron-load-balancer-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/neutron-plugin-api-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/neutron-plugin-api-subordinate-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/neutron-plugin-api-subordinate-relation-departed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/neutron-plugin-api-subordinate-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/nrpe-external-master-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/nrpe-external-master-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/post-series-upgrade: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/pre-series-upgrade: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/shared-db-relation-broken: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/shared-db-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/shared-db-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/start: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/stop: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/update-status: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/upgrade-charm: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/vsd-rest-api-relation-changed: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /hooks/vsd-rest-api-relation-joined: -------------------------------------------------------------------------------- 1 | neutron_api_hooks.py -------------------------------------------------------------------------------- /lib/.keep: -------------------------------------------------------------------------------- 1 | This file was created by release-tools to ensure that this empty 2 | directory is preserved in vcs re: lint check definitions in global 3 | tox.ini files. This file can be removed if/when this dir is actually in use. 4 | -------------------------------------------------------------------------------- /metadata.yaml: -------------------------------------------------------------------------------- 1 | name: neutron-api 2 | summary: OpenStack Networking API service 3 | maintainer: OpenStack Charmers 4 | description: | 5 | Neutron is a virtual network service for OpenStack, and a part of 6 | Netstack. Just like OpenStack Nova provides an API to dynamically 7 | request and configure virtual servers, Neutron provides an API to 8 | dynamically request and configure virtual networks. These networks 9 | connect "interfaces" from other OpenStack services (e.g., virtual NICs 10 | from Nova VMs). The Neutron API supports extensions to provide 11 | advanced network capabilities (e.g., QoS, ACLs, network monitoring, 12 | etc.) 13 | . 14 | This charm provides the OpenStack Neutron API service. 15 | docs: https://discourse.charmhub.io/t/neutron-api-docs-index/10555 16 | tags: 17 | - openstack 18 | series: 19 | - jammy 20 | extra-bindings: 21 | public: 22 | admin: 23 | internal: 24 | provides: 25 | nrpe-external-master: 26 | interface: nrpe-external-master 27 | scope: container 28 | neutron-api: 29 | interface: neutron-api 30 | neutron-plugin-api: 31 | interface: neutron-plugin-api 32 | neutron-load-balancer: 33 | interface: neutron-load-balancer 34 | requires: 35 | shared-db: 36 | interface: mysql-shared 37 | amqp: 38 | interface: rabbitmq 39 | identity-service: 40 | interface: keystone 41 | ha: 42 | interface: hacluster 43 | scope: container 44 | vsd-rest-api: 45 | interface: vsd-rest-api 46 | neutron-plugin-api-subordinate: 47 | interface: neutron-plugin-api-subordinate 48 | scope: container 49 | etcd-proxy: 50 | interface: etcd-proxy 51 | midonet: 52 | interface: midonet 53 | external-dns: 54 | interface: designate 55 | infoblox-neutron: 56 | interface: infoblox 57 | scope: container 58 | certificates: 59 | interface: tls-certificates 60 | peers: 61 | cluster: 62 | interface: neutron-api-ha 63 | resources: 64 | policyd-override: 65 | type: file 66 | filename: policyd-override.zip 67 | description: The policy.d overrides file 68 | -------------------------------------------------------------------------------- /osci.yaml: -------------------------------------------------------------------------------- 1 | - project: 2 | templates: 3 | - charm-unit-jobs-py310 4 | - charm-functional-jobs 5 | vars: 6 | needs_charm_build: true 7 | charm_build_name: neutron-api 8 | build_type: charmcraft 9 | charmcraft_channel: 3.x/beta 10 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = charm-neutron-api 3 | summary = Charm module for OpenStack Neutron API 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=hooks 25 | -------------------------------------------------------------------------------- /templates/api-paste.ini: -------------------------------------------------------------------------------- 1 | [composite:neutron] 2 | use = egg:Paste#urlmap 3 | /: neutronversions 4 | /v2.0: neutronapi_v2_0 5 | 6 | [composite:neutronapi_v2_0] 7 | use = call:neutron.auth:pipeline_factory 8 | noauth = request_id catch_errors extensions neutronapiapp_v2_0 9 | keystone = {% for m in extra_middleware %}{{ m.name }} {% endfor %}request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0 10 | 11 | {% for m in extra_middleware -%} 12 | [{{ m.type }}:{{ m.name }}] 13 | {% for k, v in m.config.items() -%} 14 | {{ k }} = {{ v }} 15 | {% endfor %} 16 | {% endfor -%} 17 | 18 | [filter:request_id] 19 | paste.filter_factory = neutron.openstack.common.middleware.request_id:RequestIdMiddleware.factory 20 | 21 | [filter:catch_errors] 22 | paste.filter_factory = neutron.openstack.common.middleware.catch_errors:CatchErrorsMiddleware.factory 23 | 24 | [filter:keystonecontext] 25 | paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory 26 | 27 | [filter:authtoken] 28 | paste.filter_factory = keystoneclient.middleware.auth_token:filter_factory 29 | 30 | [filter:extensions] 31 | paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory 32 | 33 | [app:neutronversions] 34 | paste.app_factory = neutron.api.versions:Versions.factory 35 | 36 | [app:neutronapiapp_v2_0] 37 | paste.app_factory = neutron.api.v2.router:APIRouter.factory 38 | -------------------------------------------------------------------------------- /templates/etcd: -------------------------------------------------------------------------------- 1 | ETCD_PROXY=on 2 | ETCD_INITIAL_CLUSTER={{ cluster }} 3 | ETCD_DATA_DIR=/var/lib/etcd 4 | -------------------------------------------------------------------------------- /templates/icehouse/etcd.conf: -------------------------------------------------------------------------------- 1 | # managed by juju, DO NOT EDIT 2 | description "etcd" 3 | author "etcd maintainers" 4 | 5 | start on stopped rc RUNLEVEL=[2345] 6 | stop on runlevel [!2345] 7 | 8 | respawn 9 | 10 | setuid etcd 11 | 12 | script 13 | set -a 14 | . /etc/default/etcd 15 | exec /usr/bin/etcd 16 | end script 17 | -------------------------------------------------------------------------------- /templates/icehouse/ml2_conf.ini: -------------------------------------------------------------------------------- 1 | # icehouse 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | [ml2] 7 | {% if neutron_plugin == 'Calico' -%} 8 | type_drivers = local,flat 9 | mechanism_drivers = calico 10 | {% else -%} 11 | type_drivers = {{ tenant_network_types }} 12 | tenant_network_types = {{ tenant_network_types }} 13 | mechanism_drivers = openvswitch,hyperv,l2population 14 | 15 | [ml2_type_gre] 16 | tunnel_id_ranges = 1:1000 17 | 18 | [ml2_type_vxlan] 19 | vni_ranges = {{ vni_ranges }} 20 | 21 | [ml2_type_vlan] 22 | network_vlan_ranges = {{ vlan_ranges }} 23 | 24 | [ml2_type_flat] 25 | flat_networks = {{ network_providers }} 26 | 27 | [ovs] 28 | enable_tunneling = True 29 | local_ip = {{ local_ip }} 30 | 31 | [agent] 32 | tunnel_types = {{ overlay_network_type }} 33 | {% endif -%} 34 | 35 | [securitygroup] 36 | {% if neutron_security_groups -%} 37 | enable_security_group = True 38 | {% if neutron_plugin == 'Calico' -%} 39 | firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver 40 | {% else -%} 41 | firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver 42 | {% endif -%} 43 | {% else -%} 44 | enable_security_group = False 45 | {% endif -%} 46 | -------------------------------------------------------------------------------- /templates/icehouse/neutron-server: -------------------------------------------------------------------------------- 1 | # havana 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | NEUTRON_PLUGIN_CONFIG="{{ config }}" -------------------------------------------------------------------------------- /templates/icehouse/nsx.ini: -------------------------------------------------------------------------------- 1 | # icehouse 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | [DEFAULT] 7 | nsx_user = {{ nsx_username }} 8 | nsx_password = {{ nsx_password }} 9 | nsx_controllers = {{ nsx_controllers }} 10 | default_tz_uuid = {{ nsx_tz_uuid }} 11 | default_l3_gw_service_uuid = {{ nsx_l3_uuid }} 12 | -------------------------------------------------------------------------------- /templates/icehouse/nuage_plugin.ini: -------------------------------------------------------------------------------- 1 | # icehouse 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | [restproxy] 7 | server = {{ vsd_server }} 8 | serverauth = {{ vsd_auth }} 9 | auth_resource = {{ vsd_auth_resource }} 10 | organization = {{ vsd_organization }} 11 | serverssl = {{ vsd_auth_ssl }} 12 | base_uri = {{ vsd_base_uri }} 13 | default_net_partition_name = {{ vsd_netpart_name }} 14 | -------------------------------------------------------------------------------- /templates/icehouse/plumgrid.ini: -------------------------------------------------------------------------------- 1 | # icehouse 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | [plumgriddirector] 7 | # This line should be pointing to the PLUMgrid Director, 8 | # for the PLUMgrid platform. 9 | director_server={{ virtual_ip }} 10 | director_server_port=443 11 | # Authentification parameters for the Director. 12 | # These are the admin credentials to manage and control 13 | # the PLUMgrid Director server. 14 | username={{ pg_username }} 15 | password={{ pg_password }} 16 | servertimeout=70 17 | 18 | {% if database_host -%} 19 | connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} 20 | {% endif -%} 21 | -------------------------------------------------------------------------------- /templates/juno/midonet.ini: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # [ WARNING ] 3 | # Configuration file maintained by Juju. Local changes may be overwritten. 4 | ############################################################################### 5 | [MIDONET] 6 | # MidoNet API URL 7 | midonet_uri = http://{{ midonet_api_ip }}:{{ midonet_api_port }}/midonet-api 8 | # credentials 9 | username = {{ admin_user }} 10 | password = {{ admin_password }} 11 | project_id = {{ admin_tenant_name }} 12 | -------------------------------------------------------------------------------- /templates/kilo/api-paste.ini: -------------------------------------------------------------------------------- 1 | [composite:neutron] 2 | use = egg:Paste#urlmap 3 | /: neutronversions 4 | /v2.0: neutronapi_v2_0 5 | 6 | [composite:neutronapi_v2_0] 7 | use = call:neutron.auth:pipeline_factory 8 | noauth = request_id catch_errors extensions neutronapiapp_v2_0 9 | keystone = {% for m in extra_middleware %}{{ m.name }} {% endfor %}request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0 10 | 11 | {% for m in extra_middleware -%} 12 | [{{ m.type }}:{{ m.name }}] 13 | {% for k, v in m.config.items() -%} 14 | {{ k }} = {{ v }} 15 | {% endfor %} 16 | {% endfor -%} 17 | 18 | [filter:request_id] 19 | paste.filter_factory = oslo.middleware:RequestId.factory 20 | 21 | [filter:catch_errors] 22 | paste.filter_factory = oslo.middleware:CatchErrors.factory 23 | 24 | [filter:keystonecontext] 25 | paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory 26 | 27 | [filter:authtoken] 28 | paste.filter_factory = keystonemiddleware.auth_token:filter_factory 29 | 30 | [filter:extensions] 31 | paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory 32 | 33 | [app:neutronversions] 34 | paste.app_factory = neutron.api.versions:Versions.factory 35 | 36 | [app:neutronapiapp_v2_0] 37 | paste.app_factory = neutron.api.v2.router:APIRouter.factory 38 | -------------------------------------------------------------------------------- /templates/kilo/ml2_conf.ini: -------------------------------------------------------------------------------- 1 | # kilo 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | [ml2] 7 | {% if extension_drivers -%} 8 | extension_drivers={{ extension_drivers }} 9 | {% endif -%} 10 | 11 | {% if neutron_plugin == 'Calico' -%} 12 | type_drivers = local,flat 13 | mechanism_drivers = calico 14 | {% else -%} 15 | type_drivers = {{ tenant_network_types }} 16 | tenant_network_types = {{ tenant_network_types }} 17 | mechanism_drivers = {{ mechanism_drivers }} 18 | 19 | [ml2_type_gre] 20 | tunnel_id_ranges = 1:1000 21 | 22 | [ml2_type_vxlan] 23 | vni_ranges = {{ vni_ranges }} 24 | 25 | [ml2_type_vlan] 26 | network_vlan_ranges = {{ vlan_ranges }} 27 | 28 | [ml2_type_flat] 29 | flat_networks = {{ network_providers }} 30 | 31 | [ovs] 32 | enable_tunneling = True 33 | local_ip = {{ local_ip }} 34 | 35 | [agent] 36 | tunnel_types = {{ overlay_network_type }} 37 | {% endif -%} 38 | 39 | [securitygroup] 40 | {% if neutron_security_groups -%} 41 | enable_security_group = True 42 | {% if neutron_plugin == 'Calico' -%} 43 | firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver 44 | {% else -%} 45 | firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver 46 | {% endif -%} 47 | {% else -%} 48 | enable_security_group = False 49 | {% endif -%} 50 | 51 | {% if supported_pci_vendor_devs %} 52 | [ml2_sriov] 53 | supported_pci_vendor_devs = {{ supported_pci_vendor_devs }} 54 | {% endif -%} 55 | -------------------------------------------------------------------------------- /templates/kilo/ml2_conf_sriov.ini: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # [ WARNING ] 3 | # Configuration file maintained by Juju. Local changes may be overwritten. 4 | # 5 | # __NOTE__ [ml2_sriov] section is maintained in ml2_conf.ini 6 | # 7 | ############################################################################### 8 | -------------------------------------------------------------------------------- /templates/kilo/nuage_plugin.ini: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # [ WARNING ] 3 | # Configuration file maintained by Juju. Local changes may be overwritten. 4 | ############################################################################### 5 | [restproxy] 6 | server = {{ vsd_server }} 7 | serverauth = {{ vsd_auth }} 8 | auth_resource = {{ vsd_auth_resource }} 9 | organization = {{ vsd_organization }} 10 | serverssl = {{ vsd_auth_ssl }} 11 | base_uri = {{ vsd_base_uri }} 12 | default_net_partition_name = {{ vsd_netpart_name }} 13 | cms_id = {{ vsd_cms_id }} 14 | -------------------------------------------------------------------------------- /templates/liberty/neutron_lbaas.conf: -------------------------------------------------------------------------------- 1 | # liberty 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | # [service_auth] 7 | # auth_url = http://127.0.0.1:5000/v2.0 8 | # admin_tenant_name = %SERVICE_TENANT_NAME% 9 | # admin_user = %SERVICE_USER% 10 | # admin_password = %SERVICE_PASSWORD% 11 | # admin_user_domain = %SERVICE_USER_DOMAIN% 12 | # admin_project_domain = %SERVICE_PROJECT_DOMAIN% 13 | # region = %REGION% 14 | # service_name = lbaas 15 | # auth_version = 2 16 | 17 | [service_providers] 18 | {% if neutron_plugin and neutron_plugin == 'midonet' -%} 19 | service_provider = LOADBALANCER:Midonet:midonet.neutron.services.loadbalancer.driver.MidonetLoadbalancerDriver:default 20 | {% else -%} 21 | service_provider=LOADBALANCER:Haproxy:neutron_lbaas.services.loadbalancer.drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver:default 22 | # service_provider=LOADBALANCERV2:Haproxy:neutron_lbaas.drivers.haproxy.plugin_driver.HaproxyOnHostPluginDriver:default 23 | {% endif %} 24 | -------------------------------------------------------------------------------- /templates/liberty/neutron_vpnaas.conf: -------------------------------------------------------------------------------- 1 | # liberty 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | [service_providers] 7 | service_provider=VPN:openswan:neutron_vpnaas.services.vpn.service_drivers.ipsec.IPsecVPNDriver:default -------------------------------------------------------------------------------- /templates/mitaka/api-paste.ini: -------------------------------------------------------------------------------- 1 | [composite:neutron] 2 | use = egg:Paste#urlmap 3 | /: neutronversions 4 | /v2.0: neutronapi_v2_0 5 | 6 | [composite:neutronapi_v2_0] 7 | use = call:neutron.auth:pipeline_factory 8 | noauth = cors request_id catch_errors extensions neutronapiapp_v2_0 9 | keystone = {% for m in extra_middleware %}{{ m.name }} {% endfor %}cors request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0 10 | 11 | {% for m in extra_middleware -%} 12 | [{{ m.type }}:{{ m.name }}] 13 | {% for k, v in m.config.items() -%} 14 | {{ k }} = {{ v }} 15 | {% endfor %} 16 | {% endfor -%} 17 | 18 | [filter:request_id] 19 | paste.filter_factory = oslo_middleware:RequestId.factory 20 | 21 | [filter:catch_errors] 22 | paste.filter_factory = oslo_middleware:CatchErrors.factory 23 | 24 | [filter:cors] 25 | paste.filter_factory = oslo_middleware.cors:filter_factory 26 | oslo_config_project = neutron 27 | 28 | [filter:keystonecontext] 29 | paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory 30 | 31 | [filter:authtoken] 32 | paste.filter_factory = keystonemiddleware.auth_token:filter_factory 33 | 34 | [filter:extensions] 35 | paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory 36 | 37 | [app:neutronversions] 38 | paste.app_factory = neutron.api.versions:Versions.factory 39 | 40 | [app:neutronapiapp_v2_0] 41 | paste.app_factory = neutron.api.v2.router:APIRouter.factory 42 | -------------------------------------------------------------------------------- /templates/mitaka/ml2_conf.ini: -------------------------------------------------------------------------------- 1 | # mitaka 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | [ml2] 7 | {% if extension_drivers -%} 8 | extension_drivers={{ extension_drivers }} 9 | {% endif -%} 10 | 11 | {% if neutron_plugin == 'Calico' -%} 12 | type_drivers = local,flat 13 | mechanism_drivers = calico 14 | {% else -%} 15 | type_drivers = {{ tenant_network_types }} 16 | tenant_network_types = {{ tenant_network_types }} 17 | mechanism_drivers = {{ mechanism_drivers }} 18 | 19 | {% if physical_network_mtus -%} 20 | physical_network_mtus = {{ physical_network_mtus }} 21 | {% endif -%} 22 | {% if path_mtu -%} 23 | path_mtu = {{ path_mtu }} 24 | {% endif -%} 25 | 26 | [ml2_type_gre] 27 | tunnel_id_ranges = 1:1000 28 | 29 | [ml2_type_vxlan] 30 | vni_ranges = {{ vni_ranges }} 31 | 32 | [ml2_type_vlan] 33 | network_vlan_ranges = {{ vlan_ranges }} 34 | 35 | [ml2_type_flat] 36 | flat_networks = {{ network_providers }} 37 | 38 | [ovs] 39 | enable_tunneling = True 40 | local_ip = {{ local_ip }} 41 | 42 | [agent] 43 | tunnel_types = {{ overlay_network_type }} 44 | {% endif -%} 45 | 46 | [securitygroup] 47 | {% if neutron_security_groups -%} 48 | enable_security_group = True 49 | {% if neutron_plugin == 'Calico' -%} 50 | firewall_driver = neutron.agent.linux.iptables_firewall.IptablesFirewallDriver 51 | {% else -%} 52 | firewall_driver = neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver 53 | {% endif -%} 54 | {% else -%} 55 | enable_security_group = False 56 | {% endif -%} 57 | 58 | {% if supported_pci_vendor_devs %} 59 | [ml2_sriov] 60 | supported_pci_vendor_devs = {{ supported_pci_vendor_devs }} 61 | {% endif -%} 62 | -------------------------------------------------------------------------------- /templates/newton/api-paste.ini: -------------------------------------------------------------------------------- 1 | [composite:neutron] 2 | use = egg:Paste#urlmap 3 | /: neutronversions_composite 4 | /v2.0: neutronapi_v2_0 5 | 6 | [composite:neutronapi_v2_0] 7 | use = call:neutron.auth:pipeline_factory 8 | noauth = cors http_proxy_to_wsgi request_id catch_errors extensions neutronapiapp_v2_0 9 | keystone = {% for m in extra_middleware %}{{ m.name }} {% endfor %}cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0 10 | 11 | {% for m in extra_middleware -%} 12 | [{{ m.type }}:{{ m.name }}] 13 | {% for k, v in m.config.items() -%} 14 | {{ k }} = {{ v }} 15 | {% endfor %} 16 | {% endfor -%} 17 | 18 | [composite:neutronversions_composite] 19 | use = call:neutron.auth:pipeline_factory 20 | noauth = cors http_proxy_to_wsgi neutronversions 21 | keystone = cors http_proxy_to_wsgi neutronversions 22 | 23 | [filter:request_id] 24 | paste.filter_factory = oslo_middleware:RequestId.factory 25 | 26 | [filter:catch_errors] 27 | paste.filter_factory = oslo_middleware:CatchErrors.factory 28 | 29 | [filter:cors] 30 | paste.filter_factory = oslo_middleware.cors:filter_factory 31 | oslo_config_project = neutron 32 | 33 | [filter:http_proxy_to_wsgi] 34 | paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory 35 | 36 | [filter:keystonecontext] 37 | paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory 38 | 39 | [filter:authtoken] 40 | paste.filter_factory = keystonemiddleware.auth_token:filter_factory 41 | 42 | [filter:extensions] 43 | paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory 44 | 45 | [app:neutronversions] 46 | paste.app_factory = neutron.api.versions:Versions.factory 47 | 48 | [app:neutronapiapp_v2_0] 49 | paste.app_factory = neutron.api.v2.router:APIRouter.factory 50 | 51 | [filter:osprofiler] 52 | paste.filter_factory = osprofiler.web:WsgiMiddleware.factory 53 | -------------------------------------------------------------------------------- /templates/parts/database: -------------------------------------------------------------------------------- 1 | {% if database_host -%} 2 | sql_connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} 3 | {% endif -%} 4 | -------------------------------------------------------------------------------- /templates/parts/rabbitmq: -------------------------------------------------------------------------------- 1 | {% if rabbitmq_host or rabbitmq_hosts -%} 2 | rabbit_userid = {{ rabbitmq_user }} 3 | rabbit_virtual_host = {{ rabbitmq_virtual_host }} 4 | rabbit_password = {{ rabbitmq_password }} 5 | {% if rabbitmq_hosts -%} 6 | rabbit_hosts = {{ rabbitmq_hosts }} 7 | {% if rabbitmq_ha_queues -%} 8 | rabbit_ha_queues = True 9 | rabbit_durable_queues = False 10 | {% endif -%} 11 | {% else -%} 12 | rabbit_host = {{ rabbitmq_host }} 13 | {% endif -%} 14 | {% if rabbit_ssl_port -%} 15 | rabbit_use_ssl = True 16 | rabbit_port = {{ rabbit_ssl_port }} 17 | {% if rabbit_ssl_ca -%} 18 | kombu_ssl_ca_certs = {{ rabbit_ssl_ca }} 19 | {% endif -%} 20 | {% endif -%} 21 | {% endif -%} -------------------------------------------------------------------------------- /templates/parts/section-database: -------------------------------------------------------------------------------- 1 | {% if database_host -%} 2 | [database] 3 | connection = {{ database_type }}://{{ database_user }}:{{ database_password }}@{{ database_host }}/{{ database }}{% if database_ssl_ca %}?ssl_ca={{ database_ssl_ca }}{% if database_ssl_cert %}&ssl_cert={{ database_ssl_cert }}&ssl_key={{ database_ssl_key }}{% endif %}{% endif %} 4 | {% endif -%} 5 | -------------------------------------------------------------------------------- /templates/parts/section-designate: -------------------------------------------------------------------------------- 1 | [designate] 2 | url = {{ designate_endpoint }}/v2 3 | {% if api_version == "3" -%} 4 | auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }} 5 | auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} 6 | auth_type = password 7 | project_domain_name = {{ admin_domain_name }} 8 | user_domain_name = {{ admin_domain_name }} 9 | project_name = {{ admin_tenant_name }} 10 | username = {{ admin_user }} 11 | password = {{ admin_password }} 12 | signing_dir = {{ signing_dir }} 13 | {% else -%} 14 | admin_auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }} 15 | admin_username = {{ admin_user }} 16 | admin_password = {{ admin_password }} 17 | admin_tenant_name = {{ admin_tenant_name }} 18 | {% endif -%} 19 | allow_reverse_dns_lookup = {{ allow_reverse_dns_lookup }} 20 | {% if allow_reverse_dns_lookup -%} 21 | ipv4_ptr_zone_prefix_size = {{ ipv4_ptr_zone_prefix_size }} 22 | ipv6_ptr_zone_prefix_size = {{ ipv6_ptr_zone_prefix_size }} 23 | {% endif -%} 24 | -------------------------------------------------------------------------------- /templates/parts/section-infoblox: -------------------------------------------------------------------------------- 1 | [infoblox] 2 | cloud_data_center_id = {{ cloud_data_center_id }} 3 | 4 | [infoblox-dc:{{ cloud_data_center_id }}] 5 | grid_master_host = {{ grid_master_host }} 6 | grid_master_name = {{ grid_master_name }} 7 | admin_user_name = {{ infoblox_admin_user_name }} 8 | admin_password = {{ infoblox_admin_password }} 9 | wapi_version = {{ wapi_version }} 10 | wapi_max_results = {{ wapi_max_results }} 11 | wapi_paging = {{ wapi_paging }} 12 | -------------------------------------------------------------------------------- /templates/parts/section-nova: -------------------------------------------------------------------------------- 1 | [nova] 2 | {% if enable_infoblox -%} 3 | # TODO - Exceptionally we added the content of [keystone_authtoken] due to an 4 | # internal mechanism of Infoblox plugin lp-1688039. 5 | {% if auth_host -%} 6 | auth_type = password 7 | {% if api_version == "3" -%} 8 | auth_uri = {{ service_protocol }}://{{ service_host }}:{{ service_port }}/v3 9 | auth_url = {{ auth_protocol }}://{{ auth_host }}:{{ auth_port }}/v3 10 | project_domain_name = {{ admin_domain_name }} 11 | user_domain_name = {{ admin_domain_name }} 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 | {% endif -%} 26 | {% else %} 27 | auth_section = keystone_authtoken 28 | {% endif %} 29 | region_name = {{ region }} 30 | {% if use_internal_endpoints -%} 31 | endpoint_type = internal 32 | {% endif %} 33 | -------------------------------------------------------------------------------- /templates/parts/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 | {% endif -%} 19 | {% if use_internal_endpoints -%} 20 | os_interface = internal 21 | {% endif -%} 22 | -------------------------------------------------------------------------------- /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/ml2_conf.ini: -------------------------------------------------------------------------------- 1 | # queens 2 | ############################################################################### 3 | # [ WARNING ] 4 | # Configuration file maintained by Juju. Local changes may be overwritten. 5 | ############################################################################### 6 | [ml2] 7 | {% if extension_drivers -%} 8 | extension_drivers={{ extension_drivers }} 9 | {% endif -%} 10 | 11 | {% if neutron_plugin == 'Calico' -%} 12 | type_drivers = local,flat 13 | mechanism_drivers = calico 14 | {% else -%} 15 | type_drivers = {{ tenant_network_types }} 16 | tenant_network_types = {{ tenant_network_types }} 17 | mechanism_drivers = {{ mechanism_drivers }} 18 | 19 | {% if physical_network_mtus -%} 20 | physical_network_mtus = {{ physical_network_mtus }} 21 | {% endif -%} 22 | {% if path_mtu -%} 23 | path_mtu = {{ path_mtu }} 24 | {% endif -%} 25 | 26 | [ml2_type_gre] 27 | tunnel_id_ranges = 1:1000 28 | 29 | [ml2_type_vxlan] 30 | vni_ranges = {{ vni_ranges }} 31 | 32 | [ml2_type_vlan] 33 | network_vlan_ranges = {{ vlan_ranges }} 34 | 35 | [ml2_type_flat] 36 | flat_networks = {{ network_providers }} 37 | 38 | [ovs] 39 | enable_tunneling = True 40 | local_ip = {{ local_ip }} 41 | 42 | [agent] 43 | tunnel_types = {{ overlay_network_type }} 44 | {% if 'log' in service_plugins -%} 45 | extensions = log 46 | 47 | {% endif -%} 48 | 49 | {% endif -%} 50 | 51 | [securitygroup] 52 | {% if neutron_security_groups -%} 53 | enable_security_group = True 54 | {% else -%} 55 | enable_security_group = False 56 | {% endif -%} 57 | 58 | {% if supported_pci_vendor_devs %} 59 | [ml2_sriov] 60 | supported_pci_vendor_devs = {{ supported_pci_vendor_devs }} 61 | {% endif -%} 62 | 63 | {% for section in sections -%} 64 | {% if section != 'DEFAULT' -%} 65 | [{{ section }}] 66 | {% for key, value in sections[section] -%} 67 | {{ key }} = {{ value }} 68 | {% endfor %} 69 | {% endif %} 70 | {%- endfor %} 71 | -------------------------------------------------------------------------------- /templates/rocky/api-paste.ini: -------------------------------------------------------------------------------- 1 | [composite:neutron] 2 | use = egg:Paste#urlmap 3 | /: neutronversions_composite 4 | /healthcheck: healthcheck 5 | /v2.0: neutronapi_v2_0 6 | 7 | [composite:neutronapi_v2_0] 8 | use = call:neutron.auth:pipeline_factory 9 | noauth = cors http_proxy_to_wsgi request_id catch_errors extensions neutronapiapp_v2_0 10 | keystone = {% for m in extra_middleware %}{{ m.name }} {% endfor %}cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0 11 | 12 | {% for m in extra_middleware -%} 13 | [{{ m.type }}:{{ m.name }}] 14 | {% for k, v in m.config.items() -%} 15 | {{ k }} = {{ v }} 16 | {% endfor %} 17 | {% endfor -%} 18 | 19 | [composite:neutronversions_composite] 20 | use = call:neutron.auth:pipeline_factory 21 | noauth = cors http_proxy_to_wsgi neutronversions 22 | keystone = cors http_proxy_to_wsgi neutronversions 23 | 24 | [filter:request_id] 25 | paste.filter_factory = oslo_middleware:RequestId.factory 26 | 27 | [filter:catch_errors] 28 | paste.filter_factory = oslo_middleware:CatchErrors.factory 29 | 30 | [filter:cors] 31 | paste.filter_factory = oslo_middleware.cors:filter_factory 32 | oslo_config_project = neutron 33 | 34 | [filter:http_proxy_to_wsgi] 35 | paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory 36 | 37 | [filter:keystonecontext] 38 | paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory 39 | 40 | [filter:authtoken] 41 | paste.filter_factory = keystonemiddleware.auth_token:filter_factory 42 | 43 | [filter:extensions] 44 | paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory 45 | 46 | [app:neutronversions] 47 | paste.app_factory = neutron.pecan_wsgi.app:versions_factory 48 | 49 | [app:neutronapiapp_v2_0] 50 | paste.app_factory = neutron.api.v2.router:APIRouter.factory 51 | 52 | [filter:osprofiler] 53 | paste.filter_factory = osprofiler.web:WsgiMiddleware.factory 54 | 55 | [app:healthcheck] 56 | paste.app_factory = oslo_middleware:Healthcheck.app_factory 57 | backends = disable_by_file 58 | disable_by_file_path = /var/lib/neutron/healthcheck_disable 59 | -------------------------------------------------------------------------------- /templates/yoga/api-paste.ini: -------------------------------------------------------------------------------- 1 | [composite:neutron] 2 | use = egg:Paste#urlmap 3 | /: neutronversions_composite 4 | /healthcheck: healthcheck 5 | /v2.0: neutronapi_v2_0 6 | 7 | [composite:neutronapi_v2_0] 8 | use = call:neutron.auth:pipeline_factory 9 | noauth = cors http_proxy_to_wsgi request_id catch_errors extensions neutronapiapp_v2_0 10 | {% if audit_middleware and service_name -%} 11 | keystone = {% for m in extra_middleware %}{{ m.name }} {% endfor %}cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext audit extensions neutronapiapp_v2_0 12 | {% else -%} 13 | keystone = {% for m in extra_middleware %}{{ m.name }} {% endfor %}cors http_proxy_to_wsgi request_id catch_errors authtoken keystonecontext extensions neutronapiapp_v2_0 14 | {% endif %} 15 | 16 | {% for m in extra_middleware -%} 17 | [{{ m.type }}:{{ m.name }}] 18 | {% for k, v in m.config.items() -%} 19 | {{ k }} = {{ v }} 20 | {% endfor %} 21 | {% endfor -%} 22 | 23 | [composite:neutronversions_composite] 24 | use = call:neutron.auth:pipeline_factory 25 | noauth = cors http_proxy_to_wsgi neutronversions 26 | keystone = cors http_proxy_to_wsgi neutronversions 27 | 28 | [filter:request_id] 29 | paste.filter_factory = oslo_middleware:RequestId.factory 30 | 31 | [filter:catch_errors] 32 | paste.filter_factory = oslo_middleware:CatchErrors.factory 33 | 34 | [filter:cors] 35 | paste.filter_factory = oslo_middleware.cors:filter_factory 36 | oslo_config_project = neutron 37 | 38 | [filter:http_proxy_to_wsgi] 39 | paste.filter_factory = oslo_middleware.http_proxy_to_wsgi:HTTPProxyToWSGI.factory 40 | 41 | [filter:keystonecontext] 42 | paste.filter_factory = neutron.auth:NeutronKeystoneContext.factory 43 | 44 | [filter:authtoken] 45 | paste.filter_factory = keystonemiddleware.auth_token:filter_factory 46 | 47 | [filter:extensions] 48 | paste.filter_factory = neutron.api.extensions:plugin_aware_extension_middleware_factory 49 | 50 | [app:neutronversions] 51 | paste.app_factory = neutron.pecan_wsgi.app:versions_factory 52 | 53 | [app:neutronapiapp_v2_0] 54 | paste.app_factory = neutron.api.v2.router:APIRouter.factory 55 | 56 | [filter:osprofiler] 57 | paste.filter_factory = osprofiler.web:WsgiMiddleware.factory 58 | 59 | [app:healthcheck] 60 | paste.app_factory = oslo_middleware:Healthcheck.app_factory 61 | backends = disable_by_file 62 | disable_by_file_path = /var/lib/neutron/healthcheck_disable 63 | 64 | {% include "section-filter-audit" %} -------------------------------------------------------------------------------- /templates/yoga/api_audit_map.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | # default target endpoint type 3 | # should match the endpoint type defined in service catalog 4 | target_endpoint_type = None 5 | 6 | [custom_actions] 7 | add_router_interface = update/add 8 | remove_router_interface = update/remove 9 | 10 | # possible end path of api requests 11 | [path_keywords] 12 | floatingips = ip 13 | healthmonitors = healthmonitor 14 | health_monitors = health_monitor 15 | lb = None 16 | members = member 17 | metering-labels = label 18 | metering-label-rules = rule 19 | networks = network 20 | pools = pool 21 | ports = port 22 | routers = router 23 | quotas = quota 24 | security-groups = security-group 25 | security-group-rules = rule 26 | subnets = subnet 27 | vips = vip 28 | 29 | # map endpoint type defined in service catalog to CADF typeURI 30 | [service_endpoints] 31 | network = service/network -------------------------------------------------------------------------------- /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: &series noble 5 | 6 | applications: 7 | keystone-mysql-router: 8 | charm: ch:mysql-router 9 | channel: latest/edge 10 | neutron-mysql-router: 11 | charm: ch:mysql-router 12 | channel: latest/edge 13 | mysql-innodb-cluster: 14 | charm: ch:mysql-innodb-cluster 15 | num_units: 3 16 | options: 17 | source: *openstack-origin 18 | channel: latest/edge 19 | rabbitmq-server: 20 | charm: ch:rabbitmq-server 21 | num_units: 1 22 | channel: latest/edge 23 | neutron-api: 24 | charm: ../../neutron-api.charm 25 | num_units: 1 26 | options: 27 | manage-neutron-plugin-legacy-mode: True 28 | flat-network-providers: physnet1 29 | neutron-security-groups: true 30 | openstack-origin: *openstack-origin 31 | keystone: 32 | charm: ch:keystone 33 | num_units: 1 34 | options: 35 | openstack-origin: *openstack-origin 36 | channel: latest/edge 37 | relations: 38 | - - neutron-api:amqp 39 | - rabbitmq-server:amqp 40 | - - neutron-api:identity-service 41 | - keystone:identity-service 42 | - ["keystone:shared-db", "keystone-mysql-router:shared-db"] 43 | - ["neutron-api:shared-db", "neutron-mysql-router:shared-db"] 44 | - ["keystone-mysql-router:db-router", "mysql-innodb-cluster:db-router"] 45 | - ["neutron-mysql-router:db-router", "mysql-innodb-cluster:db-router"] 46 | -------------------------------------------------------------------------------- /tests/tests.yaml: -------------------------------------------------------------------------------- 1 | charm_name: neutron-api 2 | 3 | gate_bundles: 4 | - noble-caracal 5 | smoke_bundles: 6 | - jammy-caracal 7 | dev_bundles: 8 | - noble-caracal 9 | tests: 10 | - zaza.openstack.charm_tests.neutron.tests.NeutronApiTest 11 | - zaza.openstack.charm_tests.neutron.tests.NeutronCreateNetworkTest 12 | - zaza.openstack.charm_tests.neutron.tests.SecurityTest 13 | - zaza.openstack.charm_tests.policyd.tests.NeutronApiTests 14 | - zaza.openstack.charm_tests.audit.tests.KeystoneAuditMiddlewareTest 15 | 16 | configure: 17 | - zaza.openstack.charm_tests.keystone.setup.add_demo_user 18 | 19 | tests_options: 20 | audit-middleware: 21 | service: neutron 22 | application: neutron-api 23 | policyd: 24 | service: neutron 25 | -------------------------------------------------------------------------------- /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 sys 16 | from unittest import mock 17 | 18 | # python-apt is not installed as part of test-requirements but is imported by 19 | # some charmhelpers modules so create a fake import. 20 | sys.modules['apt'] = mock.MagicMock() 21 | sys.modules['apt_pkg'] = mock.MagicMock() 22 | 23 | sys.path.append('actions/') 24 | sys.path.append('hooks/') 25 | sys.path.append('unit_tests') 26 | 27 | # Patch out lsb_release() and get_platform() as unit tests should be fully 28 | # insulated from the underlying platform. Unit tests assume that the system is 29 | # ubuntu jammy. 30 | mock.patch( 31 | 'charmhelpers.osplatform.get_platform', return_value='ubuntu' 32 | ).start() 33 | mock.patch( 34 | 'charmhelpers.core.host.lsb_release', 35 | return_value={ 36 | 'DISTRIB_CODENAME': 'jammy' 37 | }).start() 38 | -------------------------------------------------------------------------------- /unit_tests/test_actions.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 | 17 | mock.patch('charmhelpers.core.hookenv.status_set').start() 18 | with mock.patch('charmhelpers.core.hookenv.config') as config: 19 | config.return_value = 'neutron' 20 | import neutron_api_utils as utils # noqa 21 | 22 | # Need to do some early patching to get the module loaded. 23 | _reg = utils.register_configs 24 | _map = utils.restart_map 25 | 26 | utils.register_configs = mock.MagicMock() 27 | utils.restart_map = mock.MagicMock() 28 | 29 | import actions 30 | 31 | # Unpatch it now that its loaded. 32 | utils.register_configs = _reg 33 | utils.restart_map = _map 34 | 35 | from test_utils import ( 36 | CharmTestCase 37 | ) 38 | 39 | TO_PATCH = [ 40 | ] 41 | 42 | 43 | class PauseTestCase(CharmTestCase): 44 | 45 | def setUp(self): 46 | super(PauseTestCase, self).setUp( 47 | actions, ["register_configs", "pause_unit_helper"]) 48 | self.register_configs.return_value = 'test-config' 49 | 50 | def test_pauses_services(self): 51 | actions.pause([]) 52 | self.pause_unit_helper.assert_called_once_with('test-config') 53 | 54 | 55 | class ResumeTestCase(CharmTestCase): 56 | 57 | def setUp(self): 58 | super(ResumeTestCase, self).setUp( 59 | actions, ["register_configs", "resume_unit_helper"]) 60 | self.register_configs.return_value = 'test-config' 61 | 62 | def test_resumes_services(self): 63 | actions.resume([]) 64 | self.resume_unit_helper.assert_called_once_with('test-config') 65 | 66 | 67 | class MainTestCase(CharmTestCase): 68 | 69 | def setUp(self): 70 | super(MainTestCase, self).setUp(actions, ["register_configs", 71 | "action_fail"]) 72 | self.register_configs.return_value = 'test-config' 73 | 74 | def test_invokes_action(self): 75 | dummy_calls = [] 76 | 77 | def dummy_action(args): 78 | dummy_calls.append(True) 79 | 80 | with mock.patch.dict(actions.ACTIONS, {"foo": dummy_action}): 81 | actions.main(["foo"]) 82 | self.assertEqual(dummy_calls, [True]) 83 | 84 | def test_unknown_action(self): 85 | """Unknown actions aren't a traceback.""" 86 | exit_string = actions.main(["foo"]) 87 | self.assertEqual("Action foo undefined", exit_string) 88 | 89 | def test_failing_action(self): 90 | """Actions which traceback trigger action_fail() calls.""" 91 | dummy_calls = [] 92 | 93 | self.action_fail.side_effect = dummy_calls.append 94 | 95 | def dummy_action(args): 96 | raise ValueError("uh oh") 97 | 98 | with mock.patch.dict(actions.ACTIONS, {"foo": dummy_action}): 99 | actions.main(["foo"]) 100 | self.assertEqual(dummy_calls, ["uh oh"]) 101 | -------------------------------------------------------------------------------- /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 | 17 | from unittest.mock import patch 18 | 19 | os.environ['JUJU_UNIT_NAME'] = 'neutron-api' 20 | 21 | with patch('charmhelpers.core.hookenv.config') as config: 22 | with patch('neutron_api_utils.restart_map'): 23 | config.return_value = 'ovs' 24 | with patch('neutron_api_utils.register_configs') as register_configs: 25 | import openstack_upgrade 26 | 27 | from test_utils import CharmTestCase 28 | 29 | TO_PATCH = [ 30 | 'do_openstack_upgrade', 31 | 'config_changed', 32 | ] 33 | 34 | 35 | class TestNeutronAPIUpgradeActions(CharmTestCase): 36 | 37 | def setUp(self): 38 | super(TestNeutronAPIUpgradeActions, self).setUp(openstack_upgrade, 39 | TO_PATCH) 40 | 41 | @patch('charmhelpers.contrib.openstack.utils.juju_log') 42 | @patch('charmhelpers.contrib.openstack.utils.config') 43 | @patch('charmhelpers.contrib.openstack.utils.action_set') 44 | @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') 45 | def test_openstack_upgrade_true(self, upgrade_avail, 46 | action_set, config, log): 47 | upgrade_avail.return_value = True 48 | config.return_value = True 49 | 50 | openstack_upgrade.openstack_upgrade() 51 | 52 | self.assertTrue(self.do_openstack_upgrade.called) 53 | self.assertTrue(self.config_changed.called) 54 | 55 | @patch('charmhelpers.contrib.openstack.utils.juju_log') 56 | @patch('charmhelpers.contrib.openstack.utils.config') 57 | @patch('charmhelpers.contrib.openstack.utils.action_set') 58 | @patch('charmhelpers.contrib.openstack.utils.openstack_upgrade_available') 59 | def test_openstack_upgrade_false(self, upgrade_avail, 60 | action_set, config, log): 61 | upgrade_avail.return_value = True 62 | config.return_value = False 63 | 64 | openstack_upgrade.openstack_upgrade() 65 | 66 | self.assertFalse(self.do_openstack_upgrade.called) 67 | self.assertFalse(self.config_changed.called) 68 | --------------------------------------------------------------------------------