Merge ~hellsworth/+git/appstream-cloud:built-appstream-generator into ~ubuntu-desktop/+git/appstream-cloud:built-appstream-generator

Proposed by Heather Ellsworth
Status: Merged
Merged at revision: 8b0905935171995645a5627f358e9ccf9dc71c5e
Proposed branch: ~hellsworth/+git/appstream-cloud:built-appstream-generator
Merge into: ~ubuntu-desktop/+git/appstream-cloud:built-appstream-generator
Diff against target: 321997 lines (+318431/-0)
419 files modified
charms/appstream-generator/LICENSE (+202/-0)
charms/appstream-generator/actions.yaml (+18/-0)
charms/appstream-generator/config.yaml (+16/-0)
charms/appstream-generator/dispatch (+3/-0)
charms/appstream-generator/hooks/install (+3/-0)
charms/appstream-generator/hooks/start (+3/-0)
charms/appstream-generator/hooks/upgrade-charm (+3/-0)
charms/appstream-generator/manifest.yaml (+13/-0)
charms/appstream-generator/metadata.yaml (+18/-0)
charms/appstream-generator/src/charm.py (+455/-0)
charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/INSTALLER (+1/-0)
charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/LICENSE.rst (+28/-0)
charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/METADATA (+113/-0)
charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/RECORD (+58/-0)
charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/WHEEL (+5/-0)
charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/entry_points.txt (+3/-0)
charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/top_level.txt (+1/-0)
charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/INSTALLER (+1/-0)
charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/LICENSE.rst (+28/-0)
charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/METADATA (+100/-0)
charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/RECORD (+14/-0)
charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/WHEEL (+5/-0)
charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/top_level.txt (+1/-0)
charms/appstream-generator/venv/PyYAML-6.0.dist-info/INSTALLER (+1/-0)
charms/appstream-generator/venv/PyYAML-6.0.dist-info/LICENSE (+20/-0)
charms/appstream-generator/venv/PyYAML-6.0.dist-info/METADATA (+46/-0)
charms/appstream-generator/venv/PyYAML-6.0.dist-info/RECORD (+43/-0)
charms/appstream-generator/venv/PyYAML-6.0.dist-info/WHEEL (+8/-0)
charms/appstream-generator/venv/PyYAML-6.0.dist-info/top_level.txt (+2/-0)
charms/appstream-generator/venv/Tempita-0.5.2.dist-info/INSTALLER (+1/-0)
charms/appstream-generator/venv/Tempita-0.5.2.dist-info/METADATA (+30/-0)
charms/appstream-generator/venv/Tempita-0.5.2.dist-info/RECORD (+14/-0)
charms/appstream-generator/venv/Tempita-0.5.2.dist-info/WHEEL (+5/-0)
charms/appstream-generator/venv/Tempita-0.5.2.dist-info/top_level.txt (+1/-0)
charms/appstream-generator/venv/Tempita-0.5.2.dist-info/zip-safe (+1/-0)
charms/appstream-generator/venv/_yaml/__init__.py (+33/-0)
charms/appstream-generator/venv/bin/charmsupport (+31/-0)
charms/appstream-generator/venv/bin/chlp (+8/-0)
charms/appstream-generator/venv/bin/netaddr (+8/-0)
charms/appstream-generator/venv/bin/pbr (+8/-0)
charms/appstream-generator/venv/bin/salt-call (+11/-0)
charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/AUTHORS (+222/-0)
charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/INSTALLER (+1/-0)
charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/LICENSE (+202/-0)
charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/METADATA (+82/-0)
charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/RECORD (+338/-0)
charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/WHEEL (+5/-0)
charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/pbr.json (+1/-0)
charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/top_level.txt (+1/-0)
charms/appstream-generator/venv/charmhelpers/__init__.py (+99/-0)
charms/appstream-generator/venv/charmhelpers/cli/README.rst (+57/-0)
charms/appstream-generator/venv/charmhelpers/cli/__init__.py (+196/-0)
charms/appstream-generator/venv/charmhelpers/cli/benchmark.py (+34/-0)
charms/appstream-generator/venv/charmhelpers/cli/commands.py (+30/-0)
charms/appstream-generator/venv/charmhelpers/cli/hookenv.py (+21/-0)
charms/appstream-generator/venv/charmhelpers/cli/host.py (+29/-0)
charms/appstream-generator/venv/charmhelpers/cli/unitdata.py (+46/-0)
charms/appstream-generator/venv/charmhelpers/context.py (+205/-0)
charms/appstream-generator/venv/charmhelpers/contrib/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/ansible/__init__.py (+306/-0)
charms/appstream-generator/venv/charmhelpers/contrib/benchmark/__init__.py (+124/-0)
charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/IMPORT (+4/-0)
charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/__init__.py (+203/-0)
charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/IMPORT (+14/-0)
charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/nrpe.py (+522/-0)
charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/volumes.py (+173/-0)
charms/appstream-generator/venv/charmhelpers/contrib/database/__init__.py (+11/-0)
charms/appstream-generator/venv/charmhelpers/contrib/database/mysql.py (+840/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hahelpers/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hahelpers/apache.py (+90/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hahelpers/cluster.py (+451/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/README.hardening.md (+38/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/apache/__init__.py (+17/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/apache/checks/__init__.py (+29/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/apache/checks/config.py (+104/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/apache/templates/99-hardening.conf (+32/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/apache/templates/alias.conf (+31/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/audits/__init__.py (+54/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/audits/apache.py (+105/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/audits/apt.py (+104/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/audits/file.py (+550/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/defaults/apache.yaml (+16/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/defaults/apache.yaml.schema (+12/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/defaults/mysql.yaml (+38/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/defaults/mysql.yaml.schema (+15/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/defaults/os.yaml (+68/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/defaults/os.yaml.schema (+43/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/defaults/ssh.yaml (+49/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/defaults/ssh.yaml.schema (+42/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/harden.py (+96/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/__init__.py (+17/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/__init__.py (+48/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/apt.py (+37/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/limits.py (+53/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/login.py (+65/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/minimize_access.py (+50/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/pam.py (+132/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/profile.py (+49/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/securetty.py (+37/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/suid_sgid.py (+129/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/checks/sysctl.py (+209/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/templates/10.hardcore.conf (+8/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/templates/99-hardening.sh (+5/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf (+7/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/templates/login.defs (+349/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/templates/modules (+117/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/templates/passwdqc.conf (+11/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh (+8/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/templates/securetty (+11/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/host/templates/tally2 (+14/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/mysql/__init__.py (+17/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/mysql/checks/__init__.py (+29/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/mysql/checks/config.py (+87/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/mysql/templates/hardening.cnf (+12/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/ssh/__init__.py (+17/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/ssh/checks/__init__.py (+29/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/ssh/checks/config.py (+435/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/ssh/templates/ssh_config (+70/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/ssh/templates/sshd_config (+159/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/templating.py (+73/-0)
charms/appstream-generator/venv/charmhelpers/contrib/hardening/utils.py (+155/-0)
charms/appstream-generator/venv/charmhelpers/contrib/mellanox/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/mellanox/infiniband.py (+153/-0)
charms/appstream-generator/venv/charmhelpers/contrib/network/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/network/ip.py (+603/-0)
charms/appstream-generator/venv/charmhelpers/contrib/network/ovs/__init__.py (+693/-0)
charms/appstream-generator/venv/charmhelpers/contrib/network/ovs/ovn.py (+233/-0)
charms/appstream-generator/venv/charmhelpers/contrib/network/ovs/ovsdb.py (+246/-0)
charms/appstream-generator/venv/charmhelpers/contrib/network/ovs/utils.py (+26/-0)
charms/appstream-generator/venv/charmhelpers/contrib/network/ufw.py (+386/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/alternatives.py (+44/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/audits/__init__.py (+212/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/audits/openstack_security_guide.py (+270/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/cert_utils.py (+443/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/context.py (+3363/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/deferred_events.py (+416/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/exceptions.py (+26/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/files/__init__.py (+16/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/files/check_haproxy.sh (+34/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh (+30/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/files/policy_rc_d_script.py (+196/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/ha/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/ha/utils.py (+348/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/ip.py (+235/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/keystone.py (+178/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/neutron.py (+359/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/policy_rcd.py (+173/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/policyd.py (+801/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/ssh_migrations.py (+412/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/__init__.py (+16/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/ceph.conf (+28/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/git.upstart (+17/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/haproxy.cfg (+89/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/logrotate (+9/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/memcached.conf (+53/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/openstack_https_frontend (+35/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf (+35/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-ceph-bluestore-compression (+28/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-keystone-authtoken (+12/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-legacy (+10/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka (+22/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-keystone-authtoken-v3only (+9/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-oslo-cache (+6/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-oslo-messaging-rabbit (+10/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-oslo-messaging-rabbit-ocata (+10/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-oslo-middleware (+5/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-oslo-notifications (+15/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-placement (+20/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo (+22/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/section-zeromq (+14/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/vendor_data.json (+1/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf (+91/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf (+91/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/templating.py (+379/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/utils.py (+2643/-0)
charms/appstream-generator/venv/charmhelpers/contrib/openstack/vaultlocker.py (+179/-0)
charms/appstream-generator/venv/charmhelpers/contrib/peerstorage/__init__.py (+267/-0)
charms/appstream-generator/venv/charmhelpers/contrib/python.py (+21/-0)
charms/appstream-generator/venv/charmhelpers/contrib/saltstack/__init__.py (+116/-0)
charms/appstream-generator/venv/charmhelpers/contrib/ssl/__init__.py (+92/-0)
charms/appstream-generator/venv/charmhelpers/contrib/ssl/service.py (+277/-0)
charms/appstream-generator/venv/charmhelpers/contrib/storage/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/storage/linux/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/storage/linux/bcache.py (+74/-0)
charms/appstream-generator/venv/charmhelpers/contrib/storage/linux/ceph.py (+2378/-0)
charms/appstream-generator/venv/charmhelpers/contrib/storage/linux/loopback.py (+92/-0)
charms/appstream-generator/venv/charmhelpers/contrib/storage/linux/lvm.py (+182/-0)
charms/appstream-generator/venv/charmhelpers/contrib/storage/linux/utils.py (+128/-0)
charms/appstream-generator/venv/charmhelpers/contrib/sysctl/__init__.py (+0/-0)
charms/appstream-generator/venv/charmhelpers/contrib/sysctl/watermark_scale_factor.py (+104/-0)
charms/appstream-generator/venv/charmhelpers/contrib/templating/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/contrib/templating/contexts.py (+137/-0)
charms/appstream-generator/venv/charmhelpers/contrib/templating/jinja.py (+51/-0)
charms/appstream-generator/venv/charmhelpers/contrib/templating/pyformat.py (+27/-0)
charms/appstream-generator/venv/charmhelpers/contrib/unison/__init__.py (+316/-0)
charms/appstream-generator/venv/charmhelpers/coordinator.py (+606/-0)
charms/appstream-generator/venv/charmhelpers/core/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/core/decorators.py (+93/-0)
charms/appstream-generator/venv/charmhelpers/core/files.py (+43/-0)
charms/appstream-generator/venv/charmhelpers/core/fstab.py (+132/-0)
charms/appstream-generator/venv/charmhelpers/core/hookenv.py (+1639/-0)
charms/appstream-generator/venv/charmhelpers/core/host.py (+1279/-0)
charms/appstream-generator/venv/charmhelpers/core/host_factory/__init__.py (+0/-0)
charms/appstream-generator/venv/charmhelpers/core/host_factory/centos.py (+72/-0)
charms/appstream-generator/venv/charmhelpers/core/host_factory/ubuntu.py (+121/-0)
charms/appstream-generator/venv/charmhelpers/core/hugepage.py (+69/-0)
charms/appstream-generator/venv/charmhelpers/core/kernel.py (+72/-0)
charms/appstream-generator/venv/charmhelpers/core/kernel_factory/__init__.py (+0/-0)
charms/appstream-generator/venv/charmhelpers/core/kernel_factory/centos.py (+17/-0)
charms/appstream-generator/venv/charmhelpers/core/kernel_factory/ubuntu.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/core/services/__init__.py (+16/-0)
charms/appstream-generator/venv/charmhelpers/core/services/base.py (+367/-0)
charms/appstream-generator/venv/charmhelpers/core/services/helpers.py (+290/-0)
charms/appstream-generator/venv/charmhelpers/core/strutils.py (+132/-0)
charms/appstream-generator/venv/charmhelpers/core/sysctl.py (+75/-0)
charms/appstream-generator/venv/charmhelpers/core/templating.py (+93/-0)
charms/appstream-generator/venv/charmhelpers/core/unitdata.py (+525/-0)
charms/appstream-generator/venv/charmhelpers/fetch/__init__.py (+212/-0)
charms/appstream-generator/venv/charmhelpers/fetch/archiveurl.py (+165/-0)
charms/appstream-generator/venv/charmhelpers/fetch/bzrurl.py (+76/-0)
charms/appstream-generator/venv/charmhelpers/fetch/centos.py (+171/-0)
charms/appstream-generator/venv/charmhelpers/fetch/giturl.py (+69/-0)
charms/appstream-generator/venv/charmhelpers/fetch/python/__init__.py (+13/-0)
charms/appstream-generator/venv/charmhelpers/fetch/python/debug.py (+54/-0)
charms/appstream-generator/venv/charmhelpers/fetch/python/packages.py (+156/-0)
charms/appstream-generator/venv/charmhelpers/fetch/python/rpdb.py (+56/-0)
charms/appstream-generator/venv/charmhelpers/fetch/python/version.py (+32/-0)
charms/appstream-generator/venv/charmhelpers/fetch/snap.py (+150/-0)
charms/appstream-generator/venv/charmhelpers/fetch/ubuntu.py (+1011/-0)
charms/appstream-generator/venv/charmhelpers/fetch/ubuntu_apt_pkg.py (+312/-0)
charms/appstream-generator/venv/charmhelpers/osplatform.py (+49/-0)
charms/appstream-generator/venv/charmhelpers/payload/__init__.py (+15/-0)
charms/appstream-generator/venv/charmhelpers/payload/archive.py (+71/-0)
charms/appstream-generator/venv/charmhelpers/payload/execd.py (+65/-0)
charms/appstream-generator/venv/charmhelpers/version.py (+2/-0)
charms/appstream-generator/venv/jinja2/__init__.py (+45/-0)
charms/appstream-generator/venv/jinja2/_identifier.py (+6/-0)
charms/appstream-generator/venv/jinja2/async_utils.py (+68/-0)
charms/appstream-generator/venv/jinja2/bccache.py (+364/-0)
charms/appstream-generator/venv/jinja2/compiler.py (+1957/-0)
charms/appstream-generator/venv/jinja2/constants.py (+20/-0)
charms/appstream-generator/venv/jinja2/debug.py (+279/-0)
charms/appstream-generator/venv/jinja2/defaults.py (+48/-0)
charms/appstream-generator/venv/jinja2/environment.py (+1674/-0)
charms/appstream-generator/venv/jinja2/exceptions.py (+166/-0)
charms/appstream-generator/venv/jinja2/ext.py (+879/-0)
charms/appstream-generator/venv/jinja2/filters.py (+1824/-0)
charms/appstream-generator/venv/jinja2/idtracking.py (+318/-0)
charms/appstream-generator/venv/jinja2/lexer.py (+869/-0)
charms/appstream-generator/venv/jinja2/loaders.py (+644/-0)
charms/appstream-generator/venv/jinja2/meta.py (+111/-0)
charms/appstream-generator/venv/jinja2/nativetypes.py (+118/-0)
charms/appstream-generator/venv/jinja2/nodes.py (+1205/-0)
charms/appstream-generator/venv/jinja2/optimizer.py (+47/-0)
charms/appstream-generator/venv/jinja2/parser.py (+1040/-0)
charms/appstream-generator/venv/jinja2/py.typed (+0/-0)
charms/appstream-generator/venv/jinja2/runtime.py (+1104/-0)
charms/appstream-generator/venv/jinja2/sandbox.py (+428/-0)
charms/appstream-generator/venv/jinja2/tests.py (+255/-0)
charms/appstream-generator/venv/jinja2/utils.py (+854/-0)
charms/appstream-generator/venv/jinja2/visitor.py (+92/-0)
charms/appstream-generator/venv/markupsafe/__init__.py (+288/-0)
charms/appstream-generator/venv/markupsafe/_native.py (+75/-0)
charms/appstream-generator/venv/markupsafe/_speedups.c (+339/-0)
charms/appstream-generator/venv/markupsafe/_speedups.pyi (+9/-0)
charms/appstream-generator/venv/markupsafe/py.typed (+0/-0)
charms/appstream-generator/venv/netaddr-0.8.0.dist-info/AUTHORS (+5/-0)
charms/appstream-generator/venv/netaddr-0.8.0.dist-info/INSTALLER (+1/-0)
charms/appstream-generator/venv/netaddr-0.8.0.dist-info/LICENSE (+36/-0)
charms/appstream-generator/venv/netaddr-0.8.0.dist-info/METADATA (+118/-0)
charms/appstream-generator/venv/netaddr-0.8.0.dist-info/RECORD (+57/-0)
charms/appstream-generator/venv/netaddr-0.8.0.dist-info/WHEEL (+6/-0)
charms/appstream-generator/venv/netaddr-0.8.0.dist-info/entry_points.txt (+3/-0)
charms/appstream-generator/venv/netaddr-0.8.0.dist-info/top_level.txt (+1/-0)
charms/appstream-generator/venv/netaddr/__init__.py (+48/-0)
charms/appstream-generator/venv/netaddr/cli.py (+42/-0)
charms/appstream-generator/venv/netaddr/compat.py (+93/-0)
charms/appstream-generator/venv/netaddr/contrib/__init__.py (+12/-0)
charms/appstream-generator/venv/netaddr/contrib/subnet_splitter.py (+46/-0)
charms/appstream-generator/venv/netaddr/core.py (+206/-0)
charms/appstream-generator/venv/netaddr/eui/__init__.py (+749/-0)
charms/appstream-generator/venv/netaddr/eui/iab.idx (+4575/-0)
charms/appstream-generator/venv/netaddr/eui/iab.txt (+27381/-0)
charms/appstream-generator/venv/netaddr/eui/ieee.py (+293/-0)
charms/appstream-generator/venv/netaddr/eui/oui.idx (+28112/-0)
charms/appstream-generator/venv/netaddr/eui/oui.txt (+168378/-0)
charms/appstream-generator/venv/netaddr/fbsocket.py (+246/-0)
charms/appstream-generator/venv/netaddr/ip/__init__.py (+1972/-0)
charms/appstream-generator/venv/netaddr/ip/glob.py (+312/-0)
charms/appstream-generator/venv/netaddr/ip/iana.py (+448/-0)
charms/appstream-generator/venv/netaddr/ip/ipv4-address-space.xml (+2644/-0)
charms/appstream-generator/venv/netaddr/ip/ipv6-address-space.xml (+198/-0)
charms/appstream-generator/venv/netaddr/ip/ipv6-unicast-address-assignments.xml (+435/-0)
charms/appstream-generator/venv/netaddr/ip/multicast-addresses.xml (+4441/-0)
charms/appstream-generator/venv/netaddr/ip/nmap.py (+117/-0)
charms/appstream-generator/venv/netaddr/ip/rfc1924.py (+61/-0)
charms/appstream-generator/venv/netaddr/ip/sets.py (+748/-0)
charms/appstream-generator/venv/netaddr/strategy/__init__.py (+273/-0)
charms/appstream-generator/venv/netaddr/strategy/eui48.py (+296/-0)
charms/appstream-generator/venv/netaddr/strategy/eui64.py (+273/-0)
charms/appstream-generator/venv/netaddr/strategy/ipv4.py (+279/-0)
charms/appstream-generator/venv/netaddr/strategy/ipv6.py (+259/-0)
charms/appstream-generator/venv/ops-1.2.0.dist-info/INSTALLER (+1/-0)
charms/appstream-generator/venv/ops-1.2.0.dist-info/METADATA (+263/-0)
charms/appstream-generator/venv/ops-1.2.0.dist-info/RECORD (+33/-0)
charms/appstream-generator/venv/ops-1.2.0.dist-info/WHEEL (+5/-0)
charms/appstream-generator/venv/ops-1.2.0.dist-info/top_level.txt (+1/-0)
charms/appstream-generator/venv/ops/__init__.py (+44/-0)
charms/appstream-generator/venv/ops/_private/__init__.py (+0/-0)
charms/appstream-generator/venv/ops/_private/yaml.py (+32/-0)
charms/appstream-generator/venv/ops/charm.py (+893/-0)
charms/appstream-generator/venv/ops/framework.py (+1199/-0)
charms/appstream-generator/venv/ops/jujuversion.py (+114/-0)
charms/appstream-generator/venv/ops/lib/__init__.py (+264/-0)
charms/appstream-generator/venv/ops/log.py (+58/-0)
charms/appstream-generator/venv/ops/main.py (+410/-0)
charms/appstream-generator/venv/ops/model.py (+1579/-0)
charms/appstream-generator/venv/ops/pebble.py (+1112/-0)
charms/appstream-generator/venv/ops/storage.py (+374/-0)
charms/appstream-generator/venv/ops/testing.py (+1111/-0)
charms/appstream-generator/venv/ops/version.py (+3/-0)
charms/appstream-generator/venv/pbr-5.6.0.dist-info/AUTHORS (+153/-0)
charms/appstream-generator/venv/pbr-5.6.0.dist-info/INSTALLER (+1/-0)
charms/appstream-generator/venv/pbr-5.6.0.dist-info/LICENSE (+176/-0)
charms/appstream-generator/venv/pbr-5.6.0.dist-info/METADATA (+75/-0)
charms/appstream-generator/venv/pbr-5.6.0.dist-info/RECORD (+110/-0)
charms/appstream-generator/venv/pbr-5.6.0.dist-info/WHEEL (+6/-0)
charms/appstream-generator/venv/pbr-5.6.0.dist-info/entry_points.txt (+9/-0)
charms/appstream-generator/venv/pbr-5.6.0.dist-info/top_level.txt (+1/-0)
charms/appstream-generator/venv/pbr/__init__.py (+0/-0)
charms/appstream-generator/venv/pbr/builddoc.py (+292/-0)
charms/appstream-generator/venv/pbr/cmd/__init__.py (+0/-0)
charms/appstream-generator/venv/pbr/cmd/main.py (+119/-0)
charms/appstream-generator/venv/pbr/core.py (+145/-0)
charms/appstream-generator/venv/pbr/extra_files.py (+35/-0)
charms/appstream-generator/venv/pbr/find_package.py (+29/-0)
charms/appstream-generator/venv/pbr/git.py (+338/-0)
charms/appstream-generator/venv/pbr/hooks/__init__.py (+28/-0)
charms/appstream-generator/venv/pbr/hooks/backwards.py (+33/-0)
charms/appstream-generator/venv/pbr/hooks/base.py (+34/-0)
charms/appstream-generator/venv/pbr/hooks/commands.py (+66/-0)
charms/appstream-generator/venv/pbr/hooks/files.py (+126/-0)
charms/appstream-generator/venv/pbr/hooks/metadata.py (+32/-0)
charms/appstream-generator/venv/pbr/options.py (+53/-0)
charms/appstream-generator/venv/pbr/packaging.py (+887/-0)
charms/appstream-generator/venv/pbr/pbr_json.py (+34/-0)
charms/appstream-generator/venv/pbr/sphinxext.py (+99/-0)
charms/appstream-generator/venv/pbr/testr_command.py (+167/-0)
charms/appstream-generator/venv/pbr/tests/__init__.py (+26/-0)
charms/appstream-generator/venv/pbr/tests/base.py (+226/-0)
charms/appstream-generator/venv/pbr/tests/test_commands.py (+84/-0)
charms/appstream-generator/venv/pbr/tests/test_core.py (+158/-0)
charms/appstream-generator/venv/pbr/tests/test_files.py (+148/-0)
charms/appstream-generator/venv/pbr/tests/test_hooks.py (+75/-0)
charms/appstream-generator/venv/pbr/tests/test_integration.py (+286/-0)
charms/appstream-generator/venv/pbr/tests/test_packaging.py (+1113/-0)
charms/appstream-generator/venv/pbr/tests/test_pbr_json.py (+30/-0)
charms/appstream-generator/venv/pbr/tests/test_setup.py (+447/-0)
charms/appstream-generator/venv/pbr/tests/test_util.py (+320/-0)
charms/appstream-generator/venv/pbr/tests/test_version.py (+311/-0)
charms/appstream-generator/venv/pbr/tests/test_wsgi.py (+163/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/CHANGES.txt (+86/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/LICENSE.txt (+29/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/MANIFEST.in (+2/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/README.txt (+148/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/data_files/a.txt (+0/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/data_files/b.txt (+0/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/data_files/c.rst (+0/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/doc/source/conf.py (+59/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/doc/source/index.rst (+23/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/doc/source/installation.rst (+12/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/doc/source/usage.rst (+7/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/extra-file.txt (+0/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/git-extra-file.txt (+0/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/pbr_testpackage/__init__.py (+3/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/pbr_testpackage/_setup_hooks.py (+65/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/pbr_testpackage/cmd.py (+26/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/pbr_testpackage/extra.py (+0/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/pbr_testpackage/package_data/1.txt (+0/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/pbr_testpackage/package_data/2.txt (+0/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/pbr_testpackage/wsgi.py (+40/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/setup.cfg (+58/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/setup.py (+21/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/src/testext.c (+29/-0)
charms/appstream-generator/venv/pbr/tests/testpackage/test-requirements.txt (+2/-0)
charms/appstream-generator/venv/pbr/tests/util.py (+78/-0)
charms/appstream-generator/venv/pbr/util.py (+621/-0)
charms/appstream-generator/venv/pbr/version.py (+483/-0)
charms/appstream-generator/venv/six-1.16.0.dist-info/INSTALLER (+1/-0)
charms/appstream-generator/venv/six-1.16.0.dist-info/LICENSE (+18/-0)
charms/appstream-generator/venv/six-1.16.0.dist-info/METADATA (+49/-0)
charms/appstream-generator/venv/six-1.16.0.dist-info/RECORD (+8/-0)
charms/appstream-generator/venv/six-1.16.0.dist-info/WHEEL (+6/-0)
charms/appstream-generator/venv/six-1.16.0.dist-info/top_level.txt (+1/-0)
charms/appstream-generator/venv/six.py (+998/-0)
charms/appstream-generator/venv/tempita/__init__.py (+1187/-0)
charms/appstream-generator/venv/tempita/__main__.py (+3/-0)
charms/appstream-generator/venv/tempita/_looper.py (+164/-0)
charms/appstream-generator/venv/tempita/compat3.py (+45/-0)
charms/appstream-generator/venv/yaml/__init__.py (+390/-0)
charms/appstream-generator/venv/yaml/composer.py (+139/-0)
charms/appstream-generator/venv/yaml/constructor.py (+748/-0)
charms/appstream-generator/venv/yaml/cyaml.py (+101/-0)
charms/appstream-generator/venv/yaml/dumper.py (+62/-0)
charms/appstream-generator/venv/yaml/emitter.py (+1137/-0)
charms/appstream-generator/venv/yaml/error.py (+75/-0)
charms/appstream-generator/venv/yaml/events.py (+86/-0)
charms/appstream-generator/venv/yaml/loader.py (+63/-0)
charms/appstream-generator/venv/yaml/nodes.py (+49/-0)
charms/appstream-generator/venv/yaml/parser.py (+589/-0)
charms/appstream-generator/venv/yaml/reader.py (+185/-0)
charms/appstream-generator/venv/yaml/representer.py (+389/-0)
charms/appstream-generator/venv/yaml/resolver.py (+227/-0)
charms/appstream-generator/venv/yaml/scanner.py (+1435/-0)
charms/appstream-generator/venv/yaml/serializer.py (+111/-0)
charms/appstream-generator/venv/yaml/tokens.py (+104/-0)
Reviewer Review Type Date Requested Status
Ubuntu Desktop Pending
Review via email: mp+410902@code.launchpad.net

Commit message

New build

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/charms/appstream-generator/LICENSE b/charms/appstream-generator/LICENSE
2new file mode 100644
3index 0000000..d645695
4--- /dev/null
5+++ b/charms/appstream-generator/LICENSE
6@@ -0,0 +1,202 @@
7+
8+ Apache License
9+ Version 2.0, January 2004
10+ http://www.apache.org/licenses/
11+
12+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
13+
14+ 1. Definitions.
15+
16+ "License" shall mean the terms and conditions for use, reproduction,
17+ and distribution as defined by Sections 1 through 9 of this document.
18+
19+ "Licensor" shall mean the copyright owner or entity authorized by
20+ the copyright owner that is granting the License.
21+
22+ "Legal Entity" shall mean the union of the acting entity and all
23+ other entities that control, are controlled by, or are under common
24+ control with that entity. For the purposes of this definition,
25+ "control" means (i) the power, direct or indirect, to cause the
26+ direction or management of such entity, whether by contract or
27+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
28+ outstanding shares, or (iii) beneficial ownership of such entity.
29+
30+ "You" (or "Your") shall mean an individual or Legal Entity
31+ exercising permissions granted by this License.
32+
33+ "Source" form shall mean the preferred form for making modifications,
34+ including but not limited to software source code, documentation
35+ source, and configuration files.
36+
37+ "Object" form shall mean any form resulting from mechanical
38+ transformation or translation of a Source form, including but
39+ not limited to compiled object code, generated documentation,
40+ and conversions to other media types.
41+
42+ "Work" shall mean the work of authorship, whether in Source or
43+ Object form, made available under the License, as indicated by a
44+ copyright notice that is included in or attached to the work
45+ (an example is provided in the Appendix below).
46+
47+ "Derivative Works" shall mean any work, whether in Source or Object
48+ form, that is based on (or derived from) the Work and for which the
49+ editorial revisions, annotations, elaborations, or other modifications
50+ represent, as a whole, an original work of authorship. For the purposes
51+ of this License, Derivative Works shall not include works that remain
52+ separable from, or merely link (or bind by name) to the interfaces of,
53+ the Work and Derivative Works thereof.
54+
55+ "Contribution" shall mean any work of authorship, including
56+ the original version of the Work and any modifications or additions
57+ to that Work or Derivative Works thereof, that is intentionally
58+ submitted to Licensor for inclusion in the Work by the copyright owner
59+ or by an individual or Legal Entity authorized to submit on behalf of
60+ the copyright owner. For the purposes of this definition, "submitted"
61+ means any form of electronic, verbal, or written communication sent
62+ to the Licensor or its representatives, including but not limited to
63+ communication on electronic mailing lists, source code control systems,
64+ and issue tracking systems that are managed by, or on behalf of, the
65+ Licensor for the purpose of discussing and improving the Work, but
66+ excluding communication that is conspicuously marked or otherwise
67+ designated in writing by the copyright owner as "Not a Contribution."
68+
69+ "Contributor" shall mean Licensor and any individual or Legal Entity
70+ on behalf of whom a Contribution has been received by Licensor and
71+ subsequently incorporated within the Work.
72+
73+ 2. Grant of Copyright License. Subject to the terms and conditions of
74+ this License, each Contributor hereby grants to You a perpetual,
75+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76+ copyright license to reproduce, prepare Derivative Works of,
77+ publicly display, publicly perform, sublicense, and distribute the
78+ Work and such Derivative Works in Source or Object form.
79+
80+ 3. Grant of Patent License. Subject to the terms and conditions of
81+ this License, each Contributor hereby grants to You a perpetual,
82+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
83+ (except as stated in this section) patent license to make, have made,
84+ use, offer to sell, sell, import, and otherwise transfer the Work,
85+ where such license applies only to those patent claims licensable
86+ by such Contributor that are necessarily infringed by their
87+ Contribution(s) alone or by combination of their Contribution(s)
88+ with the Work to which such Contribution(s) was submitted. If You
89+ institute patent litigation against any entity (including a
90+ cross-claim or counterclaim in a lawsuit) alleging that the Work
91+ or a Contribution incorporated within the Work constitutes direct
92+ or contributory patent infringement, then any patent licenses
93+ granted to You under this License for that Work shall terminate
94+ as of the date such litigation is filed.
95+
96+ 4. Redistribution. You may reproduce and distribute copies of the
97+ Work or Derivative Works thereof in any medium, with or without
98+ modifications, and in Source or Object form, provided that You
99+ meet the following conditions:
100+
101+ (a) You must give any other recipients of the Work or
102+ Derivative Works a copy of this License; and
103+
104+ (b) You must cause any modified files to carry prominent notices
105+ stating that You changed the files; and
106+
107+ (c) You must retain, in the Source form of any Derivative Works
108+ that You distribute, all copyright, patent, trademark, and
109+ attribution notices from the Source form of the Work,
110+ excluding those notices that do not pertain to any part of
111+ the Derivative Works; and
112+
113+ (d) If the Work includes a "NOTICE" text file as part of its
114+ distribution, then any Derivative Works that You distribute must
115+ include a readable copy of the attribution notices contained
116+ within such NOTICE file, excluding those notices that do not
117+ pertain to any part of the Derivative Works, in at least one
118+ of the following places: within a NOTICE text file distributed
119+ as part of the Derivative Works; within the Source form or
120+ documentation, if provided along with the Derivative Works; or,
121+ within a display generated by the Derivative Works, if and
122+ wherever such third-party notices normally appear. The contents
123+ of the NOTICE file are for informational purposes only and
124+ do not modify the License. You may add Your own attribution
125+ notices within Derivative Works that You distribute, alongside
126+ or as an addendum to the NOTICE text from the Work, provided
127+ that such additional attribution notices cannot be construed
128+ as modifying the License.
129+
130+ You may add Your own copyright statement to Your modifications and
131+ may provide additional or different license terms and conditions
132+ for use, reproduction, or distribution of Your modifications, or
133+ for any such Derivative Works as a whole, provided Your use,
134+ reproduction, and distribution of the Work otherwise complies with
135+ the conditions stated in this License.
136+
137+ 5. Submission of Contributions. Unless You explicitly state otherwise,
138+ any Contribution intentionally submitted for inclusion in the Work
139+ by You to the Licensor shall be under the terms and conditions of
140+ this License, without any additional terms or conditions.
141+ Notwithstanding the above, nothing herein shall supersede or modify
142+ the terms of any separate license agreement you may have executed
143+ with Licensor regarding such Contributions.
144+
145+ 6. Trademarks. This License does not grant permission to use the trade
146+ names, trademarks, service marks, or product names of the Licensor,
147+ except as required for reasonable and customary use in describing the
148+ origin of the Work and reproducing the content of the NOTICE file.
149+
150+ 7. Disclaimer of Warranty. Unless required by applicable law or
151+ agreed to in writing, Licensor provides the Work (and each
152+ Contributor provides its Contributions) on an "AS IS" BASIS,
153+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
154+ implied, including, without limitation, any warranties or conditions
155+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
156+ PARTICULAR PURPOSE. You are solely responsible for determining the
157+ appropriateness of using or redistributing the Work and assume any
158+ risks associated with Your exercise of permissions under this License.
159+
160+ 8. Limitation of Liability. In no event and under no legal theory,
161+ whether in tort (including negligence), contract, or otherwise,
162+ unless required by applicable law (such as deliberate and grossly
163+ negligent acts) or agreed to in writing, shall any Contributor be
164+ liable to You for damages, including any direct, indirect, special,
165+ incidental, or consequential damages of any character arising as a
166+ result of this License or out of the use or inability to use the
167+ Work (including but not limited to damages for loss of goodwill,
168+ work stoppage, computer failure or malfunction, or any and all
169+ other commercial damages or losses), even if such Contributor
170+ has been advised of the possibility of such damages.
171+
172+ 9. Accepting Warranty or Additional Liability. While redistributing
173+ the Work or Derivative Works thereof, You may choose to offer,
174+ and charge a fee for, acceptance of support, warranty, indemnity,
175+ or other liability obligations and/or rights consistent with this
176+ License. However, in accepting such obligations, You may act only
177+ on Your own behalf and on Your sole responsibility, not on behalf
178+ of any other Contributor, and only if You agree to indemnify,
179+ defend, and hold each Contributor harmless for any liability
180+ incurred by, or claims asserted against, such Contributor by reason
181+ of your accepting any such warranty or additional liability.
182+
183+ END OF TERMS AND CONDITIONS
184+
185+ APPENDIX: How to apply the Apache License to your work.
186+
187+ To apply the Apache License to your work, attach the following
188+ boilerplate notice, with the fields enclosed by brackets "[]"
189+ replaced with your own identifying information. (Don't include
190+ the brackets!) The text should be enclosed in the appropriate
191+ comment syntax for the file format. We also recommend that a
192+ file or class name and description of purpose be included on the
193+ same "printed page" as the copyright notice for easier
194+ identification within third-party archives.
195+
196+ Copyright [yyyy] [name of copyright owner]
197+
198+ Licensed under the Apache License, Version 2.0 (the "License");
199+ you may not use this file except in compliance with the License.
200+ You may obtain a copy of the License at
201+
202+ http://www.apache.org/licenses/LICENSE-2.0
203+
204+ Unless required by applicable law or agreed to in writing, software
205+ distributed under the License is distributed on an "AS IS" BASIS,
206+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
207+ See the License for the specific language governing permissions and
208+ limitations under the License.
209diff --git a/charms/appstream-generator/actions.yaml b/charms/appstream-generator/actions.yaml
210new file mode 100644
211index 0000000..03a55e4
212--- /dev/null
213+++ b/charms/appstream-generator/actions.yaml
214@@ -0,0 +1,18 @@
215+clean:
216+ description: Clean up so that the next run forces a full refresh
217+run-now:
218+ description: Run an update immediately
219+forget:
220+ description: Forget packages from all releases, causing them to be reprocessed the next time
221+ params:
222+ packages:
223+ type: array
224+ items: { type: string }
225+ required: [packages]
226+forget-tag:
227+ description: Forget all packages with a given tag, causing them to be reprocessed the next time
228+ params:
229+ tag:
230+ type: string
231+ description: The tag to forget
232+ required: [tag]
233diff --git a/charms/appstream-generator/config.yaml b/charms/appstream-generator/config.yaml
234new file mode 100644
235index 0000000..3d30680
236--- /dev/null
237+++ b/charms/appstream-generator/config.yaml
238@@ -0,0 +1,16 @@
239+options:
240+ hostname:
241+ description: The hostname the service will be presented on
242+ type: string
243+ config:
244+ description: The configuration of the asgen (releases, etc - see config.json)
245+ type: string
246+ mirror:
247+ default: http://archive.ubuntu.com/ubuntu/
248+ default_snap_channel:
249+ description: When installing snaps, default to this channel
250+ type: string
251+ default: stable
252+ http_proxy:
253+ https_proxy:
254+ no_proxy:
255diff --git a/charms/appstream-generator/dispatch b/charms/appstream-generator/dispatch
256new file mode 100755
257index 0000000..fe31c05
258--- /dev/null
259+++ b/charms/appstream-generator/dispatch
260@@ -0,0 +1,3 @@
261+#!/bin/sh
262+
263+JUJU_DISPATCH_PATH="${JUJU_DISPATCH_PATH:-$0}" PYTHONPATH=lib:venv ./src/charm.py
264diff --git a/charms/appstream-generator/hooks/install b/charms/appstream-generator/hooks/install
265new file mode 100755
266index 0000000..fe31c05
267--- /dev/null
268+++ b/charms/appstream-generator/hooks/install
269@@ -0,0 +1,3 @@
270+#!/bin/sh
271+
272+JUJU_DISPATCH_PATH="${JUJU_DISPATCH_PATH:-$0}" PYTHONPATH=lib:venv ./src/charm.py
273diff --git a/charms/appstream-generator/hooks/start b/charms/appstream-generator/hooks/start
274new file mode 100755
275index 0000000..fe31c05
276--- /dev/null
277+++ b/charms/appstream-generator/hooks/start
278@@ -0,0 +1,3 @@
279+#!/bin/sh
280+
281+JUJU_DISPATCH_PATH="${JUJU_DISPATCH_PATH:-$0}" PYTHONPATH=lib:venv ./src/charm.py
282diff --git a/charms/appstream-generator/hooks/upgrade-charm b/charms/appstream-generator/hooks/upgrade-charm
283new file mode 100755
284index 0000000..fe31c05
285--- /dev/null
286+++ b/charms/appstream-generator/hooks/upgrade-charm
287@@ -0,0 +1,3 @@
288+#!/bin/sh
289+
290+JUJU_DISPATCH_PATH="${JUJU_DISPATCH_PATH:-$0}" PYTHONPATH=lib:venv ./src/charm.py
291diff --git a/charms/appstream-generator/manifest.yaml b/charms/appstream-generator/manifest.yaml
292new file mode 100644
293index 0000000..c8edade
294--- /dev/null
295+++ b/charms/appstream-generator/manifest.yaml
296@@ -0,0 +1,13 @@
297+analysis:
298+ attributes:
299+ - name: language
300+ result: python
301+ - name: framework
302+ result: operator
303+bases:
304+- architectures:
305+ - amd64
306+ channel: '20.04'
307+ name: ubuntu
308+charmcraft-started-at: '2021-10-27T19:57:21.230918Z'
309+charmcraft-version: 1.2.1
310diff --git a/charms/appstream-generator/metadata.yaml b/charms/appstream-generator/metadata.yaml
311new file mode 100644
312index 0000000..099feed
313--- /dev/null
314+++ b/charms/appstream-generator/metadata.yaml
315@@ -0,0 +1,18 @@
316+name: appstream-generator
317+description: |
318+ Generate Appstream metadata from a distribution's software archive
319+summary: |
320+ Generate Appstream metadata from a distribution's software archive
321+series: [focal]
322+storage:
323+ appstream:
324+ type: filesystem
325+ description: appstream-generator's output & working data
326+ shared: false
327+ read-only: false
328+ minimum-size: 200G
329+ location: /home/ubuntu/appstream
330+provides:
331+ rsync:
332+ interface: appstream-rsync
333+ optional: true
334diff --git a/charms/appstream-generator/src/charm.py b/charms/appstream-generator/src/charm.py
335new file mode 100755
336index 0000000..b415f50
337--- /dev/null
338+++ b/charms/appstream-generator/src/charm.py
339@@ -0,0 +1,455 @@
340+#!/usr/bin/env python3
341+# Copyright 2021 Canonical Ltd
342+
343+# This program is free software: you can redistribute it and/or modify it under
344+# the terms of the GNU General Public License as published by the Free Software
345+# Foundation, either version 3 of the License, or (at your option) any later
346+# version.
347+#
348+# This program is distributed in the hope that it will be useful, but WITHOUT
349+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
350+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
351+#
352+# You should have received a copy of the GNU General Public License along with
353+# this program. If not, see <http://www.gnu.org/licenses/>.
354+
355+import json
356+import logging
357+import os
358+import shutil
359+import subprocess
360+from pathlib import Path
361+from textwrap import dedent
362+
363+from charmhelpers.core.hookenv import open_port
364+from ops.charm import CharmBase
365+from ops.framework import StoredState
366+from ops.main import main
367+from ops.model import ActiveStatus, BlockedStatus, MaintenanceStatus
368+
369+logger = logging.getLogger(__name__)
370+
371+# This is a special value that means we use the default channel, which comes
372+# from the charm config.
373+DEFAULT_SNAP_CHANNEL = None
374+
375+APPSTREAM_BASE = Path("~ubuntu/appstream").expanduser()
376+APPSTREAM_PUBLIC = APPSTREAM_BASE / "appstream-public"
377+APPSTREAM_WORKDIR = APPSTREAM_BASE / "appstream-workdir"
378+ENVIRONMENT_FILE = Path("/etc/environment.d/proxy.conf")
379+INPUT_FILENAME = "asgen-config.json.in"
380+OUTPUT_FILENAME = APPSTREAM_WORKDIR / "asgen-config.json"
381+PACKAGES_TO_INSTALL = ["jq"]
382+SNAPS_TO_INSTALL = {"appstream-generator": DEFAULT_SNAP_CHANNEL}
383+SYSTEMD_ENABLE_UNITS = ("appstream-generator.timer",)
384+SYSTEMD_UNITS = ("appstream-generator.service", "appstream-generator.timer")
385+
386+CONFIG_DEFAULT_PRIORITY = 0
387+
388+CONFIG_PRIORITIES = {
389+ "updates": 10,
390+ "security": 20,
391+ "proposed": 30,
392+ "backports": 40,
393+}
394+
395+CONFIG_HEADER = {
396+ "ProjectName": "Ubuntu",
397+ "Backend": "ubuntu",
398+ "Oldsuites": [],
399+ "Suites": {},
400+ "Features": {"validateMetainfo": True},
401+}
402+
403+
404+class AppstreamGeneratorCharm(CharmBase):
405+ _stored = StoredState()
406+
407+ def __init__(self, *args):
408+ super().__init__(*args)
409+
410+ self.framework.observe(self.on.config_changed, self._on_config_changed)
411+ self.framework.observe(self.on.install, self._on_install)
412+ self.framework.observe(
413+ self.on.appstream_storage_attached,
414+ self._on_appstream_storage_attached,
415+ )
416+ self.framework.observe(
417+ self.on.appstream_storage_detaching,
418+ self._on_appstream_storage_detaching,
419+ )
420+ self.framework.observe(self.on.start, self._on_start)
421+ self.framework.observe(self.on.clean_action, self._on_clean_action)
422+ self.framework.observe(self.on.forget_action, self._on_forget_action)
423+ self.framework.observe(
424+ self.on.forget_tag_action, self._on_forget_tag_action
425+ )
426+
427+ self._stored.set_default(
428+ installed_packages=set(),
429+ installed_snaps=dict(),
430+ storage_attached=False,
431+ )
432+
433+ def _on_clean_action(self, event):
434+ clean_file = APPSTREAM_BASE / "clean"
435+ with clean_file.open("w") as f:
436+ pass
437+ shutil.chown(clean_file, user="ubuntu", group="ubuntu")
438+ logger.info("Data will be cleaned with the next full run.")
439+
440+ def _on_forget_action(self, event):
441+ forget_file = APPSTREAM_BASE / "forget"
442+ packages_raw = event.params["packages"]
443+ packages = json.loads(packages_raw)
444+ packages_s = ", ".join(packages)
445+ logger.info(f"Forgetting {packages_s}")
446+ with forget_file.open("a") as f:
447+ for package in packages:
448+ f.write(f"{package}\n")
449+
450+ def _on_forget_tag_action(self, event):
451+ import lzma
452+
453+ forget_file = APPSTREAM_BASE / "forget"
454+ hints_files = (APPSTREAM_PUBLIC / "hints").glob("*/*/Hints-*.xz")
455+ tag_to_forget = event.params["tag"]
456+ logger.info(f"Forgetting all packages with tag {tag_to_forget}")
457+
458+ for filename in hints_files:
459+ p = set()
460+ hf = lzma.open(filename, "rt", encoding="utf-8")
461+ j = json.load(hf)
462+ for (v, pkg) in [
463+ (v, pkg)
464+ for (d, pkg) in [(t["hints"], t["package"]) for t in j]
465+ for v in d.values()
466+ ]:
467+ for i in v:
468+ if i["tag"] == tag_to_forget:
469+ p.add(pkg)
470+
471+ with forget_file.open("a") as f:
472+ for pkg in p:
473+ f.write(f"{pkg}\n")
474+
475+ def _on_appstream_storage_attached(self, event):
476+ self._stored.storage_attached = True
477+ mp = self.meta.storages["appstream"].location
478+ shutil.chown(mp, user="ubuntu", group="ubuntu")
479+ try:
480+ APPSTREAM_WORKDIR.mkdir(parents=True, exist_ok=False)
481+ shutil.chown(APPSTREAM_WORKDIR, user="ubuntu", group="ubuntu")
482+ except FileExistsError:
483+ pass
484+
485+ def _on_appstream_storage_detaching(self, _):
486+ self._stored.storage_attached = False
487+
488+ def _on_start(self, event):
489+ if not self._ensure_set_up(event, more_to_do=True):
490+ return
491+
492+ for unit in SYSTEMD_ENABLE_UNITS:
493+ subprocess.check_call(
494+ ["systemctl", "enable", "--quiet", "--now", unit]
495+ )
496+ self.unit.status = ActiveStatus()
497+
498+ def _install_packages(self, packages):
499+ packages = packages - self._stored.installed_packages
500+ if not packages:
501+ logger.info("No packages to install.")
502+ return
503+ pkgs = ", ".join(packages)
504+ self.unit.status = MaintenanceStatus(f"Installing {pkgs}")
505+ logger.info(f"Installing apt package(s) {pkgs}")
506+ subprocess.check_call(
507+ [
508+ "apt-get",
509+ "--assume-yes",
510+ "--option=Dpkg::Options::=--force-confold",
511+ "install",
512+ ]
513+ + list(packages)
514+ )
515+ self._stored.installed_packages |= packages
516+
517+ def _install_snaps(self, wanted_snaps):
518+ default_snap_channel = self.model.config.get(
519+ "default_snap_channel", "stable"
520+ )
521+ snaps_to_install = {}
522+ for snap in wanted_snaps:
523+ wanted_channel = wanted_snaps[snap] or default_snap_channel
524+ if snap not in self._stored.installed_snaps:
525+ verb = "install"
526+ else:
527+ if self._stored.installed_snaps[snap] == wanted_channel:
528+ continue
529+ verb = "refresh"
530+ snaps_to_install[snap] = wanted_channel, verb
531+
532+ if not snaps_to_install:
533+ logger.info("No snaps to install.")
534+ return
535+ snps_install = ", ".join(
536+ [
537+ s
538+ for s in snaps_to_install
539+ if snaps_to_install[s][1] == "install"
540+ ]
541+ )
542+ status_list = []
543+ if snps_install:
544+ status_list.append(f"Installing snap packages: {snps_install}")
545+ snps_refresh = ", ".join(
546+ [
547+ f"{s}/{snaps_to_install[s][0]}"
548+ for s in snaps_to_install
549+ if snaps_to_install[s][1] == "refresh"
550+ ]
551+ )
552+ if snps_refresh:
553+ status_list.append(f"Refreshing snap packages: {snps_refresh}")
554+ status_string = "; ".join(status_list)
555+
556+ self.unit.status = MaintenanceStatus(status_string)
557+ logger.info(status_string)
558+ for snap, (channel, verb) in snaps_to_install.items():
559+ subprocess.check_call(
560+ [
561+ "snap",
562+ verb,
563+ "--channel",
564+ channel,
565+ snap,
566+ ],
567+ )
568+ self._stored.installed_snaps[snap] = channel
569+
570+ def _set_up_proxy(self):
571+ http_proxy = self.model.config.get("http-proxy")
572+ https_proxy = self.model.config.get("https-proxy")
573+ no_proxy = self.model.config.get("no-proxy")
574+
575+ if http_proxy or https_proxy or no_proxy:
576+ logger.info(f"Writing proxy settings to {ENVIRONMENT_FILE}")
577+ ENVIRONMENT_FILE.mkdir(parents=True, exist_ok=True)
578+ with ENVIRONMENT_FILE.open("w") as env:
579+ if http_proxy:
580+ env.write(f"http_proxy={http_proxy}\n")
581+ if https_proxy:
582+ env.write(f"https_proxy={https_proxy}\n")
583+ if no_proxy:
584+ env.write(f"no_proxy={no_proxy}\n")
585+ else:
586+ try:
587+ os.unlink(ENVIRONMENT_FILE)
588+ except FileNotFoundError:
589+ pass
590+
591+ def _config_process_suite(
592+ self, release, info, default_arches, default_suites
593+ ):
594+ out = {}
595+ for suite in (
596+ "%s%s" % (release, s) for s in info.get("suites", default_suites)
597+ ):
598+ out[suite] = {
599+ "useIconTheme": "Humanity",
600+ "dataPriority": CONFIG_PRIORITIES.get(
601+ suite.split("-")[-1], CONFIG_DEFAULT_PRIORITY
602+ ),
603+ "sections": ["main", "universe", "multiverse", "restricted"],
604+ "architectures": info.get("architectures", default_arches),
605+ }
606+ if suite != release:
607+ out[suite]["baseSuite"] = release
608+ try:
609+ # if we have 'released' : true, make the base suite immutable
610+ info["released"]
611+ out[release]["immutable"] = True
612+ except KeyError:
613+ pass
614+
615+ return out
616+
617+ def _write_config(self):
618+ mirror = self.model.config.get("mirror")
619+ hostname = self.model.config.get("hostname")
620+ config = self.model.config.get("config")
621+
622+ if not (mirror and hostname and config):
623+ logger.info("No config set. Can't continue.")
624+ return False
625+
626+ config_j = json.loads(config)
627+
628+ # Read the defaults
629+ default_arches = config_j["default"]["architectures"]
630+ default_suites = config_j["default"]["suites"]
631+ del config_j["default"]
632+
633+ o = CONFIG_HEADER
634+ o["ArchiveRoot"] = mirror
635+ o["MediaBaseUrl"] = f"{hostname}/media"
636+ o["HtmlBaseUrl"] = hostname
637+
638+ # Now do the rest
639+ for (release, ting) in config_j.items():
640+ if ting.get("oldSuite", False):
641+ CONFIG_HEADER["Oldsuites"].append(release)
642+ o["Suites"].update(
643+ self._config_process_suite(
644+ release, ting, default_arches, default_suites
645+ )
646+ )
647+
648+ OUTPUT_FILENAME.parent.mkdir(parents=True, exist_ok=True)
649+ with OUTPUT_FILENAME.open("w") as f:
650+ f.write(json.dumps(o, indent=4, sort_keys=True))
651+ shutil.chown(OUTPUT_FILENAME, user="ubuntu", group="ubuntu")
652+
653+ return True
654+
655+ def _symlink_systemd_units(self):
656+ any_changed = False
657+ unit_dir = Path("/etc/systemd/system")
658+ charm_dir = Path(self.charm_dir)
659+
660+ for unit in SYSTEMD_UNITS:
661+ dest = unit_dir / unit
662+ try:
663+ target = charm_dir / "units" / unit
664+ dest.symlink_to(target)
665+ any_changed = True
666+ logger.info(f"Symlinking {dest} → {target}")
667+ except FileExistsError:
668+ if dest.resolve() != target.resolve():
669+ logger.info(
670+ f"Target for {dest} has changed to {target}. Re-creating."
671+ )
672+ dest.unlink()
673+ dest.symlink_to(target)
674+ any_changed = True
675+
676+ if any_changed:
677+ subprocess.check_call(["systemctl", "daemon-reload"])
678+
679+ def _symlink_scripts(self):
680+ home = Path("~ubuntu").expanduser()
681+ scripts_dir = Path(self.charm_dir) / "scripts"
682+
683+ for script in scripts_dir.glob("*"):
684+ if script.is_dir():
685+ continue
686+ dest = home / script.name
687+ try:
688+ dest.symlink_to(script)
689+ logger.info(f"Symlinking {dest} → {script}")
690+ except FileExistsError:
691+ pass
692+
693+ def _set_up_rsync(self):
694+ any_written = False
695+ rsync_conf = Path("/etc/rsyncd.conf")
696+ rsync_d = Path("/etc/rsync-juju.d/")
697+ names_paths = (
698+ ("appstream", APPSTREAM_PUBLIC / "data"),
699+ ("www", APPSTREAM_PUBLIC),
700+ ("logs", APPSTREAM_BASE / "logs"),
701+ )
702+ rsync_template = dedent(
703+ """[{name}]
704+ path = {path}
705+ read only = yes
706+ list = yes
707+ uid = ubuntu
708+ gid = ubuntu
709+ chroot = false
710+ """
711+ )
712+
713+ try:
714+ # XXX: This means if we change the template, changes will not be
715+ # written to the output file.
716+ with rsync_conf.open("x") as f:
717+ f.write(
718+ dedent(
719+ """
720+ uid = nobody
721+ gid = nogroup
722+ pid file = /var/run/rsyncd.pid
723+ syslog facility = daemon
724+ socket options = SO_KEEPALIVE
725+ timeout = 7200
726+
727+ &include /etc/rsync-juju.d
728+ """
729+ )
730+ )
731+ except FileExistsError:
732+ pass
733+
734+ rsync_d.mkdir(parents=True, exist_ok=True)
735+
736+ for name, path in names_paths:
737+ path.mkdir(parents=True, exist_ok=True)
738+ try:
739+ # XXX: This means if we change the template above, changes will
740+ # not be written to the output file.
741+ conf = rsync_d / f"{name}.conf"
742+ with conf.open("x") as f:
743+ logger.info(f"Writing rsync config to {conf}")
744+ f.write(rsync_template.format(name=name, path=path))
745+ any_written = True
746+ except FileExistsError:
747+ pass
748+
749+ if any_written:
750+ subprocess.check_call(["systemctl", "restart", "rsync"])
751+ open_port(873)
752+
753+ def _ensure_set_up(self, event, more_to_do=False):
754+ if not self._stored.storage_attached:
755+ event.defer()
756+ self.unit.status = BlockedStatus(
757+ "Waiting for storage to become attached."
758+ )
759+ return False
760+
761+ self._install_packages(set(PACKAGES_TO_INSTALL))
762+ self._install_snaps(SNAPS_TO_INSTALL)
763+ self._set_up_proxy()
764+ self._symlink_systemd_units()
765+ self._symlink_scripts()
766+ self._set_up_rsync()
767+
768+ if not self._write_config():
769+ logger.info("Failed to write config. Blocked.")
770+ event.defer()
771+ self.unit.status = BlockedStatus(
772+ "Config not set. Make sure config, hostname and mirror are set."
773+ )
774+ return False
775+
776+ if not more_to_do:
777+ self.unit.status = ActiveStatus()
778+ return True
779+
780+ def _on_install(self, event):
781+ # We're not started yet, so don't set active. We'll do that after the
782+ # `start` hook.
783+ self._ensure_set_up(event, more_to_do=True)
784+
785+ def _on_config_changed(self, event):
786+ if "appstream-generator" not in self._stored.installed_snaps:
787+ event.defer()
788+ return
789+
790+ self._ensure_set_up(event)
791+
792+
793+if __name__ == "__main__":
794+ main(AppstreamGeneratorCharm)
795diff --git a/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/INSTALLER b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/INSTALLER
796new file mode 100644
797index 0000000..a1b589e
798--- /dev/null
799+++ b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/INSTALLER
800@@ -0,0 +1 @@
801+pip
802diff --git a/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/LICENSE.rst b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/LICENSE.rst
803new file mode 100644
804index 0000000..c37cae4
805--- /dev/null
806+++ b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/LICENSE.rst
807@@ -0,0 +1,28 @@
808+Copyright 2007 Pallets
809+
810+Redistribution and use in source and binary forms, with or without
811+modification, are permitted provided that the following conditions are
812+met:
813+
814+1. Redistributions of source code must retain the above copyright
815+ notice, this list of conditions and the following disclaimer.
816+
817+2. Redistributions in binary form must reproduce the above copyright
818+ notice, this list of conditions and the following disclaimer in the
819+ documentation and/or other materials provided with the distribution.
820+
821+3. Neither the name of the copyright holder nor the names of its
822+ contributors may be used to endorse or promote products derived from
823+ this software without specific prior written permission.
824+
825+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
826+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
827+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
828+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
829+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
830+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
831+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
832+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
833+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
834+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
835+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
836diff --git a/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/METADATA b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/METADATA
837new file mode 100644
838index 0000000..274e91c
839--- /dev/null
840+++ b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/METADATA
841@@ -0,0 +1,113 @@
842+Metadata-Version: 2.1
843+Name: Jinja2
844+Version: 3.0.2
845+Summary: A very fast and expressive template engine.
846+Home-page: https://palletsprojects.com/p/jinja/
847+Author: Armin Ronacher
848+Author-email: armin.ronacher@active-4.com
849+Maintainer: Pallets
850+Maintainer-email: contact@palletsprojects.com
851+License: BSD-3-Clause
852+Project-URL: Donate, https://palletsprojects.com/donate
853+Project-URL: Documentation, https://jinja.palletsprojects.com/
854+Project-URL: Changes, https://jinja.palletsprojects.com/changes/
855+Project-URL: Source Code, https://github.com/pallets/jinja/
856+Project-URL: Issue Tracker, https://github.com/pallets/jinja/issues/
857+Project-URL: Twitter, https://twitter.com/PalletsTeam
858+Project-URL: Chat, https://discord.gg/pallets
859+Platform: UNKNOWN
860+Classifier: Development Status :: 5 - Production/Stable
861+Classifier: Environment :: Web Environment
862+Classifier: Intended Audience :: Developers
863+Classifier: License :: OSI Approved :: BSD License
864+Classifier: Operating System :: OS Independent
865+Classifier: Programming Language :: Python
866+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
867+Classifier: Topic :: Text Processing :: Markup :: HTML
868+Requires-Python: >=3.6
869+Description-Content-Type: text/x-rst
870+License-File: LICENSE.rst
871+Requires-Dist: MarkupSafe (>=2.0)
872+Provides-Extra: i18n
873+Requires-Dist: Babel (>=2.7) ; extra == 'i18n'
874+
875+Jinja
876+=====
877+
878+Jinja is a fast, expressive, extensible templating engine. Special
879+placeholders in the template allow writing code similar to Python
880+syntax. Then the template is passed data to render the final document.
881+
882+It includes:
883+
884+- Template inheritance and inclusion.
885+- Define and import macros within templates.
886+- HTML templates can use autoescaping to prevent XSS from untrusted
887+ user input.
888+- A sandboxed environment can safely render untrusted templates.
889+- AsyncIO support for generating templates and calling async
890+ functions.
891+- I18N support with Babel.
892+- Templates are compiled to optimized Python code just-in-time and
893+ cached, or can be compiled ahead-of-time.
894+- Exceptions point to the correct line in templates to make debugging
895+ easier.
896+- Extensible filters, tests, functions, and even syntax.
897+
898+Jinja's philosophy is that while application logic belongs in Python if
899+possible, it shouldn't make the template designer's job difficult by
900+restricting functionality too much.
901+
902+
903+Installing
904+----------
905+
906+Install and update using `pip`_:
907+
908+.. code-block:: text
909+
910+ $ pip install -U Jinja2
911+
912+.. _pip: https://pip.pypa.io/en/stable/getting-started/
913+
914+
915+In A Nutshell
916+-------------
917+
918+.. code-block:: jinja
919+
920+ {% extends "base.html" %}
921+ {% block title %}Members{% endblock %}
922+ {% block content %}
923+ <ul>
924+ {% for user in users %}
925+ <li><a href="{{ user.url }}">{{ user.username }}</a></li>
926+ {% endfor %}
927+ </ul>
928+ {% endblock %}
929+
930+
931+Donate
932+------
933+
934+The Pallets organization develops and supports Jinja and other popular
935+packages. In order to grow the community of contributors and users, and
936+allow the maintainers to devote more time to the projects, `please
937+donate today`_.
938+
939+.. _please donate today: https://palletsprojects.com/donate
940+
941+
942+Links
943+-----
944+
945+- Documentation: https://jinja.palletsprojects.com/
946+- Changes: https://jinja.palletsprojects.com/changes/
947+- PyPI Releases: https://pypi.org/project/Jinja2/
948+- Source Code: https://github.com/pallets/jinja/
949+- Issue Tracker: https://github.com/pallets/jinja/issues/
950+- Website: https://palletsprojects.com/p/jinja/
951+- Twitter: https://twitter.com/PalletsTeam
952+- Chat: https://discord.gg/pallets
953+
954+
955diff --git a/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/RECORD b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/RECORD
956new file mode 100644
957index 0000000..7438108
958--- /dev/null
959+++ b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/RECORD
960@@ -0,0 +1,58 @@
961+Jinja2-3.0.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
962+Jinja2-3.0.2.dist-info/LICENSE.rst,sha256=O0nc7kEF6ze6wQ-vG-JgQI_oXSUrjp3y4JefweCUQ3s,1475
963+Jinja2-3.0.2.dist-info/METADATA,sha256=NxKriGP-LwQjFBZ4hjV696NkZMe3GPy3tE00McDQZZ0,3539
964+Jinja2-3.0.2.dist-info/RECORD,,
965+Jinja2-3.0.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
966+Jinja2-3.0.2.dist-info/entry_points.txt,sha256=Qy_DkVo6Xj_zzOtmErrATe8lHZhOqdjpt3e4JJAGyi8,61
967+Jinja2-3.0.2.dist-info/top_level.txt,sha256=PkeVWtLb3-CqjWi1fO29OCbj55EhX_chhKrCdrVe_zs,7
968+jinja2/__init__.py,sha256=w4sanG5_dogKC4J_2thsPN9tXzmily3fmXK8Pd46Q1Y,2205
969+jinja2/__pycache__/__init__.cpython-38.pyc,,
970+jinja2/__pycache__/_identifier.cpython-38.pyc,,
971+jinja2/__pycache__/async_utils.cpython-38.pyc,,
972+jinja2/__pycache__/bccache.cpython-38.pyc,,
973+jinja2/__pycache__/compiler.cpython-38.pyc,,
974+jinja2/__pycache__/constants.cpython-38.pyc,,
975+jinja2/__pycache__/debug.cpython-38.pyc,,
976+jinja2/__pycache__/defaults.cpython-38.pyc,,
977+jinja2/__pycache__/environment.cpython-38.pyc,,
978+jinja2/__pycache__/exceptions.cpython-38.pyc,,
979+jinja2/__pycache__/ext.cpython-38.pyc,,
980+jinja2/__pycache__/filters.cpython-38.pyc,,
981+jinja2/__pycache__/idtracking.cpython-38.pyc,,
982+jinja2/__pycache__/lexer.cpython-38.pyc,,
983+jinja2/__pycache__/loaders.cpython-38.pyc,,
984+jinja2/__pycache__/meta.cpython-38.pyc,,
985+jinja2/__pycache__/nativetypes.cpython-38.pyc,,
986+jinja2/__pycache__/nodes.cpython-38.pyc,,
987+jinja2/__pycache__/optimizer.cpython-38.pyc,,
988+jinja2/__pycache__/parser.cpython-38.pyc,,
989+jinja2/__pycache__/runtime.cpython-38.pyc,,
990+jinja2/__pycache__/sandbox.cpython-38.pyc,,
991+jinja2/__pycache__/tests.cpython-38.pyc,,
992+jinja2/__pycache__/utils.cpython-38.pyc,,
993+jinja2/__pycache__/visitor.cpython-38.pyc,,
994+jinja2/_identifier.py,sha256=EdgGJKi7O1yvr4yFlvqPNEqV6M1qHyQr8Gt8GmVTKVM,1775
995+jinja2/async_utils.py,sha256=bY2nCUfBA_4FSnNUsIsJgljBq3hACr6fzLi7LiyMTn8,1751
996+jinja2/bccache.py,sha256=v5rKAlYxIvfJEa0uGzAC6yCYSS3KuXT5Eqi-n9qvNi8,12670
997+jinja2/compiler.py,sha256=v7zKz-mgSYXmfXD9mRmi2BU0B6Z-1RGZmOXCrsPKzc0,72209
998+jinja2/constants.py,sha256=GMoFydBF_kdpaRKPoM5cl5MviquVRLVyZtfp5-16jg0,1433
999+jinja2/debug.py,sha256=uBmrsiwjYH5l14R9STn5mydOOyriBYol5lDGvEqAb3A,9238
1000+jinja2/defaults.py,sha256=boBcSw78h-lp20YbaXSJsqkAI2uN_mD_TtCydpeq5wU,1267
1001+jinja2/environment.py,sha256=T6U4be9mY1CUXXin_EQFwpvpFqCiryweGqzXGRYIoSA,61573
1002+jinja2/exceptions.py,sha256=ioHeHrWwCWNaXX1inHmHVblvc4haO7AXsjCp3GfWvx0,5071
1003+jinja2/ext.py,sha256=44SjDjeYkkxQTpmC2BetOTxEFMgQ42p2dfSwXmPFcSo,32122
1004+jinja2/filters.py,sha256=jusKTZbd0ddZMaibZkxMUVKNsOsaYtOq_Il8Imtx4BE,52609
1005+jinja2/idtracking.py,sha256=WekexMql3u5n3vDxFsQ_i8HW0j24AtjWTjrPBLWrHww,10721
1006+jinja2/lexer.py,sha256=qNEQqDQw_zO5EaH6rFQsER7Qwn2du0o22prB-TR11HE,29930
1007+jinja2/loaders.py,sha256=WGJNP5RQEGtuTABIK2tp1sWEy2kfe5ZFOY0ON0gi0LY,22477
1008+jinja2/meta.py,sha256=GNPEvifmSaU3CMxlbheBOZjeZ277HThOPUTf1RkppKQ,4396
1009+jinja2/nativetypes.py,sha256=62hvvsAxAj0YaxylOHoREYVogJ5JqOlJISgGY3OKd_o,3675
1010+jinja2/nodes.py,sha256=8zvzM5mYaylI-kbT6oI1DChxGH8dZkghWt1fVizy4Us,34605
1011+jinja2/optimizer.py,sha256=tHkMwXxfZkbfA1KmLcqmBMSaz7RLIvvItrJcPoXTyD8,1650
1012+jinja2/parser.py,sha256=kHnU8v92GwMYkfr0MVakWv8UlSf_kJPx8LUsgQMof70,39767
1013+jinja2/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1014+jinja2/runtime.py,sha256=wVRlkEmAgNU67AIQDqLvI6UkNLkzDqpLA-z4Mi3vl3g,35054
1015+jinja2/sandbox.py,sha256=-8zxR6TO9kUkciAVFsIKu8Oq-C7PTeYEdZ5TtA55-gw,14600
1016+jinja2/tests.py,sha256=Am5Z6Lmfr2XaH_npIfJJ8MdXtWsbLjMULZJulTAj30E,5905
1017+jinja2/utils.py,sha256=udQxWIKaq4QDCZiXN31ngKOaGGdaMA5fl0JMaM-F6fg,26971
1018+jinja2/visitor.py,sha256=ZmeLuTj66ic35-uFH-1m0EKXiw4ObDDb_WuE6h5vPFg,3572
1019diff --git a/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/WHEEL b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/WHEEL
1020new file mode 100644
1021index 0000000..5bad85f
1022--- /dev/null
1023+++ b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/WHEEL
1024@@ -0,0 +1,5 @@
1025+Wheel-Version: 1.0
1026+Generator: bdist_wheel (0.37.0)
1027+Root-Is-Purelib: true
1028+Tag: py3-none-any
1029+
1030diff --git a/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/entry_points.txt b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/entry_points.txt
1031new file mode 100644
1032index 0000000..3619483
1033--- /dev/null
1034+++ b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/entry_points.txt
1035@@ -0,0 +1,3 @@
1036+[babel.extractors]
1037+jinja2 = jinja2.ext:babel_extract [i18n]
1038+
1039diff --git a/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/top_level.txt b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/top_level.txt
1040new file mode 100644
1041index 0000000..7f7afbf
1042--- /dev/null
1043+++ b/charms/appstream-generator/venv/Jinja2-3.0.2.dist-info/top_level.txt
1044@@ -0,0 +1 @@
1045+jinja2
1046diff --git a/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/INSTALLER b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/INSTALLER
1047new file mode 100644
1048index 0000000..a1b589e
1049--- /dev/null
1050+++ b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/INSTALLER
1051@@ -0,0 +1 @@
1052+pip
1053diff --git a/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/LICENSE.rst b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/LICENSE.rst
1054new file mode 100644
1055index 0000000..9d227a0
1056--- /dev/null
1057+++ b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/LICENSE.rst
1058@@ -0,0 +1,28 @@
1059+Copyright 2010 Pallets
1060+
1061+Redistribution and use in source and binary forms, with or without
1062+modification, are permitted provided that the following conditions are
1063+met:
1064+
1065+1. Redistributions of source code must retain the above copyright
1066+ notice, this list of conditions and the following disclaimer.
1067+
1068+2. Redistributions in binary form must reproduce the above copyright
1069+ notice, this list of conditions and the following disclaimer in the
1070+ documentation and/or other materials provided with the distribution.
1071+
1072+3. Neither the name of the copyright holder nor the names of its
1073+ contributors may be used to endorse or promote products derived from
1074+ this software without specific prior written permission.
1075+
1076+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
1077+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
1078+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
1079+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
1080+HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
1081+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
1082+TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
1083+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
1084+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
1085+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
1086+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
1087diff --git a/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/METADATA b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/METADATA
1088new file mode 100644
1089index 0000000..ef44e2b
1090--- /dev/null
1091+++ b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/METADATA
1092@@ -0,0 +1,100 @@
1093+Metadata-Version: 2.1
1094+Name: MarkupSafe
1095+Version: 2.0.1
1096+Summary: Safely add untrusted strings to HTML/XML markup.
1097+Home-page: https://palletsprojects.com/p/markupsafe/
1098+Author: Armin Ronacher
1099+Author-email: armin.ronacher@active-4.com
1100+Maintainer: Pallets
1101+Maintainer-email: contact@palletsprojects.com
1102+License: BSD-3-Clause
1103+Project-URL: Donate, https://palletsprojects.com/donate
1104+Project-URL: Documentation, https://markupsafe.palletsprojects.com/
1105+Project-URL: Changes, https://markupsafe.palletsprojects.com/changes/
1106+Project-URL: Source Code, https://github.com/pallets/markupsafe/
1107+Project-URL: Issue Tracker, https://github.com/pallets/markupsafe/issues/
1108+Project-URL: Twitter, https://twitter.com/PalletsTeam
1109+Project-URL: Chat, https://discord.gg/pallets
1110+Platform: UNKNOWN
1111+Classifier: Development Status :: 5 - Production/Stable
1112+Classifier: Environment :: Web Environment
1113+Classifier: Intended Audience :: Developers
1114+Classifier: License :: OSI Approved :: BSD License
1115+Classifier: Operating System :: OS Independent
1116+Classifier: Programming Language :: Python
1117+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content
1118+Classifier: Topic :: Text Processing :: Markup :: HTML
1119+Requires-Python: >=3.6
1120+Description-Content-Type: text/x-rst
1121+
1122+MarkupSafe
1123+==========
1124+
1125+MarkupSafe implements a text object that escapes characters so it is
1126+safe to use in HTML and XML. Characters that have special meanings are
1127+replaced so that they display as the actual characters. This mitigates
1128+injection attacks, meaning untrusted user input can safely be displayed
1129+on a page.
1130+
1131+
1132+Installing
1133+----------
1134+
1135+Install and update using `pip`_:
1136+
1137+.. code-block:: text
1138+
1139+ pip install -U MarkupSafe
1140+
1141+.. _pip: https://pip.pypa.io/en/stable/quickstart/
1142+
1143+
1144+Examples
1145+--------
1146+
1147+.. code-block:: pycon
1148+
1149+ >>> from markupsafe import Markup, escape
1150+
1151+ >>> # escape replaces special characters and wraps in Markup
1152+ >>> escape("<script>alert(document.cookie);</script>")
1153+ Markup('&lt;script&gt;alert(document.cookie);&lt;/script&gt;')
1154+
1155+ >>> # wrap in Markup to mark text "safe" and prevent escaping
1156+ >>> Markup("<strong>Hello</strong>")
1157+ Markup('<strong>hello</strong>')
1158+
1159+ >>> escape(Markup("<strong>Hello</strong>"))
1160+ Markup('<strong>hello</strong>')
1161+
1162+ >>> # Markup is a str subclass
1163+ >>> # methods and operators escape their arguments
1164+ >>> template = Markup("Hello <em>{name}</em>")
1165+ >>> template.format(name='"World"')
1166+ Markup('Hello <em>&#34;World&#34;</em>')
1167+
1168+
1169+Donate
1170+------
1171+
1172+The Pallets organization develops and supports MarkupSafe and other
1173+popular packages. In order to grow the community of contributors and
1174+users, and allow the maintainers to devote more time to the projects,
1175+`please donate today`_.
1176+
1177+.. _please donate today: https://palletsprojects.com/donate
1178+
1179+
1180+Links
1181+-----
1182+
1183+- Documentation: https://markupsafe.palletsprojects.com/
1184+- Changes: https://markupsafe.palletsprojects.com/changes/
1185+- PyPI Releases: https://pypi.org/project/MarkupSafe/
1186+- Source Code: https://github.com/pallets/markupsafe/
1187+- Issue Tracker: https://github.com/pallets/markupsafe/issues/
1188+- Website: https://palletsprojects.com/p/markupsafe/
1189+- Twitter: https://twitter.com/PalletsTeam
1190+- Chat: https://discord.gg/pallets
1191+
1192+
1193diff --git a/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/RECORD b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/RECORD
1194new file mode 100644
1195index 0000000..768efa3
1196--- /dev/null
1197+++ b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/RECORD
1198@@ -0,0 +1,14 @@
1199+MarkupSafe-2.0.1.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
1200+MarkupSafe-2.0.1.dist-info/LICENSE.rst,sha256=SJqOEQhQntmKN7uYPhHg9-HTHwvY-Zp5yESOf_N9B-o,1475
1201+MarkupSafe-2.0.1.dist-info/METADATA,sha256=FmPpxBdaqCCjF-XKqoxeEzqAzhetQnrkkSsd3V3X-Jc,3211
1202+MarkupSafe-2.0.1.dist-info/RECORD,,
1203+MarkupSafe-2.0.1.dist-info/WHEEL,sha256=RIeRBYNNiNK3sXfnenIjXDrR2Tzyz05xCMpKF2hJ1iA,111
1204+MarkupSafe-2.0.1.dist-info/top_level.txt,sha256=qy0Plje5IJuvsCBjejJyhDCjEAdcDLK_2agVcex8Z6U,11
1205+markupsafe/__init__.py,sha256=9Tez4UIlI7J6_sQcUFK1dKniT_b_8YefpGIyYJ3Sr2Q,8923
1206+markupsafe/__pycache__/__init__.cpython-38.pyc,,
1207+markupsafe/__pycache__/_native.cpython-38.pyc,,
1208+markupsafe/_native.py,sha256=GTKEV-bWgZuSjklhMHOYRHU9k0DMewTf5mVEZfkbuns,1986
1209+markupsafe/_speedups.c,sha256=CDDtwaV21D2nYtypnMQzxvvpZpcTvIs8OZ6KDa1g4t0,7400
1210+markupsafe/_speedups.cpython-38-x86_64-linux-gnu.so,sha256=rHWt9NIO4rSSHq0XYjwkiar1C-p7RB198xLDI5Aji0E,53208
1211+markupsafe/_speedups.pyi,sha256=vfMCsOgbAXRNLUXkyuyonG8uEWKYU4PDqNuMaDELAYw,229
1212+markupsafe/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
1213diff --git a/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/WHEEL b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/WHEEL
1214new file mode 100644
1215index 0000000..b1fcc33
1216--- /dev/null
1217+++ b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/WHEEL
1218@@ -0,0 +1,5 @@
1219+Wheel-Version: 1.0
1220+Generator: bdist_wheel (0.36.2)
1221+Root-Is-Purelib: false
1222+Tag: cp38-cp38-manylinux2010_x86_64
1223+
1224diff --git a/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/top_level.txt b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/top_level.txt
1225new file mode 100644
1226index 0000000..75bf729
1227--- /dev/null
1228+++ b/charms/appstream-generator/venv/MarkupSafe-2.0.1.dist-info/top_level.txt
1229@@ -0,0 +1 @@
1230+markupsafe
1231diff --git a/charms/appstream-generator/venv/PyYAML-6.0.dist-info/INSTALLER b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/INSTALLER
1232new file mode 100644
1233index 0000000..a1b589e
1234--- /dev/null
1235+++ b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/INSTALLER
1236@@ -0,0 +1 @@
1237+pip
1238diff --git a/charms/appstream-generator/venv/PyYAML-6.0.dist-info/LICENSE b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/LICENSE
1239new file mode 100644
1240index 0000000..2f1b8e1
1241--- /dev/null
1242+++ b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/LICENSE
1243@@ -0,0 +1,20 @@
1244+Copyright (c) 2017-2021 Ingy döt Net
1245+Copyright (c) 2006-2016 Kirill Simonov
1246+
1247+Permission is hereby granted, free of charge, to any person obtaining a copy of
1248+this software and associated documentation files (the "Software"), to deal in
1249+the Software without restriction, including without limitation the rights to
1250+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
1251+of the Software, and to permit persons to whom the Software is furnished to do
1252+so, subject to the following conditions:
1253+
1254+The above copyright notice and this permission notice shall be included in all
1255+copies or substantial portions of the Software.
1256+
1257+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
1258+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
1259+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
1260+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
1261+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
1262+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
1263+SOFTWARE.
1264diff --git a/charms/appstream-generator/venv/PyYAML-6.0.dist-info/METADATA b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/METADATA
1265new file mode 100644
1266index 0000000..9a91076
1267--- /dev/null
1268+++ b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/METADATA
1269@@ -0,0 +1,46 @@
1270+Metadata-Version: 2.1
1271+Name: PyYAML
1272+Version: 6.0
1273+Summary: YAML parser and emitter for Python
1274+Home-page: https://pyyaml.org/
1275+Author: Kirill Simonov
1276+Author-email: xi@resolvent.net
1277+License: MIT
1278+Download-URL: https://pypi.org/project/PyYAML/
1279+Project-URL: Bug Tracker, https://github.com/yaml/pyyaml/issues
1280+Project-URL: CI, https://github.com/yaml/pyyaml/actions
1281+Project-URL: Documentation, https://pyyaml.org/wiki/PyYAMLDocumentation
1282+Project-URL: Mailing lists, http://lists.sourceforge.net/lists/listinfo/yaml-core
1283+Project-URL: Source Code, https://github.com/yaml/pyyaml
1284+Platform: Any
1285+Classifier: Development Status :: 5 - Production/Stable
1286+Classifier: Intended Audience :: Developers
1287+Classifier: License :: OSI Approved :: MIT License
1288+Classifier: Operating System :: OS Independent
1289+Classifier: Programming Language :: Cython
1290+Classifier: Programming Language :: Python
1291+Classifier: Programming Language :: Python :: 3
1292+Classifier: Programming Language :: Python :: 3.6
1293+Classifier: Programming Language :: Python :: 3.7
1294+Classifier: Programming Language :: Python :: 3.8
1295+Classifier: Programming Language :: Python :: 3.9
1296+Classifier: Programming Language :: Python :: 3.10
1297+Classifier: Programming Language :: Python :: Implementation :: CPython
1298+Classifier: Programming Language :: Python :: Implementation :: PyPy
1299+Classifier: Topic :: Software Development :: Libraries :: Python Modules
1300+Classifier: Topic :: Text Processing :: Markup
1301+Requires-Python: >=3.6
1302+License-File: LICENSE
1303+
1304+YAML is a data serialization format designed for human readability
1305+and interaction with scripting languages. PyYAML is a YAML parser
1306+and emitter for Python.
1307+
1308+PyYAML features a complete YAML 1.1 parser, Unicode support, pickle
1309+support, capable extension API, and sensible error messages. PyYAML
1310+supports standard YAML tags and provides Python-specific tags that
1311+allow to represent an arbitrary Python object.
1312+
1313+PyYAML is applicable for a broad range of tasks from complex
1314+configuration files to object serialization and persistence.
1315+
1316diff --git a/charms/appstream-generator/venv/PyYAML-6.0.dist-info/RECORD b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/RECORD
1317new file mode 100644
1318index 0000000..7105b9f
1319--- /dev/null
1320+++ b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/RECORD
1321@@ -0,0 +1,43 @@
1322+PyYAML-6.0.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
1323+PyYAML-6.0.dist-info/LICENSE,sha256=jTko-dxEkP1jVwfLiOsmvXZBAqcoKVQwfT5RZ6V36KQ,1101
1324+PyYAML-6.0.dist-info/METADATA,sha256=QmHx9kGp_0yezQCXYaft4eEFeJ6W4oyFfYwHDLP1kdg,2006
1325+PyYAML-6.0.dist-info/RECORD,,
1326+PyYAML-6.0.dist-info/WHEEL,sha256=RiwktpmF40OphKd3_aIG01PzIOQlJ7dpBn3cFSc9vak,217
1327+PyYAML-6.0.dist-info/top_level.txt,sha256=rpj0IVMTisAjh_1vG3Ccf9v5jpCQwAz6cD1IVU5ZdhQ,11
1328+_yaml/__init__.py,sha256=04Ae_5osxahpJHa3XBZUAf4wi6XX32gR8D6X6p64GEA,1402
1329+_yaml/__pycache__/__init__.cpython-38.pyc,,
1330+yaml/__init__.py,sha256=NDS7S8XgA72-hY6LRmGzUWTPvzGzjWVrWk-OGA-77AA,12309
1331+yaml/__pycache__/__init__.cpython-38.pyc,,
1332+yaml/__pycache__/composer.cpython-38.pyc,,
1333+yaml/__pycache__/constructor.cpython-38.pyc,,
1334+yaml/__pycache__/cyaml.cpython-38.pyc,,
1335+yaml/__pycache__/dumper.cpython-38.pyc,,
1336+yaml/__pycache__/emitter.cpython-38.pyc,,
1337+yaml/__pycache__/error.cpython-38.pyc,,
1338+yaml/__pycache__/events.cpython-38.pyc,,
1339+yaml/__pycache__/loader.cpython-38.pyc,,
1340+yaml/__pycache__/nodes.cpython-38.pyc,,
1341+yaml/__pycache__/parser.cpython-38.pyc,,
1342+yaml/__pycache__/reader.cpython-38.pyc,,
1343+yaml/__pycache__/representer.cpython-38.pyc,,
1344+yaml/__pycache__/resolver.cpython-38.pyc,,
1345+yaml/__pycache__/scanner.cpython-38.pyc,,
1346+yaml/__pycache__/serializer.cpython-38.pyc,,
1347+yaml/__pycache__/tokens.cpython-38.pyc,,
1348+yaml/_yaml.cpython-38-x86_64-linux-gnu.so,sha256=lMaKSmQZy3WNZSmmU0Wg5Y5ZAs-HR5vItyGVUIsp8Rg,2847784
1349+yaml/composer.py,sha256=_Ko30Wr6eDWUeUpauUGT3Lcg9QPBnOPVlTnIMRGJ9FM,4883
1350+yaml/constructor.py,sha256=kNgkfaeLUkwQYY_Q6Ff1Tz2XVw_pG1xVE9Ak7z-viLA,28639
1351+yaml/cyaml.py,sha256=6ZrAG9fAYvdVe2FK_w0hmXoG7ZYsoYUwapG8CiC72H0,3851
1352+yaml/dumper.py,sha256=PLctZlYwZLp7XmeUdwRuv4nYOZ2UBnDIUy8-lKfLF-o,2837
1353+yaml/emitter.py,sha256=jghtaU7eFwg31bG0B7RZea_29Adi9CKmXq_QjgQpCkQ,43006
1354+yaml/error.py,sha256=Ah9z-toHJUbE9j-M8YpxgSRM5CgLCcwVzJgLLRF2Fxo,2533
1355+yaml/events.py,sha256=50_TksgQiE4up-lKo_V-nBy-tAIxkIPQxY5qDhKCeHw,2445
1356+yaml/loader.py,sha256=UVa-zIqmkFSCIYq_PgSGm4NSJttHY2Rf_zQ4_b1fHN0,2061
1357+yaml/nodes.py,sha256=gPKNj8pKCdh2d4gr3gIYINnPOaOxGhJAUiYhGRnPE84,1440
1358+yaml/parser.py,sha256=ilWp5vvgoHFGzvOZDItFoGjD6D42nhlZrZyjAwa0oJo,25495
1359+yaml/reader.py,sha256=0dmzirOiDG4Xo41RnuQS7K9rkY3xjHiVasfDMNTqCNw,6794
1360+yaml/representer.py,sha256=IuWP-cAW9sHKEnS0gCqSa894k1Bg4cgTxaDwIcbRQ-Y,14190
1361+yaml/resolver.py,sha256=9L-VYfm4mWHxUD1Vg4X7rjDRK_7VZd6b92wzq7Y2IKY,9004
1362+yaml/scanner.py,sha256=YEM3iLZSaQwXcQRg2l2R4MdT0zGP2F9eHkKGKnHyWQY,51279
1363+yaml/serializer.py,sha256=ChuFgmhU01hj4xgI8GaKv6vfM2Bujwa9i7d2FAHj7cA,4165
1364+yaml/tokens.py,sha256=lTQIzSVw8Mg9wv459-TjiOQe6wVziqaRlqX2_89rp54,2573
1365diff --git a/charms/appstream-generator/venv/PyYAML-6.0.dist-info/WHEEL b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/WHEEL
1366new file mode 100644
1367index 0000000..34ce4b8
1368--- /dev/null
1369+++ b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/WHEEL
1370@@ -0,0 +1,8 @@
1371+Wheel-Version: 1.0
1372+Generator: bdist_wheel (0.37.0)
1373+Root-Is-Purelib: false
1374+Tag: cp38-cp38-manylinux_2_5_x86_64
1375+Tag: cp38-cp38-manylinux1_x86_64
1376+Tag: cp38-cp38-manylinux_2_12_x86_64
1377+Tag: cp38-cp38-manylinux2010_x86_64
1378+
1379diff --git a/charms/appstream-generator/venv/PyYAML-6.0.dist-info/top_level.txt b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/top_level.txt
1380new file mode 100644
1381index 0000000..e6475e9
1382--- /dev/null
1383+++ b/charms/appstream-generator/venv/PyYAML-6.0.dist-info/top_level.txt
1384@@ -0,0 +1,2 @@
1385+_yaml
1386+yaml
1387diff --git a/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/INSTALLER b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/INSTALLER
1388new file mode 100644
1389index 0000000..a1b589e
1390--- /dev/null
1391+++ b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/INSTALLER
1392@@ -0,0 +1 @@
1393+pip
1394diff --git a/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/METADATA b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/METADATA
1395new file mode 100644
1396index 0000000..a5f1fcd
1397--- /dev/null
1398+++ b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/METADATA
1399@@ -0,0 +1,30 @@
1400+Metadata-Version: 2.1
1401+Name: Tempita
1402+Version: 0.5.2
1403+Summary: A very small text templating language
1404+Home-page: http://pythonpaste.org/tempita/
1405+Author: Ian Bicking
1406+Author-email: ianb@colorstudy.com
1407+License: MIT
1408+Keywords: templating template language html
1409+Platform: UNKNOWN
1410+Classifier: Development Status :: 4 - Beta
1411+Classifier: Intended Audience :: Developers
1412+Classifier: License :: OSI Approved :: MIT License
1413+Classifier: Topic :: Text Processing
1414+Classifier: Programming Language :: Python :: 2
1415+Classifier: Programming Language :: Python :: 3
1416+
1417+Tempita is a small templating language for text substitution.
1418+
1419+This isn't meant to be the Next Big Thing in templating; it's just a
1420+handy little templating language for when your project outgrows
1421+``string.Template`` or ``%`` substitution. It's small, it embeds
1422+Python in strings, and it doesn't do much else.
1423+
1424+You can read about the `language
1425+<http://pythonpaste.org/tempita/#the-language>`_, the `interface
1426+<http://pythonpaste.org/tempita/#the-interface>`_, and there's nothing
1427+more to learn about it.
1428+
1429+
1430diff --git a/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/RECORD b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/RECORD
1431new file mode 100644
1432index 0000000..f74b6b1
1433--- /dev/null
1434+++ b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/RECORD
1435@@ -0,0 +1,14 @@
1436+Tempita-0.5.2.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
1437+Tempita-0.5.2.dist-info/METADATA,sha256=WDCnE3v--BhabjMdVq9G-O_kmuimR1grwKxXtGdMibI,1048
1438+Tempita-0.5.2.dist-info/RECORD,,
1439+Tempita-0.5.2.dist-info/WHEEL,sha256=ewwEueio1C2XeHTvT17n8dZUJgOvyCWCt0WVNLClP9o,92
1440+Tempita-0.5.2.dist-info/top_level.txt,sha256=2y9rvt_84XtP01ieixzzLnKmE9uWXFZteDjBnAXpDow,8
1441+Tempita-0.5.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
1442+tempita/__init__.py,sha256=MOpudct6XWwEKdFsS-t-QBCkrhJzaRf8UYQO2X58HLs,39822
1443+tempita/__main__.py,sha256=odLX23WPEN1ld-BWsAUHhT-npHpedjL9d_B0lijyNwk,49
1444+tempita/__pycache__/__init__.cpython-38.pyc,,
1445+tempita/__pycache__/__main__.cpython-38.pyc,,
1446+tempita/__pycache__/_looper.cpython-38.pyc,,
1447+tempita/__pycache__/compat3.cpython-38.pyc,,
1448+tempita/_looper.py,sha256=-CLh64iZ6c1czHxchvibTyOyMm7_mS81oleO6FBwcMg,4164
1449+tempita/compat3.py,sha256=y4HPrDVYmij84_bHi3Pk1A02SvvTmm98eodpWgxzH20,838
1450diff --git a/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/WHEEL b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/WHEEL
1451new file mode 100644
1452index 0000000..5bad85f
1453--- /dev/null
1454+++ b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/WHEEL
1455@@ -0,0 +1,5 @@
1456+Wheel-Version: 1.0
1457+Generator: bdist_wheel (0.37.0)
1458+Root-Is-Purelib: true
1459+Tag: py3-none-any
1460+
1461diff --git a/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/top_level.txt b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/top_level.txt
1462new file mode 100644
1463index 0000000..eddfc48
1464--- /dev/null
1465+++ b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/top_level.txt
1466@@ -0,0 +1 @@
1467+tempita
1468diff --git a/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/zip-safe b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/zip-safe
1469new file mode 100644
1470index 0000000..8b13789
1471--- /dev/null
1472+++ b/charms/appstream-generator/venv/Tempita-0.5.2.dist-info/zip-safe
1473@@ -0,0 +1 @@
1474+
1475diff --git a/charms/appstream-generator/venv/__pycache__/six.cpython-38.pyc b/charms/appstream-generator/venv/__pycache__/six.cpython-38.pyc
1476new file mode 100644
1477index 0000000..07e38cc
1478Binary files /dev/null and b/charms/appstream-generator/venv/__pycache__/six.cpython-38.pyc differ
1479diff --git a/charms/appstream-generator/venv/_yaml/__init__.py b/charms/appstream-generator/venv/_yaml/__init__.py
1480new file mode 100644
1481index 0000000..7baa8c4
1482--- /dev/null
1483+++ b/charms/appstream-generator/venv/_yaml/__init__.py
1484@@ -0,0 +1,33 @@
1485+# This is a stub package designed to roughly emulate the _yaml
1486+# extension module, which previously existed as a standalone module
1487+# and has been moved into the `yaml` package namespace.
1488+# It does not perfectly mimic its old counterpart, but should get
1489+# close enough for anyone who's relying on it even when they shouldn't.
1490+import yaml
1491+
1492+# in some circumstances, the yaml module we imoprted may be from a different version, so we need
1493+# to tread carefully when poking at it here (it may not have the attributes we expect)
1494+if not getattr(yaml, '__with_libyaml__', False):
1495+ from sys import version_info
1496+
1497+ exc = ModuleNotFoundError if version_info >= (3, 6) else ImportError
1498+ raise exc("No module named '_yaml'")
1499+else:
1500+ from yaml._yaml import *
1501+ import warnings
1502+ warnings.warn(
1503+ 'The _yaml extension module is now located at yaml._yaml'
1504+ ' and its location is subject to change. To use the'
1505+ ' LibYAML-based parser and emitter, import from `yaml`:'
1506+ ' `from yaml import CLoader as Loader, CDumper as Dumper`.',
1507+ DeprecationWarning
1508+ )
1509+ del warnings
1510+ # Don't `del yaml` here because yaml is actually an existing
1511+ # namespace member of _yaml.
1512+
1513+__name__ = '_yaml'
1514+# If the module is top-level (i.e. not a part of any specific package)
1515+# then the attribute should be set to ''.
1516+# https://docs.python.org/3.8/library/types.html
1517+__package__ = ''
1518diff --git a/charms/appstream-generator/venv/_yaml/__pycache__/__init__.cpython-38.pyc b/charms/appstream-generator/venv/_yaml/__pycache__/__init__.cpython-38.pyc
1519new file mode 100644
1520index 0000000..4ea8a2e
1521Binary files /dev/null and b/charms/appstream-generator/venv/_yaml/__pycache__/__init__.cpython-38.pyc differ
1522diff --git a/charms/appstream-generator/venv/bin/charmsupport b/charms/appstream-generator/venv/bin/charmsupport
1523new file mode 100755
1524index 0000000..15759e2
1525--- /dev/null
1526+++ b/charms/appstream-generator/venv/bin/charmsupport
1527@@ -0,0 +1,31 @@
1528+#!/usr/bin/python3
1529+
1530+import argparse
1531+from charmhelpers.contrib.charmsupport import execd
1532+
1533+
1534+def run_execd(args):
1535+ execd.execd_run(args.module, args.dir, die_on_error=True)
1536+
1537+
1538+def parse_args():
1539+ parser = argparse.ArgumentParser(description='Perform common charm tasks')
1540+ subparsers = parser.add_subparsers(help='Commands')
1541+
1542+ execd_parser = subparsers.add_parser('execd',
1543+ help='Execute a directory of commands')
1544+ execd_parser.add_argument('--module', default='charm-pre-install',
1545+ help='module to run (default: charm-pre-install)')
1546+ execd_parser.add_argument('--dir',
1547+ help="Override the exec.d directory path")
1548+ execd_parser.set_defaults(func=run_execd)
1549+
1550+ return parser.parse_args()
1551+
1552+
1553+def main():
1554+ arguments = parse_args()
1555+ arguments.func(arguments)
1556+
1557+if __name__ == '__main__':
1558+ exit(main())
1559diff --git a/charms/appstream-generator/venv/bin/chlp b/charms/appstream-generator/venv/bin/chlp
1560new file mode 100755
1561index 0000000..6bf2d2b
1562--- /dev/null
1563+++ b/charms/appstream-generator/venv/bin/chlp
1564@@ -0,0 +1,8 @@
1565+#!/usr/bin/python3
1566+
1567+from charmhelpers.cli import cmdline
1568+from charmhelpers.cli.commands import *
1569+
1570+
1571+if __name__ == '__main__':
1572+ cmdline.run()
1573diff --git a/charms/appstream-generator/venv/bin/netaddr b/charms/appstream-generator/venv/bin/netaddr
1574new file mode 100755
1575index 0000000..f2faba5
1576--- /dev/null
1577+++ b/charms/appstream-generator/venv/bin/netaddr
1578@@ -0,0 +1,8 @@
1579+#!/usr/bin/python3
1580+# -*- coding: utf-8 -*-
1581+import re
1582+import sys
1583+from netaddr.cli import main
1584+if __name__ == '__main__':
1585+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
1586+ sys.exit(main())
1587diff --git a/charms/appstream-generator/venv/bin/pbr b/charms/appstream-generator/venv/bin/pbr
1588new file mode 100755
1589index 0000000..730fb39
1590--- /dev/null
1591+++ b/charms/appstream-generator/venv/bin/pbr
1592@@ -0,0 +1,8 @@
1593+#!/usr/bin/python3
1594+# -*- coding: utf-8 -*-
1595+import re
1596+import sys
1597+from pbr.cmd.main import main
1598+if __name__ == '__main__':
1599+ sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
1600+ sys.exit(main())
1601diff --git a/charms/appstream-generator/venv/bin/salt-call b/charms/appstream-generator/venv/bin/salt-call
1602new file mode 100755
1603index 0000000..d641b0a
1604--- /dev/null
1605+++ b/charms/appstream-generator/venv/bin/salt-call
1606@@ -0,0 +1,11 @@
1607+#!/usr/bin/python3
1608+'''
1609+Directly call a salt command in the modules, does not require a running salt
1610+minion to run.
1611+'''
1612+
1613+from salt.scripts import salt_call
1614+
1615+
1616+if __name__ == '__main__':
1617+ salt_call()
1618diff --git a/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/AUTHORS b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/AUTHORS
1619new file mode 100644
1620index 0000000..41e552d
1621--- /dev/null
1622+++ b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/AUTHORS
1623@@ -0,0 +1,222 @@
1624+Adam Collard <adam.collard@canonical.com>
1625+Adam Dyess <adam.dyess@canonical.com>
1626+Adam Dyess <adyess@gmail.com>
1627+Adam Gandelman <adamg@canonical.com>
1628+Adam Gandelman <adamg@ubuntu.com>
1629+Adam Israel <adam.israel@gmail.com>
1630+Alex Kavanagh <567675+ajkavanagh@users.noreply.github.com>
1631+Alex Kavanagh <ajkavanagh@users.noreply.github.com>
1632+Alex Kavanagh <alex.kavanagh@canonical.com>
1633+Alex Kavanagh <alex@ajkavanagh.co.uk>
1634+Alexandros Soumplis <a.soumplis@gmail.com>
1635+Alvaro Uría <alvaro.uria@canonical.com>
1636+Andreas Hasenack <andreas@canonical.com>
1637+Andres Rodriguez <andreserl@ubuntu.com>
1638+Andrew McLeod <andrew.mcleod@canonical.com>
1639+Andrew Wilkins <andrew.wilkins@canonical.com>
1640+Ante Karamatic <ante.karamatic@canonical.com>
1641+Ante Karamatic <ivoks@ubuntu.com>
1642+Anton Skriptsov anton.skriptsov@nexenta.com <>
1643+Antoni Segura Puimedon <toni@midokura.com>
1644+Arif Ali <arif-ali@users.noreply.github.com>
1645+Aurelien Lourot <aurelien.lourot@canonical.com>
1646+Aurelien Lourot <aurelien.lourot@gmail.com>
1647+Barry Price <barryprice@users.noreply.github.com>
1648+Beaujolais ! <axino@canonical.com>
1649+Ben Kaehne <ben.kaehne@canonical.com>
1650+Benjamin Saller <benjamin.saller@canonical.com>
1651+Bilal Baqar <bbaqar@plumgrid.com>
1652+Billy Olsen <billy.olsen@canonical.com>
1653+Billy Olsen <wolsen@users.noreply.github.com>
1654+Bjorn Tillenius <bjorn@tillenius.me>
1655+Brad Marshall <brad.marshall@canonical.com>
1656+Brett <19863984+brettmilford@users.noreply.github.com>
1657+Brett Milford <brettmilford@gmail.com>
1658+Brian Murray <brian@canonical.com>
1659+Brian Murray <brian@murraytwins.com>
1660+Brian Murray <brian@ubuntu.com>
1661+Casey Marshall <casey.marshall@canonical.com>
1662+Chad Smith <chad.smith@canonical.com>
1663+Charles Butler <charles.butler@canonical.com>
1664+Charles Butler <chuck@dasroot.net>
1665+Chris Holcombe <chris.holcombe@canonical.com>
1666+Chris J Arges <chris.j.arges@canonical.com>
1667+Chris Johnston <chrisjohnston@ubuntu.com>
1668+Chris Johnston <cjohnston1158@users.noreply.github.com>
1669+Chris MacNaughton <chmacnaughton@gmail.com>
1670+Chris MacNaughton <chris.macnaughton@canonical.com>
1671+Chris MacNaughton <chris@centaurisolutions.nl>
1672+Chris Sanders <sanders.chris@gmail.com>
1673+Chris Stratford <chris.stratford@canonical.com>
1674+Christopher Glass <christopher.glass@canonical.com>
1675+Chuck Short <zulcss@ubuntu.com>
1676+Colin Watson <cjwatson@ubuntu.com>
1677+Corey Bryant <corey.bryant@canonical.com>
1678+Cory Benfield <cory.benfield@metaswitch.com>
1679+Cory Johns <cory.johns@canonical.com>
1680+Cory Johns <johnsca@gmail.com>
1681+Craige McWhirter <craige@mcwhirter.io>
1682+Dan Ryan <dan@danryan.co>
1683+Daniel Watkins <daniel.watkins@canonical.com>
1684+Dave Lawson <dave.lawson@canonical.com>
1685+David Ames <david.ames@canonical.com>
1686+David Ames <david@davam.net>
1687+David Britton <david.britton@canonical.com>
1688+David Della Vecchia <ddv@canonical.com>
1689+Denis Buliga <dbuliga@cloudbasesolutions.com>
1690+Diko Parvanov <42830406+dparv@users.noreply.github.com>
1691+Dmitrii Shcherbakov <dshcherb@users.noreply.github.com>
1692+Domas Monkus <domas.monkus@canonical.com>
1693+Drew Freiberger <drew.freiberger@canonical.com>
1694+Duyle Campos <duyleruan@hotmail.com>
1695+Edward Hope-Morley <edward.hope-morley@canonical.com>
1696+Edward Hope-Morley <opentastic@gmail.com>
1697+Erlon R. Cruz <erlon@canonical.com>
1698+Evan Dandrea <evan.dandrea@canonical.com>
1699+Felipe Reyes <felipe.reyes@canonical.com>
1700+Felipe Reyes <freyes@tty.cl>
1701+Francis Ginther <francis.ginther@canonical.com>
1702+Free Ekanayaka <free.ekanayaka@canonical.com>
1703+Frode Nordahl <frode.nordahl@canonical.com>
1704+Frode Nordahl <frode.nordahl@gmail.com>
1705+Gabor Meszaros gabor.meszaros@canonical.com <>
1706+Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
1707+Gabriel Angelo Sgarbi Cocenza <gabrielcocenza@gmail.com>
1708+Gareth Woolridge <gareth.woolridge@canonical.com>
1709+Geoffrey J. Teale <geoffrey.teale@canonical.com>
1710+George Kraft <cynerva@gmail.com>
1711+Guo Qiao (Joe) <guoqiao@gmail.com>
1712+Haw Loeung (hloeung) <haw.loeung@canonical.com>
1713+Haw Loeung <h.loeung+github@unixque.com>
1714+Haw Loeung <haw.loeung@canonical.com>
1715+Hemanth Nakkina <hemanth.nakkina@canonical.com>
1716+Hui Xiang <hui.xiang@canonical.com>
1717+Ian Booth <ian.booth@canonical.com>
1718+Ionut Balutoiu <ibalutoiu@cloudbasesolutions.com>
1719+Jacek Nykis <jacek.nykis@canonical.com>
1720+James Beedy <jamesbeedy@gmail.com>
1721+James Beedy <jbeedy@virt-test0>
1722+James Page <james.page@canonical.com>
1723+James Page <james.page@ubuntu.com>
1724+James Troup <james.troup@canonical.com>
1725+Jason Hobbs <jason.hobbs@gmail.com>
1726+Jian Wen <jian.wen@canonical.com>
1727+Joe Borg <cyborg101010@gmail.com>
1728+Joe Borg <joseph.borg@canonical.com>
1729+Jonathan Herlin <Jonte@jherlin.se>
1730+Jorge Niedbalski <jorge.niedbalski@canonical.com>
1731+Jorge Niedbalski R <jorge.niedbalski@canonical.com>
1732+Joseph Borg <joseph.borg@canonical.com>
1733+Joshua Powers <mrpowersj@gmail.com>
1734+Juan L. Negron <juan@ubuntu.com>
1735+JuanJo Ciarlante <jjo@canonical.com>
1736+Kapil Thangavelu <kapil@canonical.com>
1737+Kevin W Monroe <kevin.monroe@canonical.com>
1738+Kit Randel <kit.randel@canonical.com>
1739+Laurent Sesquès <sajoupa@users.noreply.github.com>
1740+Liam Young <liam.young@canonical.com>
1741+Liam Young <liam@landv.org.uk>
1742+Liam young <liam.young@canonical.com>
1743+Liang Chen liang.chen@canonical.com <>
1744+Lihui <lihuiguo@gmail.com>
1745+Linda Guo <linda.guo@canonical.com>
1746+Marco CEppi <marco@ceppi.net>
1747+Marco Ceppi <marco@T430>
1748+Marco Ceppi <marco@ceppi.net>
1749+Mario Splivalo <mario.splivalo@canonical.com>
1750+Marius Oprin <moprin@cloudbasesolutions.com>
1751+Martin Hilton <martin.hilton@canonical.com>
1752+Matt Bruzek <matthew.bruzek@canonical.com>
1753+Matt Bruzek matthew.bruzek@canonical.com <>
1754+Matt Rae <matt.rae@canonical.com>
1755+Matthew Bruzek <matthew.bruzek@canonical.com>
1756+Matthew Wedgwood <matthew.wedgwood@canonical.com>
1757+Matúš Košút <matuskosut@users.noreply.github.com>
1758+Merlijn Sebrechts <merlijn.sebrechts@gmail.com>
1759+Michael Nelson <michael.nelson@canonical.com>
1760+Michael Skalka <michael.skalka@canonical.com>
1761+Michał Sawicz <michal.sawicz@canonical.com>
1762+Narinder Gupta <narinder.gupta@canonical.com>
1763+Nathan Osman <nathan@quickmediasolutions.com>
1764+Nick Moffitt <nick.moffitt@canonical.com>
1765+Nicolas Bock <nicolasbock@gmail.com>
1766+Nicolas Pochet <npochet@automate-solutions.com>
1767+Nikolay Nikolaev <nicknickolaev@gmail.com>
1768+Nikolay Nikolaev <nikolay.nikolaev@canonical.com>
1769+Nikolay Vinogradov <32940265+nikolayvinogradov@users.noreply.github.com>
1770+Nobuto Murata <nobuto.murata@canonical.com>
1771+Oprin Marius <moprin@cloudbasesolutions.com>
1772+Paolo de Rosa <paolo.de.rosa@canonical.com>
1773+Patrick Hetu <patrick.hetu@gmail.com>
1774+Paul Collins <paul.collins@canonical.com>
1775+Paul Gear <paul.gear@canonical.com>
1776+Pedro Guimaraes <pedro.guimaraes@canonical.com>
1777+Pete Vander Giessen <petevg@gmail.com>
1778+Peter Sabaini <peter.sabaini@canonical.com>
1779+Peter Sabaini <peter@sabaini.at>
1780+Ponnuvel Palaniyappan <pponnuvel@gmail.com>
1781+Przemysław Lal <przemeklal@gmail.com>
1782+Przemysław Lal <przemyslaw.lal@canonical.com>
1783+Rick Harding <rharding@mitechie.com>
1784+Robert Jennings <robert.jennings@canonical.com>
1785+Rodrigo Barbieri <rodrigo.barbieri2010@gmail.com>
1786+Ryan Beisner <ryan-beisner@users.noreply.github.com>
1787+Ryan Beisner <ryan.beisner@canonical.com>
1788+Ryan Harper <ryan.harper@ubuntu.com>
1789+Sahid Orentino Ferdjaoui <sahid.ferdjaoui@libremel.fr>
1790+Sandor Zeestraten <sandor.zeestraten@ntnu.no>
1791+Scott Moser <smoser@ubuntu.com>
1792+Seyeong Kim <seyeong.kim@canonical.com>
1793+Seyeong Kim <xtrusia@gmail.com>
1794+Shiva Prasad Rao <shivrao@cisco.com>
1795+Simon Davy <bloodearnest@gmail.com>
1796+Simon Kollberg <simon@asfalt>
1797+Simon Poirier <simon.poirier@canonical.com>
1798+Stamatis Katsaounis <katsaouniss@gmail.com>
1799+Stephan <7194553+SPFZ@users.noreply.github.com>
1800+Stuart Bishop <stuart.bishop@canonical.com>
1801+Stuart Bishop <stuart@stuartbishop.net>
1802+Stuart Bishop <stub@canonical.com>
1803+Stub <stuart.bishop@canonical.com>
1804+Stub <stuart@stuartbishop.net>
1805+Subbarayudu Mukkamala <smukkamala@nuagenetworks.net>
1806+Sunny Verma <sunny.verma@nuagenetworks.net>
1807+Sunny Verma <sunnyverma1992@gmail.com>
1808+Tiago Pasqualini <tiago.pasqualini@gmail.com>
1809+Tim Kuhlman <timothy.kuhlman@canonical.com>
1810+Tim McNamara <code@timmcnamara.co.nz>
1811+Tim McNamara <tim.mcnamara@canonical.com>
1812+Tim Van Steenburgh <tim.van.steenburgh@canonical.com>
1813+Tim Van Steenburgh <tvansteenburgh@gmail.com>
1814+Tom Haddon <tom.haddon@canonical.com>
1815+Trent Lloyd <trent.lloyd@canonical.com>
1816+Tytus Kurek <tkurek@itsteer.com>
1817+Tytus Kurek <tytus.kurek@canonical.com>
1818+Ubuntu <ubuntu@gabrielcocenza-bastion.project.serverstack>
1819+Ubuntu <ubuntu@localhost.localdomain>
1820+Vladimir Grevtsev <vlgrevtsev@gmail.com>
1821+Xtrusia <xtrusia@xtrusia-desktop>
1822+Yaguang Tang <yaguang.tang@canonical.com>
1823+Yang Kelvin <kelvin.liu@canonical.com>
1824+Zhang Hua <joshua.zhang@canonical.com>
1825+afrikha <aymen.frikha@canonical.com>
1826+amulet@dummy-user.tld <>
1827+andrew.glen-young@canonical.com <>
1828+axino <axino@canonical.com>
1829+billy.olsen@canonical.com <>
1830+charles butler <Charles.butler@ubuntu.com>
1831+chris.macnaughton@canonical.com <>
1832+coreycb <corey.bryant@canonical.com>
1833+dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
1834+evilnick <>
1835+hemanthnakkina <mail2hemanth.n@gmail.com>
1836+joshua.zhang@canonical.com <>
1837+jrwren@xmtp.net <>
1838+mattyw <lp@mattyw.net>
1839+michael.skalka@canonical.com <>
1840+mkalcok <69471063+mkalcok@users.noreply.github.com>
1841+shane.peters@canonical.com <>
1842+stub42 <stuart@stuartbishop.net>
1843+vincenzo di somma <vincenzo.di.somma@canonical.com>
1844+xavpaice <xavpaice@users.noreply.github.com>
1845+yolanda.robla@canonical.com <>
1846diff --git a/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/INSTALLER b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/INSTALLER
1847new file mode 100644
1848index 0000000..a1b589e
1849--- /dev/null
1850+++ b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/INSTALLER
1851@@ -0,0 +1 @@
1852+pip
1853diff --git a/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/LICENSE b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/LICENSE
1854new file mode 100644
1855index 0000000..d645695
1856--- /dev/null
1857+++ b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/LICENSE
1858@@ -0,0 +1,202 @@
1859+
1860+ Apache License
1861+ Version 2.0, January 2004
1862+ http://www.apache.org/licenses/
1863+
1864+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1865+
1866+ 1. Definitions.
1867+
1868+ "License" shall mean the terms and conditions for use, reproduction,
1869+ and distribution as defined by Sections 1 through 9 of this document.
1870+
1871+ "Licensor" shall mean the copyright owner or entity authorized by
1872+ the copyright owner that is granting the License.
1873+
1874+ "Legal Entity" shall mean the union of the acting entity and all
1875+ other entities that control, are controlled by, or are under common
1876+ control with that entity. For the purposes of this definition,
1877+ "control" means (i) the power, direct or indirect, to cause the
1878+ direction or management of such entity, whether by contract or
1879+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
1880+ outstanding shares, or (iii) beneficial ownership of such entity.
1881+
1882+ "You" (or "Your") shall mean an individual or Legal Entity
1883+ exercising permissions granted by this License.
1884+
1885+ "Source" form shall mean the preferred form for making modifications,
1886+ including but not limited to software source code, documentation
1887+ source, and configuration files.
1888+
1889+ "Object" form shall mean any form resulting from mechanical
1890+ transformation or translation of a Source form, including but
1891+ not limited to compiled object code, generated documentation,
1892+ and conversions to other media types.
1893+
1894+ "Work" shall mean the work of authorship, whether in Source or
1895+ Object form, made available under the License, as indicated by a
1896+ copyright notice that is included in or attached to the work
1897+ (an example is provided in the Appendix below).
1898+
1899+ "Derivative Works" shall mean any work, whether in Source or Object
1900+ form, that is based on (or derived from) the Work and for which the
1901+ editorial revisions, annotations, elaborations, or other modifications
1902+ represent, as a whole, an original work of authorship. For the purposes
1903+ of this License, Derivative Works shall not include works that remain
1904+ separable from, or merely link (or bind by name) to the interfaces of,
1905+ the Work and Derivative Works thereof.
1906+
1907+ "Contribution" shall mean any work of authorship, including
1908+ the original version of the Work and any modifications or additions
1909+ to that Work or Derivative Works thereof, that is intentionally
1910+ submitted to Licensor for inclusion in the Work by the copyright owner
1911+ or by an individual or Legal Entity authorized to submit on behalf of
1912+ the copyright owner. For the purposes of this definition, "submitted"
1913+ means any form of electronic, verbal, or written communication sent
1914+ to the Licensor or its representatives, including but not limited to
1915+ communication on electronic mailing lists, source code control systems,
1916+ and issue tracking systems that are managed by, or on behalf of, the
1917+ Licensor for the purpose of discussing and improving the Work, but
1918+ excluding communication that is conspicuously marked or otherwise
1919+ designated in writing by the copyright owner as "Not a Contribution."
1920+
1921+ "Contributor" shall mean Licensor and any individual or Legal Entity
1922+ on behalf of whom a Contribution has been received by Licensor and
1923+ subsequently incorporated within the Work.
1924+
1925+ 2. Grant of Copyright License. Subject to the terms and conditions of
1926+ this License, each Contributor hereby grants to You a perpetual,
1927+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
1928+ copyright license to reproduce, prepare Derivative Works of,
1929+ publicly display, publicly perform, sublicense, and distribute the
1930+ Work and such Derivative Works in Source or Object form.
1931+
1932+ 3. Grant of Patent License. Subject to the terms and conditions of
1933+ this License, each Contributor hereby grants to You a perpetual,
1934+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
1935+ (except as stated in this section) patent license to make, have made,
1936+ use, offer to sell, sell, import, and otherwise transfer the Work,
1937+ where such license applies only to those patent claims licensable
1938+ by such Contributor that are necessarily infringed by their
1939+ Contribution(s) alone or by combination of their Contribution(s)
1940+ with the Work to which such Contribution(s) was submitted. If You
1941+ institute patent litigation against any entity (including a
1942+ cross-claim or counterclaim in a lawsuit) alleging that the Work
1943+ or a Contribution incorporated within the Work constitutes direct
1944+ or contributory patent infringement, then any patent licenses
1945+ granted to You under this License for that Work shall terminate
1946+ as of the date such litigation is filed.
1947+
1948+ 4. Redistribution. You may reproduce and distribute copies of the
1949+ Work or Derivative Works thereof in any medium, with or without
1950+ modifications, and in Source or Object form, provided that You
1951+ meet the following conditions:
1952+
1953+ (a) You must give any other recipients of the Work or
1954+ Derivative Works a copy of this License; and
1955+
1956+ (b) You must cause any modified files to carry prominent notices
1957+ stating that You changed the files; and
1958+
1959+ (c) You must retain, in the Source form of any Derivative Works
1960+ that You distribute, all copyright, patent, trademark, and
1961+ attribution notices from the Source form of the Work,
1962+ excluding those notices that do not pertain to any part of
1963+ the Derivative Works; and
1964+
1965+ (d) If the Work includes a "NOTICE" text file as part of its
1966+ distribution, then any Derivative Works that You distribute must
1967+ include a readable copy of the attribution notices contained
1968+ within such NOTICE file, excluding those notices that do not
1969+ pertain to any part of the Derivative Works, in at least one
1970+ of the following places: within a NOTICE text file distributed
1971+ as part of the Derivative Works; within the Source form or
1972+ documentation, if provided along with the Derivative Works; or,
1973+ within a display generated by the Derivative Works, if and
1974+ wherever such third-party notices normally appear. The contents
1975+ of the NOTICE file are for informational purposes only and
1976+ do not modify the License. You may add Your own attribution
1977+ notices within Derivative Works that You distribute, alongside
1978+ or as an addendum to the NOTICE text from the Work, provided
1979+ that such additional attribution notices cannot be construed
1980+ as modifying the License.
1981+
1982+ You may add Your own copyright statement to Your modifications and
1983+ may provide additional or different license terms and conditions
1984+ for use, reproduction, or distribution of Your modifications, or
1985+ for any such Derivative Works as a whole, provided Your use,
1986+ reproduction, and distribution of the Work otherwise complies with
1987+ the conditions stated in this License.
1988+
1989+ 5. Submission of Contributions. Unless You explicitly state otherwise,
1990+ any Contribution intentionally submitted for inclusion in the Work
1991+ by You to the Licensor shall be under the terms and conditions of
1992+ this License, without any additional terms or conditions.
1993+ Notwithstanding the above, nothing herein shall supersede or modify
1994+ the terms of any separate license agreement you may have executed
1995+ with Licensor regarding such Contributions.
1996+
1997+ 6. Trademarks. This License does not grant permission to use the trade
1998+ names, trademarks, service marks, or product names of the Licensor,
1999+ except as required for reasonable and customary use in describing the
2000+ origin of the Work and reproducing the content of the NOTICE file.
2001+
2002+ 7. Disclaimer of Warranty. Unless required by applicable law or
2003+ agreed to in writing, Licensor provides the Work (and each
2004+ Contributor provides its Contributions) on an "AS IS" BASIS,
2005+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
2006+ implied, including, without limitation, any warranties or conditions
2007+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
2008+ PARTICULAR PURPOSE. You are solely responsible for determining the
2009+ appropriateness of using or redistributing the Work and assume any
2010+ risks associated with Your exercise of permissions under this License.
2011+
2012+ 8. Limitation of Liability. In no event and under no legal theory,
2013+ whether in tort (including negligence), contract, or otherwise,
2014+ unless required by applicable law (such as deliberate and grossly
2015+ negligent acts) or agreed to in writing, shall any Contributor be
2016+ liable to You for damages, including any direct, indirect, special,
2017+ incidental, or consequential damages of any character arising as a
2018+ result of this License or out of the use or inability to use the
2019+ Work (including but not limited to damages for loss of goodwill,
2020+ work stoppage, computer failure or malfunction, or any and all
2021+ other commercial damages or losses), even if such Contributor
2022+ has been advised of the possibility of such damages.
2023+
2024+ 9. Accepting Warranty or Additional Liability. While redistributing
2025+ the Work or Derivative Works thereof, You may choose to offer,
2026+ and charge a fee for, acceptance of support, warranty, indemnity,
2027+ or other liability obligations and/or rights consistent with this
2028+ License. However, in accepting such obligations, You may act only
2029+ on Your own behalf and on Your sole responsibility, not on behalf
2030+ of any other Contributor, and only if You agree to indemnify,
2031+ defend, and hold each Contributor harmless for any liability
2032+ incurred by, or claims asserted against, such Contributor by reason
2033+ of your accepting any such warranty or additional liability.
2034+
2035+ END OF TERMS AND CONDITIONS
2036+
2037+ APPENDIX: How to apply the Apache License to your work.
2038+
2039+ To apply the Apache License to your work, attach the following
2040+ boilerplate notice, with the fields enclosed by brackets "[]"
2041+ replaced with your own identifying information. (Don't include
2042+ the brackets!) The text should be enclosed in the appropriate
2043+ comment syntax for the file format. We also recommend that a
2044+ file or class name and description of purpose be included on the
2045+ same "printed page" as the copyright notice for easier
2046+ identification within third-party archives.
2047+
2048+ Copyright [yyyy] [name of copyright owner]
2049+
2050+ Licensed under the Apache License, Version 2.0 (the "License");
2051+ you may not use this file except in compliance with the License.
2052+ You may obtain a copy of the License at
2053+
2054+ http://www.apache.org/licenses/LICENSE-2.0
2055+
2056+ Unless required by applicable law or agreed to in writing, software
2057+ distributed under the License is distributed on an "AS IS" BASIS,
2058+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2059+ See the License for the specific language governing permissions and
2060+ limitations under the License.
2061diff --git a/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/METADATA b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/METADATA
2062new file mode 100644
2063index 0000000..9e4fc06
2064--- /dev/null
2065+++ b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/METADATA
2066@@ -0,0 +1,82 @@
2067+Metadata-Version: 2.1
2068+Name: charmhelpers
2069+Version: 0.20.23
2070+Summary: Helpers for Juju Charm development
2071+Home-page: https://github.com/juju/charm-helpers
2072+Author: Charmers
2073+Author-email: juju@lists.ubuntu.com
2074+License: UNKNOWN
2075+Platform: UNKNOWN
2076+Classifier: Intended Audience :: Information Technology
2077+Classifier: Intended Audience :: System Administrators
2078+Classifier: License :: OSI Approved :: Apache Software License
2079+Classifier: Operating System :: POSIX :: Linux
2080+Classifier: Programming Language :: Python
2081+Classifier: Programming Language :: Python :: 2
2082+Classifier: Programming Language :: Python :: 2.7
2083+Classifier: Programming Language :: Python :: 3
2084+Classifier: Programming Language :: Python :: 3.6
2085+Classifier: Programming Language :: Python :: 3.7
2086+Classifier: Programming Language :: Python :: 3.8
2087+Requires-Dist: Jinja2
2088+Requires-Dist: PyYAML
2089+Requires-Dist: Tempita
2090+Requires-Dist: netaddr
2091+Requires-Dist: pbr (!=2.1.0,>=2.0.0)
2092+Requires-Dist: six
2093+
2094+CharmHelpers |badge|
2095+--------------------
2096+
2097+.. |badge| image:: https://github.com/juju/charm-helpers/actions/workflows/build.yml/badge.svg?branch=master
2098+ :target: https://github.com/juju/charm-helpers/actions/workflows/build.yml
2099+
2100+Overview
2101+========
2102+
2103+CharmHelpers provides an opinionated set of tools for building Juju charms.
2104+
2105+The full documentation is available online at: https://charm-helpers.readthedocs.io/
2106+
2107+Common Usage Examples
2108+=====================
2109+
2110+* interaction with charm-specific Juju unit agents via hook tools;
2111+* processing of events and execution of decorated functions based on event names;
2112+* handling of persistent storage between independent charm invocations;
2113+* rendering of configuration file templates;
2114+* modification of system configuration files;
2115+* installation of packages;
2116+* retrieval of machine-specific details;
2117+* implementation of application-specific code reused in similar charms.
2118+
2119+Why Python?
2120+===========
2121+
2122+* Python is an extremely popular, easy to learn, and powerful language which is also common in automation tools;
2123+* An interpreted language helps with charm portability across different CPU architectures;
2124+* Doesn't require debugging symbols (just use pdb in-place);
2125+* An author or a user is able to make debugging changes without recompiling a charm.
2126+
2127+Dev/Test
2128+========
2129+
2130+See the HACKING.md file for information about testing and development.
2131+
2132+License
2133+=======
2134+
2135+Licensed under the Apache License, Version 2.0 (the "License");
2136+you may not use this file except in compliance with the License.
2137+You may obtain a copy of the License at
2138+
2139+ http://www.apache.org/licenses/LICENSE-2.0
2140+
2141+Unless required by applicable law or agreed to in writing, software
2142+distributed under the License is distributed on an "AS IS" BASIS,
2143+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2144+See the License for the specific language governing permissions and
2145+limitations under the License.
2146+
2147+
2148+
2149diff --git a/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/RECORD b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/RECORD
2150new file mode 100644
2151index 0000000..1265a34
2152--- /dev/null
2153+++ b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/RECORD
2154@@ -0,0 +1,338 @@
2155+../../bin/charmsupport,sha256=z2Ahhl48jERwUMbWFry9nN3-HYE7ef6Yb9kyKcVG65k,910
2156+../../bin/chlp,sha256=TAx1ZeHF6RvrjVNTL6Z8EnYqVw6iaqM97ssF5Kqd6dc,134
2157+../../bin/salt-call,sha256=7GSY5yTe3yc5mXfCV7l2tLFPd1QFsF67-O4KbSCOhFM,190
2158+charmhelpers-0.20.23.dist-info/AUTHORS,sha256=LA1fKQDSel2F6WV5z1bCGJi15Kax16cmgcoIVOTU3Hc,9149
2159+charmhelpers-0.20.23.dist-info/INSTALLER,sha256=zuuue4knoyJ-UwPPXg8fezS7VCrXJQrAP7zeNuwvFQg,4
2160+charmhelpers-0.20.23.dist-info/LICENSE,sha256=z8d0m5b2O9McPEK1xHG_dWgUBT6EfBDz6wA0F7xSPTA,11358
2161+charmhelpers-0.20.23.dist-info/METADATA,sha256=UhVXDqyJGeKkJZu59SV0cZeY9z64kissYjjoGUZ042g,2867
2162+charmhelpers-0.20.23.dist-info/RECORD,,
2163+charmhelpers-0.20.23.dist-info/WHEEL,sha256=g4nMs7d-Xl9-xC9XovUrsDHGXt-FT0E17Yqo92DEfvY,92
2164+charmhelpers-0.20.23.dist-info/pbr.json,sha256=vtUyvqen8FVsRTiN4YXgDwa7-zr8YW5AI4_tVaJmoQU,47
2165+charmhelpers-0.20.23.dist-info/top_level.txt,sha256=5KmF7d1Uf0ZK3rqHxNFzsBbrycxZUoYLmaOB0762zNw,13
2166+charmhelpers/__init__.py,sha256=UZ_2r186iMSYkGIIgLWT0U81lZXy5OwGuwG4HFpR6P8,3593
2167+charmhelpers/__pycache__/__init__.cpython-38.pyc,,
2168+charmhelpers/__pycache__/context.cpython-38.pyc,,
2169+charmhelpers/__pycache__/coordinator.cpython-38.pyc,,
2170+charmhelpers/__pycache__/osplatform.cpython-38.pyc,,
2171+charmhelpers/__pycache__/version.cpython-38.pyc,,
2172+charmhelpers/cli/README.rst,sha256=eAa7cMrPjilRFO3Fd8f-ZqXZcQduxsaFw6An6T2Mxtw,3101
2173+charmhelpers/cli/__init__.py,sha256=aWJPQAlZqfmC9dEvcbuQvXDD9Podo7wuf3NWSUd5CzI,6897
2174+charmhelpers/cli/__pycache__/__init__.cpython-38.pyc,,
2175+charmhelpers/cli/__pycache__/benchmark.cpython-38.pyc,,
2176+charmhelpers/cli/__pycache__/commands.cpython-38.pyc,,
2177+charmhelpers/cli/__pycache__/hookenv.cpython-38.pyc,,
2178+charmhelpers/cli/__pycache__/host.cpython-38.pyc,,
2179+charmhelpers/cli/__pycache__/unitdata.cpython-38.pyc,,
2180+charmhelpers/cli/benchmark.py,sha256=0EVA9bM9dEiGG5zo3ROxF_trVpnkJwhA1QKau-9i9hk,1296
2181+charmhelpers/cli/commands.py,sha256=5Clhh44FLf8vDGGrZFaaF2BHvRYw4tjtFk_WabxgVlM,1101
2182+charmhelpers/cli/hookenv.py,sha256=5J7_rfObmUqPX5ovbXTPminsRX1Z7anXxRVlC70f4ac,848
2183+charmhelpers/cli/host.py,sha256=Z5oXQ5d9vMN_tC2rTHSdx1I5tdQd6kL6QsB0uGrGS6M,1022
2184+charmhelpers/cli/unitdata.py,sha256=qiEEjaYdiHzM_LQ-SVTiV-3PAwMVqX1k-mXPg2fH5a0,1799
2185+charmhelpers/context.py,sha256=IaQv7Jwv5TtgNVLStDUPLYlLvpW80-j3-SsZIFRrL38,7575
2186+charmhelpers/contrib/__init__.py,sha256=cgDOTi6NEduIPlGmBYi69fC9cCaht22kCre0am-M2rk,584
2187+charmhelpers/contrib/__pycache__/__init__.cpython-38.pyc,,
2188+charmhelpers/contrib/__pycache__/python.cpython-38.pyc,,
2189+charmhelpers/contrib/ansible/__init__.py,sha256=xV6QwHRr4p0J9bVi2gbGeQfnu1P0JBSPmkby96sdy_I,10197
2190+charmhelpers/contrib/ansible/__pycache__/__init__.cpython-38.pyc,,
2191+charmhelpers/contrib/benchmark/__init__.py,sha256=qoJQb98jrVAbd3xRVQlpl2norUfS53kcDG_a36qXo-g,3768
2192+charmhelpers/contrib/benchmark/__pycache__/__init__.cpython-38.pyc,,
2193+charmhelpers/contrib/charmhelpers/IMPORT,sha256=MAzGjhWluP6tDirtfiQAPQAW-aN1QIbTOC_LvQaRcM8,274
2194+charmhelpers/contrib/charmhelpers/__init__.py,sha256=zIsbK9HVGnJ2B_blpaIk1uqq6oB15UL8WBc7en8YhoE,7746
2195+charmhelpers/contrib/charmhelpers/__pycache__/__init__.cpython-38.pyc,,
2196+charmhelpers/contrib/charmsupport/IMPORT,sha256=P5fXENEbVggQtH7Ug6rB6Cn4U1KzJO9lT2sDg1VZBRI,961
2197+charmhelpers/contrib/charmsupport/__init__.py,sha256=cgDOTi6NEduIPlGmBYi69fC9cCaht22kCre0am-M2rk,584
2198+charmhelpers/contrib/charmsupport/__pycache__/__init__.cpython-38.pyc,,
2199+charmhelpers/contrib/charmsupport/__pycache__/nrpe.cpython-38.pyc,,
2200+charmhelpers/contrib/charmsupport/__pycache__/volumes.cpython-38.pyc,,
2201+charmhelpers/contrib/charmsupport/nrpe.py,sha256=VbsK-rZbcPvN2RVZTQA2um-G53hgUkd0mnqvrF26eoc,19188
2202+charmhelpers/contrib/charmsupport/volumes.py,sha256=qaB_Lhk7fMWUwZ90fZ4d7Zwo-8ByDey6By9-Bjg4rVM,5928
2203+charmhelpers/contrib/database/__init__.py,sha256=uLfkEetO5N_jsH5lEDW7BdtiLrA2dlHStiHOv8NNp9Q,541
2204+charmhelpers/contrib/database/__pycache__/__init__.cpython-38.pyc,,
2205+charmhelpers/contrib/database/__pycache__/mysql.cpython-38.pyc,,
2206+charmhelpers/contrib/database/mysql.py,sha256=v9lYP5BP39vPTNuundpveL_FPetFD_M0Gg_d4OLZ7O4,30985
2207+charmhelpers/contrib/hahelpers/__init__.py,sha256=cgDOTi6NEduIPlGmBYi69fC9cCaht22kCre0am-M2rk,584
2208+charmhelpers/contrib/hahelpers/__pycache__/__init__.cpython-38.pyc,,
2209+charmhelpers/contrib/hahelpers/__pycache__/apache.cpython-38.pyc,,
2210+charmhelpers/contrib/hahelpers/__pycache__/cluster.cpython-38.pyc,,
2211+charmhelpers/contrib/hahelpers/apache.py,sha256=EvYrltL4p93JkpKYhupWbYu27gQPf7wyyqkUkhDB404,2854
2212+charmhelpers/contrib/hahelpers/cluster.py,sha256=XwfN6N81d_6FGGoXKmSb1esT4sQ9mUPp8K051A7C5z0,14761
2213+charmhelpers/contrib/hardening/README.hardening.md,sha256=fQ7ofcmpTSq0M9qAwcEqrHnBJMELfmXnhPZkXQoDWS8,1078
2214+charmhelpers/contrib/hardening/__init__.py,sha256=JyKIot_ny07U5ky-tWtzDfH_Mhsaa2JmZQFhF7P3JNs,579
2215+charmhelpers/contrib/hardening/__pycache__/__init__.cpython-38.pyc,,
2216+charmhelpers/contrib/hardening/__pycache__/harden.cpython-38.pyc,,
2217+charmhelpers/contrib/hardening/__pycache__/templating.cpython-38.pyc,,
2218+charmhelpers/contrib/hardening/__pycache__/utils.cpython-38.pyc,,
2219+charmhelpers/contrib/hardening/apache/__init__.py,sha256=AGnzufUxF09rFJMaKuYdpWPJ2VFZCt_DNm7RFjODDtY,664
2220+charmhelpers/contrib/hardening/apache/__pycache__/__init__.cpython-38.pyc,,
2221+charmhelpers/contrib/hardening/apache/checks/__init__.py,sha256=lZrfeNvgGyC0T6DkjHTnTvpViHtCi8wAUTe958wltqk,1018
2222+charmhelpers/contrib/hardening/apache/checks/__pycache__/__init__.cpython-38.pyc,,
2223+charmhelpers/contrib/hardening/apache/checks/__pycache__/config.cpython-38.pyc,,
2224+charmhelpers/contrib/hardening/apache/checks/config.py,sha256=bQKeoExtlOxnw4JLfDHL6okcaE4Wir8iMT738UHawyQ,3634
2225+charmhelpers/contrib/hardening/apache/templates/99-hardening.conf,sha256=QiM3aU1ssjiO-sqtXuCyBZchHpmfCUoP3pUgLmJuaqg,864
2226+charmhelpers/contrib/hardening/apache/templates/alias.conf,sha256=bCmG5CFSJTytjJtbHNzNmJKKZyMJuR_stS7whbZzggM,1198
2227+charmhelpers/contrib/hardening/audits/__init__.py,sha256=aKraPvxRTLrDE8lP0Sr0sNuJyNCUHp3o84XYoK6cCR8,2052
2228+charmhelpers/contrib/hardening/audits/__pycache__/__init__.cpython-38.pyc,,
2229+charmhelpers/contrib/hardening/audits/__pycache__/apache.cpython-38.pyc,,
2230+charmhelpers/contrib/hardening/audits/__pycache__/apt.cpython-38.pyc,,
2231+charmhelpers/contrib/hardening/audits/__pycache__/file.cpython-38.pyc,,
2232+charmhelpers/contrib/hardening/audits/apache.py,sha256=FvQVqwdJr4XbE2s8EYqbwUc6UipFpdFB1ejEzqQb_eE,3652
2233+charmhelpers/contrib/hardening/audits/apt.py,sha256=lzFG-GvQzuNxTRZmRFC5GFIpN4sQOc-9EwsQA6vdjy0,3529
2234+charmhelpers/contrib/hardening/audits/file.py,sha256=_GGN2oRvRaW60wEtHyn6ptFBCs5NNTQZj0Y5SsVfB5I,19624
2235+charmhelpers/contrib/hardening/defaults/apache.yaml,sha256=trCCi7WX20kklq5oHTQsa5bmiZ2WY4kGkjwSIaxOvgw,608
2236+charmhelpers/contrib/hardening/defaults/apache.yaml.schema,sha256=3lUtLjIJIV4eMVP2kcrtt-qqZi-Ju_l9hegZVDpIip4,303
2237+charmhelpers/contrib/hardening/defaults/mysql.yaml,sha256=AFxAc5r3A_dkMA40Wbpa7_gzgL4SjOS3aguMT6NRkS4,1655
2238+charmhelpers/contrib/hardening/defaults/mysql.yaml.schema,sha256=incmlBGYQ33sKWoojIq471_D5LBfCL6rMkHbwt7gfGU,393
2239+charmhelpers/contrib/hardening/defaults/os.yaml,sha256=v_yf92FkRtcR2t_CruZgdZjZuuExwNY8ZrzTAFDN1Vg,1928
2240+charmhelpers/contrib/hardening/defaults/os.yaml.schema,sha256=3HuBijlL_go6xDSi956SoPNDfBwsverh-FK-GliUyWA,908
2241+charmhelpers/contrib/hardening/defaults/ssh.yaml,sha256=VHBFUbR_By5fzUtbVXr0A3ZusyE-oM-AAB3c9hA3sj8,1537
2242+charmhelpers/contrib/hardening/defaults/ssh.yaml.schema,sha256=xjyI1eo-sNH_WiDNImmzYy4NZqUgaCyRb6_JS3O4LA8,852
2243+charmhelpers/contrib/hardening/harden.py,sha256=1OGzrpl-pj7G9LDZmpV28YP5AH9Dn6koDOJ7vKEwNzE,3909
2244+charmhelpers/contrib/hardening/host/__init__.py,sha256=AGnzufUxF09rFJMaKuYdpWPJ2VFZCt_DNm7RFjODDtY,664
2245+charmhelpers/contrib/hardening/host/__pycache__/__init__.cpython-38.pyc,,
2246+charmhelpers/contrib/hardening/host/checks/__init__.py,sha256=aIN_iRFqI1g-dPaEXT3C9yIsnlD2xRV4n_xvU120geY,1439
2247+charmhelpers/contrib/hardening/host/checks/__pycache__/__init__.cpython-38.pyc,,
2248+charmhelpers/contrib/hardening/host/checks/__pycache__/apt.cpython-38.pyc,,
2249+charmhelpers/contrib/hardening/host/checks/__pycache__/limits.cpython-38.pyc,,
2250+charmhelpers/contrib/hardening/host/checks/__pycache__/login.cpython-38.pyc,,
2251+charmhelpers/contrib/hardening/host/checks/__pycache__/minimize_access.cpython-38.pyc,,
2252+charmhelpers/contrib/hardening/host/checks/__pycache__/pam.cpython-38.pyc,,
2253+charmhelpers/contrib/hardening/host/checks/__pycache__/profile.cpython-38.pyc,,
2254+charmhelpers/contrib/hardening/host/checks/__pycache__/securetty.cpython-38.pyc,,
2255+charmhelpers/contrib/hardening/host/checks/__pycache__/suid_sgid.cpython-38.pyc,,
2256+charmhelpers/contrib/hardening/host/checks/__pycache__/sysctl.cpython-38.pyc,,
2257+charmhelpers/contrib/hardening/host/checks/apt.py,sha256=l2kKOfqp2zQLUNRWdBlzE9FGQ9_AZqVYwvgNhM-FB0g,1255
2258+charmhelpers/contrib/hardening/host/checks/limits.py,sha256=oMLlcmw8O8eWmzFkAXT7zUju3IIOKMwgB9iocwmgPRI,2015
2259+charmhelpers/contrib/hardening/host/checks/login.py,sha256=xdlp9kjYXHhFeBoXKlSCM_wQwZ0hH3NjgwBv1KTNVRM,2514
2260+charmhelpers/contrib/hardening/host/checks/minimize_access.py,sha256=Px1OQ96RNSA3tWFYW0eWkIk0elnWsjlAyhp0n-FMy5Q,1849
2261+charmhelpers/contrib/hardening/host/checks/pam.py,sha256=dhjqQNsBPQ82jqQ-FDmxuKyI-QT7nVfPLApO5DhBVTQ,4042
2262+charmhelpers/contrib/hardening/host/checks/profile.py,sha256=dC-DBwfHvSALGWzsPc8p6VTORn25jO7sjtVPK4m7XJY,1905
2263+charmhelpers/contrib/hardening/host/checks/securetty.py,sha256=SA7Ln9dW1BOQMohVKrx1fJTcNgZXfop3cnT9hmbkz5U,1283
2264+charmhelpers/contrib/hardening/host/checks/suid_sgid.py,sha256=PaNAU2oJlz6drXWpkIhtIe4rxbazyI9CXZvr21vb6jc,5057
2265+charmhelpers/contrib/hardening/host/checks/sysctl.py,sha256=o-ny9OntomnTKP-iaeFyh-jtLexG04-11Y3vGOSNGAw,7421
2266+charmhelpers/contrib/hardening/host/templates/10.hardcore.conf,sha256=32p-Xp6nTdYqDwCRkfHetIBQ9M8xdAFfMM4T0W03svk,435
2267+charmhelpers/contrib/hardening/host/templates/99-hardening.sh,sha256=YqlQ86pwqXHITmNWpjkcKsABplMfFdG2_CbaEPCFvJI,65
2268+charmhelpers/contrib/hardening/host/templates/99-juju-hardening.conf,sha256=7-ufC_2dpUh43flvsUmhPeUnJObn0xRbjCGU7Bjm7is,340
2269+charmhelpers/contrib/hardening/host/templates/login.defs,sha256=bilHHMKXNW08fCYrACqHSUx69M9Bzgf9mIfUepK8fi0,11495
2270+charmhelpers/contrib/hardening/host/templates/modules,sha256=QVulAENAQNjAKZSw3GvClf4lTk3fLF9cgqN8ErKB5S0,1726
2271+charmhelpers/contrib/hardening/host/templates/passwdqc.conf,sha256=DoJQ4pJNZyybxy9rsoMDiQVZT6mUslrzY_73gftlwYw,453
2272+charmhelpers/contrib/hardening/host/templates/pinerolo_profile.sh,sha256=L3HzvAHokRNX1SVQ8dUG8xQYn7L2iBtLCpWTbIiyIzU,472
2273+charmhelpers/contrib/hardening/host/templates/securetty,sha256=Ds3sazz6fMgEI0oMLRWXt-TOp5gF2Fk8movVDzSLASk,419
2274+charmhelpers/contrib/hardening/host/templates/tally2,sha256=6VxdzzPJIz_r2QEZfD55U6cB_rkgQ-hbsxRnoTEB4eQ,569
2275+charmhelpers/contrib/hardening/mysql/__init__.py,sha256=AGnzufUxF09rFJMaKuYdpWPJ2VFZCt_DNm7RFjODDtY,664
2276+charmhelpers/contrib/hardening/mysql/__pycache__/__init__.cpython-38.pyc,,
2277+charmhelpers/contrib/hardening/mysql/checks/__init__.py,sha256=-iXZm9k4HINfFmcOmx3dsS-r7ScgLkeBATda-pV7vcA,1014
2278+charmhelpers/contrib/hardening/mysql/checks/__pycache__/__init__.cpython-38.pyc,,
2279+charmhelpers/contrib/hardening/mysql/checks/__pycache__/config.cpython-38.pyc,,
2280+charmhelpers/contrib/hardening/mysql/checks/config.py,sha256=tmp5IAQTuLrhQWV4NCwljHELrK6sIcwtCzj6cHSW5pM,2989
2281+charmhelpers/contrib/hardening/mysql/templates/hardening.cnf,sha256=6ECbDt-t4PYHuC1yeirNmjo4qA0ThmYD36M_L-I3dPM,457
2282+charmhelpers/contrib/hardening/ssh/__init__.py,sha256=AGnzufUxF09rFJMaKuYdpWPJ2VFZCt_DNm7RFjODDtY,664
2283+charmhelpers/contrib/hardening/ssh/__pycache__/__init__.cpython-38.pyc,,
2284+charmhelpers/contrib/hardening/ssh/checks/__init__.py,sha256=hR00G4AUdIo26vzkTOq9q_nxAWqqo7Y752ITblsfRBw,1006
2285+charmhelpers/contrib/hardening/ssh/checks/__pycache__/__init__.cpython-38.pyc,,
2286+charmhelpers/contrib/hardening/ssh/checks/__pycache__/config.cpython-38.pyc,,
2287+charmhelpers/contrib/hardening/ssh/checks/config.py,sha256=nh6_HrNc7HRvpIASs28bNdF9-1lIx095FHp8SB7LOlE,18949
2288+charmhelpers/contrib/hardening/ssh/templates/ssh_config,sha256=Tpcaxl4_Im3Ol65mp2dlJQ3_qWqNtMkk8LK_PxVrIgQ,2028
2289+charmhelpers/contrib/hardening/ssh/templates/sshd_config,sha256=aU-pRJCa8IDdgdlfzRYrNn0KAOYFtMofFWutAOWVRt4,4488
2290+charmhelpers/contrib/hardening/templating.py,sha256=Iiw5lC6FIadGdH63CrMOuYX_7bGHwtltMwAhJWNOyrA,2447
2291+charmhelpers/contrib/hardening/utils.py,sha256=U5F3cH99tkMO6Eoi5BCnc0r8OmdkYQraeDxGKunMN2E,5193
2292+charmhelpers/contrib/mellanox/__init__.py,sha256=Tw33cKAnl0OzKf_D5VqVyr__eWnyW6xHvhzG6SJUsps,574
2293+charmhelpers/contrib/mellanox/__pycache__/__init__.cpython-38.pyc,,
2294+charmhelpers/contrib/mellanox/__pycache__/infiniband.cpython-38.pyc,,
2295+charmhelpers/contrib/mellanox/infiniband.py,sha256=UXjjFu9N8iY9YOAgqZlW8IOkwOjbBVnrbfsUICMhaVU,3494
2296+charmhelpers/contrib/network/__init__.py,sha256=cgDOTi6NEduIPlGmBYi69fC9cCaht22kCre0am-M2rk,584
2297+charmhelpers/contrib/network/__pycache__/__init__.cpython-38.pyc,,
2298+charmhelpers/contrib/network/__pycache__/ip.cpython-38.pyc,,
2299+charmhelpers/contrib/network/__pycache__/ufw.cpython-38.pyc,,
2300+charmhelpers/contrib/network/ip.py,sha256=FurNn_p91M6RsRM6grcmnetJvQuU4nAk_1MmuYa3qyM,19187
2301+charmhelpers/contrib/network/ovs/__init__.py,sha256=JyYO4EJam7pGsOovu9Jo7j_6YAM2oQ3__VyKiLpQUJI,27043
2302+charmhelpers/contrib/network/ovs/__pycache__/__init__.cpython-38.pyc,,
2303+charmhelpers/contrib/network/ovs/__pycache__/ovn.cpython-38.pyc,,
2304+charmhelpers/contrib/network/ovs/__pycache__/ovsdb.cpython-38.pyc,,
2305+charmhelpers/contrib/network/ovs/__pycache__/utils.cpython-38.pyc,,
2306+charmhelpers/contrib/network/ovs/ovn.py,sha256=FOMEBmqgb-9Z9n4nhy69Zq8JnxxkvZvlE2FN1u8fDM4,8627
2307+charmhelpers/contrib/network/ovs/ovsdb.py,sha256=VOPEfwTorL4Ig7Tu_bv-gPsxS-FBzZIKdgA-Exvg03M,8776
2308+charmhelpers/contrib/network/ovs/utils.py,sha256=D6d4ZRqnJzP2iHQYsHzmFsXlSY-7wMFS5zgcyWwvgl8,948
2309+charmhelpers/contrib/network/ufw.py,sha256=1-OSr1EhFcIW1Tuz_CzB-h7Lv4gP-bSYI5dUGmQPuhA,12722
2310+charmhelpers/contrib/openstack/__init__.py,sha256=cgDOTi6NEduIPlGmBYi69fC9cCaht22kCre0am-M2rk,584
2311+charmhelpers/contrib/openstack/__pycache__/__init__.cpython-38.pyc,,
2312+charmhelpers/contrib/openstack/__pycache__/alternatives.cpython-38.pyc,,
2313+charmhelpers/contrib/openstack/__pycache__/cert_utils.cpython-38.pyc,,
2314+charmhelpers/contrib/openstack/__pycache__/context.cpython-38.pyc,,
2315+charmhelpers/contrib/openstack/__pycache__/deferred_events.cpython-38.pyc,,
2316+charmhelpers/contrib/openstack/__pycache__/exceptions.cpython-38.pyc,,
2317+charmhelpers/contrib/openstack/__pycache__/ip.cpython-38.pyc,,
2318+charmhelpers/contrib/openstack/__pycache__/keystone.cpython-38.pyc,,
2319+charmhelpers/contrib/openstack/__pycache__/neutron.cpython-38.pyc,,
2320+charmhelpers/contrib/openstack/__pycache__/policy_rcd.cpython-38.pyc,,
2321+charmhelpers/contrib/openstack/__pycache__/policyd.cpython-38.pyc,,
2322+charmhelpers/contrib/openstack/__pycache__/ssh_migrations.cpython-38.pyc,,
2323+charmhelpers/contrib/openstack/__pycache__/templating.cpython-38.pyc,,
2324+charmhelpers/contrib/openstack/__pycache__/utils.cpython-38.pyc,,
2325+charmhelpers/contrib/openstack/__pycache__/vaultlocker.cpython-38.pyc,,
2326+charmhelpers/contrib/openstack/alternatives.py,sha256=WJQw90efeFIxtuXgbRpOYOPjwT1pBnZHgyWc0uL496A,1472
2327+charmhelpers/contrib/openstack/audits/__init__.py,sha256=SWgrwhWuHOP45o4CT-FX6xzHRz83JWXZ1I1WZI8WUL4,6371
2328+charmhelpers/contrib/openstack/audits/__pycache__/__init__.cpython-38.pyc,,
2329+charmhelpers/contrib/openstack/audits/__pycache__/openstack_security_guide.cpython-38.pyc,,
2330+charmhelpers/contrib/openstack/audits/openstack_security_guide.py,sha256=KNnYfimCparl-HgoDOnuMndSUghmxUjgASzulzZds48,9893
2331+charmhelpers/contrib/openstack/cert_utils.py,sha256=OLAos14O0n0ThbGRp6568hLcdWUkAIwpSD0Pvy9L9g4,15720
2332+charmhelpers/contrib/openstack/context.py,sha256=T_VZe_sCJSPUPH69bCo6e_Y-3ZpOHznxk8X3Z1HUuT4,123351
2333+charmhelpers/contrib/openstack/deferred_events.py,sha256=IJTL4GIFe5oQPzu1S-pfledau4jlV5sNy6R-tF9UrWQ,12410
2334+charmhelpers/contrib/openstack/exceptions.py,sha256=xbtpzWwFE7s4zAuWhUaaTeZdQIp-p_EkTaVA-5mZkx0,870
2335+charmhelpers/contrib/openstack/files/__init__.py,sha256=PJ_onBF2kxnou5_JEFsqozUor08PhKQHOQ2f-_DxLUc,669
2336+charmhelpers/contrib/openstack/files/__pycache__/__init__.cpython-38.pyc,,
2337+charmhelpers/contrib/openstack/files/__pycache__/policy_rc_d_script.cpython-38.pyc,,
2338+charmhelpers/contrib/openstack/files/check_haproxy.sh,sha256=6qjVzMnm9iX6SzGQOhzGMzeSZNh5e50T654g4eVE_1Q,1041
2339+charmhelpers/contrib/openstack/files/check_haproxy_queue_depth.sh,sha256=WxfWaAP82ZQlyo6pDJCqPH2zyPa5HqHzzZL49cA39T0,968
2340+charmhelpers/contrib/openstack/files/policy_rc_d_script.py,sha256=SAuPN2xZl3hUz_gROwXU9_DK66tNfTLHBvxVF6-qAM4,6052
2341+charmhelpers/contrib/openstack/ha/__init__.py,sha256=Tw33cKAnl0OzKf_D5VqVyr__eWnyW6xHvhzG6SJUsps,574
2342+charmhelpers/contrib/openstack/ha/__pycache__/__init__.cpython-38.pyc,,
2343+charmhelpers/contrib/openstack/ha/__pycache__/utils.cpython-38.pyc,,
2344+charmhelpers/contrib/openstack/ha/utils.py,sha256=gyIil5PvyLVYBpumm4LL6BxXPUVZlZBjIFlL6Xy6Z_Q,12044
2345+charmhelpers/contrib/openstack/ip.py,sha256=W_dj3lW8uKxfUWvWqnE0_NQUTF_G9eWjdIliZ0ijhog,7940
2346+charmhelpers/contrib/openstack/keystone.py,sha256=ikFV8Ajj7BkkQ-xBp7Mg7370eMyUC4Gk3QxurNwNXHE,6550
2347+charmhelpers/contrib/openstack/neutron.py,sha256=-t5eBoB6S_B1Of2ZF0By0NdbVopTz2ROYrr2GXKztFc,12738
2348+charmhelpers/contrib/openstack/policy_rcd.py,sha256=WfbCKfG-vZauxKTVMEb4axDEHa-pzo1xacbGAQZPFeg,5783
2349+charmhelpers/contrib/openstack/policyd.py,sha256=Xm9y2mBUCCFDE8ex9i0w3JsqdoCbeuLdOyApWIFYfFM,33050
2350+charmhelpers/contrib/openstack/ssh_migrations.py,sha256=PM4ndr5BDRagRH6EIfDSxeNWFcXFogGWhkBcBoCW_LI,14069
2351+charmhelpers/contrib/openstack/templates/__init__.py,sha256=PJ_onBF2kxnou5_JEFsqozUor08PhKQHOQ2f-_DxLUc,669
2352+charmhelpers/contrib/openstack/templates/__pycache__/__init__.cpython-38.pyc,,
2353+charmhelpers/contrib/openstack/templates/ceph.conf,sha256=K88GaxfoZQaGV0YgFhyOc0btnxeG0zwIXcHwO_oOhSU,819
2354+charmhelpers/contrib/openstack/templates/git.upstart,sha256=odPhJchOT0dyDodCdXhOpaFMDOL-2li2jJ2jutcjP7c,541
2355+charmhelpers/contrib/openstack/templates/haproxy.cfg,sha256=6F3lnjD2r1cbTsCrwkTdRgnnZgCpXGSKzxetMY-Gdpo,2671
2356+charmhelpers/contrib/openstack/templates/logrotate,sha256=gGK4qTsgCZ6rSb7vulAAbTpmdzpGVc0B_a_88sLTjAs,181
2357+charmhelpers/contrib/openstack/templates/memcached.conf,sha256=IkPMWuHzbnwB2DKslUAEiuQk4np400NYn1fDP9T3Vso,1714
2358+charmhelpers/contrib/openstack/templates/openstack_https_frontend,sha256=xvwUbAw_nAXTiPzvonZlJVygVCQZ6GzAWRU69J4vgq8,1347
2359+charmhelpers/contrib/openstack/templates/openstack_https_frontend.conf,sha256=xvwUbAw_nAXTiPzvonZlJVygVCQZ6GzAWRU69J4vgq8,1347
2360+charmhelpers/contrib/openstack/templates/section-ceph-bluestore-compression,sha256=cqD_o3pJIM0O_eDhWyQZOohUpD98MARIvdyQzDo3cJE,1335
2361+charmhelpers/contrib/openstack/templates/section-keystone-authtoken,sha256=gfVZd-o7yXDO2keDESwdoSZiIDXtDwun5dWS1EI3NeI,400
2362+charmhelpers/contrib/openstack/templates/section-keystone-authtoken-legacy,sha256=ml9SRXkQ1lKhNtZhRT2ADGm7oUFAObFxfT8DpYnKbRE,406
2363+charmhelpers/contrib/openstack/templates/section-keystone-authtoken-mitaka,sha256=q0byZxLux3J2moLGJCGBzX_DQ-JvUCS6aVwFBUZ12WQ,772
2364+charmhelpers/contrib/openstack/templates/section-keystone-authtoken-v3only,sha256=4mDE-vdsn_9VD9bp_AYL4FLrX4FpMfuuZdloMuqRLWo,256
2365+charmhelpers/contrib/openstack/templates/section-oslo-cache,sha256=S6W7acRDZGh3UR29aYcxwulC3MWCRIpaB9lL9KG2OwI,130
2366+charmhelpers/contrib/openstack/templates/section-oslo-messaging-rabbit,sha256=90tfS3oujFv6MlIxPCJZsVxtXHoB4fo1sHKxyPvhTlU,211
2367+charmhelpers/contrib/openstack/templates/section-oslo-messaging-rabbit-ocata,sha256=9DU9eMzwp5OA2MqfC-fS8ehCGsb3lMIsQYiXdUpRoEE,222
2368+charmhelpers/contrib/openstack/templates/section-oslo-middleware,sha256=_Ie2iLTToU2FOiEMsNsS66faNAMSi0oz2yuY2Vbfo4E,71
2369+charmhelpers/contrib/openstack/templates/section-oslo-notifications,sha256=L-lkuVhwpBdqbx-XkE0828I_APklrhD0P-B0b4OsRa0,387
2370+charmhelpers/contrib/openstack/templates/section-placement,sha256=YGoDFtueuClFKDnOhBoSCm-k-tTKi2y3lmZuRI25FsM,556
2371+charmhelpers/contrib/openstack/templates/section-rabbitmq-oslo,sha256=81fX6vXsYrNSS_50bieIQOWlmMVfObRSEBU8KCHYfRQ,598
2372+charmhelpers/contrib/openstack/templates/section-zeromq,sha256=qBteJtj4tIAEOOc7kkAvnabDTpSyQKUNbNhUraEvoy4,353
2373+charmhelpers/contrib/openstack/templates/vendor_data.json,sha256=G0Jt3EfboVTfY-htXGN3IobJEp9apB0sRuEUb0KHNVU,22
2374+charmhelpers/contrib/openstack/templates/wsgi-openstack-api.conf,sha256=8K4SrNYTq6SjWwfHB9KqAsVi0kkxPbRud3SO4VYfKRI,2825
2375+charmhelpers/contrib/openstack/templates/wsgi-openstack-metadata.conf,sha256=8K4SrNYTq6SjWwfHB9KqAsVi0kkxPbRud3SO4VYfKRI,2825
2376+charmhelpers/contrib/openstack/templating.py,sha256=-Pr-JC7a46C-bYVKzRmKyY2S3XGyGmcULRNiqMh9V_k,14743
2377+charmhelpers/contrib/openstack/utils.py,sha256=5E903-fSr8w-B2HX-LoDMEogZUq-ujWYBcVsKefcl8g,91050
2378+charmhelpers/contrib/openstack/vaultlocker.py,sha256=a6jDooWjlHNHR0zLlekdBnKbyQzPBF4k0viT7j6CKKI,6995
2379+charmhelpers/contrib/peerstorage/__init__.py,sha256=q_48JDfhHqpzSwCLgHFmxgOdF23ftC23p3tB0ofANQk,9610
2380+charmhelpers/contrib/peerstorage/__pycache__/__init__.cpython-38.pyc,,
2381+charmhelpers/contrib/python.py,sha256=neyhpDKISpH_kJXrwrS85vckFrVMR11i5b6jdKzy31c,886
2382+charmhelpers/contrib/saltstack/__init__.py,sha256=cBZHwOETTMXLfW8V7jZZMYgLzycBZyHD0rG9jEGtIIw,3485
2383+charmhelpers/contrib/saltstack/__pycache__/__init__.cpython-38.pyc,,
2384+charmhelpers/contrib/ssl/__init__.py,sha256=75wkn7oclRerwNYmMkrzc09UQ3qOzNL9jhZhmYrA9zA,3734
2385+charmhelpers/contrib/ssl/__pycache__/__init__.cpython-38.pyc,,
2386+charmhelpers/contrib/ssl/__pycache__/service.cpython-38.pyc,,
2387+charmhelpers/contrib/ssl/service.py,sha256=0JA_BjNzvNHRFjz0MD2DZcu3bqww0-4mUxzDMivP_0o,9303
2388+charmhelpers/contrib/storage/__init__.py,sha256=cgDOTi6NEduIPlGmBYi69fC9cCaht22kCre0am-M2rk,584
2389+charmhelpers/contrib/storage/__pycache__/__init__.cpython-38.pyc,,
2390+charmhelpers/contrib/storage/linux/__init__.py,sha256=cgDOTi6NEduIPlGmBYi69fC9cCaht22kCre0am-M2rk,584
2391+charmhelpers/contrib/storage/linux/__pycache__/__init__.cpython-38.pyc,,
2392+charmhelpers/contrib/storage/linux/__pycache__/bcache.cpython-38.pyc,,
2393+charmhelpers/contrib/storage/linux/__pycache__/ceph.cpython-38.pyc,,
2394+charmhelpers/contrib/storage/linux/__pycache__/loopback.cpython-38.pyc,,
2395+charmhelpers/contrib/storage/linux/__pycache__/lvm.cpython-38.pyc,,
2396+charmhelpers/contrib/storage/linux/__pycache__/utils.cpython-38.pyc,,
2397+charmhelpers/contrib/storage/linux/bcache.py,sha256=zgsF8FEFvPDIyNgGIG74hDzPTnXYzJccq6KdfzxWC5k,2294
2398+charmhelpers/contrib/storage/linux/ceph.py,sha256=9ks9H04lXQneQ0WjNF2e8d71Cyc8QAZSqZQuw3qJAFo,87100
2399+charmhelpers/contrib/storage/linux/loopback.py,sha256=bWvQlo4c_6ylUyVmTZxQAcCcObeSvSb4o8tMZloW8ZU,2771
2400+charmhelpers/contrib/storage/linux/lvm.py,sha256=E9VNULuLkr1DeVIzGQ9RL2PwBtY_uVutHZ5brV7PM3Y,5582
2401+charmhelpers/contrib/storage/linux/utils.py,sha256=WxU2NG3d4v-CWMWir8Ga-m1kdujZYqaTt7M8O4vvNdo,4018
2402+charmhelpers/contrib/sysctl/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2403+charmhelpers/contrib/sysctl/__pycache__/__init__.cpython-38.pyc,,
2404+charmhelpers/contrib/sysctl/__pycache__/watermark_scale_factor.cpython-38.pyc,,
2405+charmhelpers/contrib/sysctl/watermark_scale_factor.py,sha256=BQtmmlq0Nx6ScLMTzIl_L_y7N7IEsY0MskkFU_YahLE,3388
2406+charmhelpers/contrib/templating/__init__.py,sha256=cgDOTi6NEduIPlGmBYi69fC9cCaht22kCre0am-M2rk,584
2407+charmhelpers/contrib/templating/__pycache__/__init__.cpython-38.pyc,,
2408+charmhelpers/contrib/templating/__pycache__/contexts.cpython-38.pyc,,
2409+charmhelpers/contrib/templating/__pycache__/jinja.cpython-38.pyc,,
2410+charmhelpers/contrib/templating/__pycache__/pyformat.cpython-38.pyc,,
2411+charmhelpers/contrib/templating/contexts.py,sha256=bhMlQTNFWF83dq7bW5O9a3hstuDVQxAkkJeIxCFrB-g,5240
2412+charmhelpers/contrib/templating/jinja.py,sha256=SooB8IPYPGXKAfI_z3IRyFkL3aIsigz481v2Nk-deaU,1761
2413+charmhelpers/contrib/templating/pyformat.py,sha256=XEiPZSPbI_gvJKG8ORzqnFcNOrJyILKbPqvRMyYWQu8,935
2414+charmhelpers/contrib/unison/__init__.py,sha256=WkvPoA9bJAzHf04rguoAlH-UE-fzRrwJAT7XzV5W7gI,10262
2415+charmhelpers/contrib/unison/__pycache__/__init__.cpython-38.pyc,,
2416+charmhelpers/coordinator.py,sha256=brnN4lezo2SmzW5EAAHECcFx0_nma9ewA2FuIGhHxMA,23485
2417+charmhelpers/core/__init__.py,sha256=cgDOTi6NEduIPlGmBYi69fC9cCaht22kCre0am-M2rk,584
2418+charmhelpers/core/__pycache__/__init__.cpython-38.pyc,,
2419+charmhelpers/core/__pycache__/decorators.cpython-38.pyc,,
2420+charmhelpers/core/__pycache__/files.cpython-38.pyc,,
2421+charmhelpers/core/__pycache__/fstab.cpython-38.pyc,,
2422+charmhelpers/core/__pycache__/hookenv.cpython-38.pyc,,
2423+charmhelpers/core/__pycache__/host.cpython-38.pyc,,
2424+charmhelpers/core/__pycache__/hugepage.cpython-38.pyc,,
2425+charmhelpers/core/__pycache__/kernel.cpython-38.pyc,,
2426+charmhelpers/core/__pycache__/strutils.cpython-38.pyc,,
2427+charmhelpers/core/__pycache__/sysctl.cpython-38.pyc,,
2428+charmhelpers/core/__pycache__/templating.cpython-38.pyc,,
2429+charmhelpers/core/__pycache__/unitdata.cpython-38.pyc,,
2430+charmhelpers/core/decorators.py,sha256=mdGH6gfxeA0-lbDG3MTFHvM8DzM1zw9dhWH8gR8TdVY,3217
2431+charmhelpers/core/files.py,sha256=rrwh-nvq4TrluH3oWPwA-tBpBNDTX3K9tJwsSQC3XxM,1657
2432+charmhelpers/core/fstab.py,sha256=DF7KX0VeTEbaI3F2ESqWDQZ7cCtyX4xT3-zHqtRT6jk,4189
2433+charmhelpers/core/hookenv.py,sha256=NkCPLGSrkr9gVriOomSYGPKPkXCOpxTTynxrtiWETP4,51829
2434+charmhelpers/core/host.py,sha256=vzl114O0YeL6ngV8URbn8fZzj8TASiIUTumL2fzjAf0,48084
2435+charmhelpers/core/host_factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2436+charmhelpers/core/host_factory/__pycache__/__init__.cpython-38.pyc,,
2437+charmhelpers/core/host_factory/__pycache__/centos.cpython-38.pyc,,
2438+charmhelpers/core/host_factory/__pycache__/ubuntu.cpython-38.pyc,,
2439+charmhelpers/core/host_factory/centos.py,sha256=TixRcKI_DC65GhC6N0WXwTVbu7lLuwkzkkLEQMd1qII,1924
2440+charmhelpers/core/host_factory/ubuntu.py,sha256=rXA1T3GpnOJZZEkAeHz8rWz-vndTkHF-dMz-WioyrIA,2914
2441+charmhelpers/core/hugepage.py,sha256=uek21StWW-qxGB0huKIvhfRCwp6Mw-lsHuoVKd2amu0,2659
2442+charmhelpers/core/kernel.py,sha256=jLutRvmQLyJvFa8MDeKmjY7iOHO8YOsaHpgbH32nY08,2178
2443+charmhelpers/core/kernel_factory/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2444+charmhelpers/core/kernel_factory/__pycache__/__init__.cpython-38.pyc,,
2445+charmhelpers/core/kernel_factory/__pycache__/centos.cpython-38.pyc,,
2446+charmhelpers/core/kernel_factory/__pycache__/ubuntu.cpython-38.pyc,,
2447+charmhelpers/core/kernel_factory/centos.py,sha256=tOBPiIq9Mmp7Ee2abWqz7WqRoiRf1PX4mxORCzehk70,538
2448+charmhelpers/core/kernel_factory/ubuntu.py,sha256=oGKJ5SxbgagjvY6cnUOsGN0WFuFu3XHipJjQltZZIkk,406
2449+charmhelpers/core/services/__init__.py,sha256=ZfT_5FZslewnV9B7Y9r-W_yK0HwtET8jUeKxXe9lhZU,644
2450+charmhelpers/core/services/__pycache__/__init__.cpython-38.pyc,,
2451+charmhelpers/core/services/__pycache__/base.cpython-38.pyc,,
2452+charmhelpers/core/services/__pycache__/helpers.cpython-38.pyc,,
2453+charmhelpers/core/services/base.py,sha256=6vrPDlGUZ3Ev0ncPk80qdDQib8QQEdUVtPiRChl7x3I,14587
2454+charmhelpers/core/services/helpers.py,sha256=mV4MctagKkVDCQNsHpsJfcxBO7YJgwKGF0rceE63A0k,10684
2455+charmhelpers/core/strutils.py,sha256=qWEWkC9vfLPXxOfDt6AxmW6IJsJjKP_q6QxHEuGoXFs,3992
2456+charmhelpers/core/sysctl.py,sha256=8uJANDAIUzSefi5wgqWlBr0vMlM5-OipxgLB76LDeZI,2348
2457+charmhelpers/core/templating.py,sha256=iXknCzzlickGwTjZnQ5g5cE0O36HZeWB_KXnjUdSJBI,3567
2458+charmhelpers/core/unitdata.py,sha256=oN4Dv6ZB0l0ajK1Q7ZmFdFXNtlObBRT_BMZWro9Fe8U,16345
2459+charmhelpers/fetch/__init__.py,sha256=FNpWidcPJSEvlK1SLgZF2FVp-dmz-6dMS-Yy-j7mKj4,6861
2460+charmhelpers/fetch/__pycache__/__init__.cpython-38.pyc,,
2461+charmhelpers/fetch/__pycache__/archiveurl.cpython-38.pyc,,
2462+charmhelpers/fetch/__pycache__/bzrurl.cpython-38.pyc,,
2463+charmhelpers/fetch/__pycache__/centos.cpython-38.pyc,,
2464+charmhelpers/fetch/__pycache__/giturl.cpython-38.pyc,,
2465+charmhelpers/fetch/__pycache__/snap.cpython-38.pyc,,
2466+charmhelpers/fetch/__pycache__/ubuntu.cpython-38.pyc,,
2467+charmhelpers/fetch/__pycache__/ubuntu_apt_pkg.cpython-38.pyc,,
2468+charmhelpers/fetch/archiveurl.py,sha256=wcaorzMQdJ4uHGaMm8NDswjteSTztsdBC0U794EWo00,6095
2469+charmhelpers/fetch/bzrurl.py,sha256=yPz23U4uBQajXWsBoNwyWzNW-CTwBdOcI4FDnSQB0jI,2547
2470+charmhelpers/fetch/centos.py,sha256=fwFsxODb9mAMNfSiyNByy1hhWpwO75VjXUE9UW7Pduw,5656
2471+charmhelpers/fetch/giturl.py,sha256=1fNV446JDVUvionmQtuZyMc64v_9liizwfryJs5g0cE,2520
2472+charmhelpers/fetch/python/__init__.py,sha256=XatrYz38fPzslqxSdVKB4T-S8u69pdiirVtKA6syQD0,584
2473+charmhelpers/fetch/python/__pycache__/__init__.cpython-38.pyc,,
2474+charmhelpers/fetch/python/__pycache__/debug.cpython-38.pyc,,
2475+charmhelpers/fetch/python/__pycache__/packages.cpython-38.pyc,,
2476+charmhelpers/fetch/python/__pycache__/rpdb.cpython-38.pyc,,
2477+charmhelpers/fetch/python/__pycache__/version.cpython-38.pyc,,
2478+charmhelpers/fetch/python/debug.py,sha256=O7KEdQBVc3d7AlS4qyVBnduiE_40SopYyhb0fNNeSEw,1595
2479+charmhelpers/fetch/python/packages.py,sha256=IWruxWDMaXYERRbYVShhB8cm60cgzWLzrtYCJAYJxKY,4755
2480+charmhelpers/fetch/python/rpdb.py,sha256=8bVkgR2A5IPMQVhiw3WZsPIUah3Si_3hicc5ytfPi-A,1910
2481+charmhelpers/fetch/python/version.py,sha256=PU-BN1jdVyNVygmDMEvPc7Ls1Wp43kTKyZvvD05KLuw,1062
2482+charmhelpers/fetch/snap.py,sha256=Ea8CcNP5F4NaZTSLxdrpqgZ7Bax1ROzpavSewMgVA3M,4295
2483+charmhelpers/fetch/ubuntu.py,sha256=Wf35QS3tWD0z34EXwG86qaW47x7QFY6bNVmlk30EI2A,38793
2484+charmhelpers/fetch/ubuntu_apt_pkg.py,sha256=Lss81SobnS4lJXE_0fUM2VJDNXXdu4sk6-igZdc53Co,10709
2485+charmhelpers/osplatform.py,sha256=vTOdFVPPbQITbesl_7EILJE-M0gd1iqI8vGoJx69eyg,1745
2486+charmhelpers/payload/__init__.py,sha256=BsLxY7GAAFKbd4-nMeZYhxXSQcf4HlaC4XAP06cNX6g,662
2487+charmhelpers/payload/__pycache__/__init__.cpython-38.pyc,,
2488+charmhelpers/payload/__pycache__/archive.cpython-38.pyc,,
2489+charmhelpers/payload/__pycache__/execd.cpython-38.pyc,,
2490+charmhelpers/payload/archive.py,sha256=b5TPzQ-mWFoz2Xtw0GvDTcgnonT3_4XVRj2kcrESZtU,2156
2491+charmhelpers/payload/execd.py,sha256=64WyEjhxnBiFivmtSXGYmFauuSyUYkRimlr9Cyq6xAI,2248
2492+charmhelpers/version.py,sha256=BP5L3qROqNAJNf3RnyryDzfdiMGyLjm_6OPjMUgVf6w,58
2493diff --git a/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/WHEEL b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/WHEEL
2494new file mode 100644
2495index 0000000..b552003
2496--- /dev/null
2497+++ b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/WHEEL
2498@@ -0,0 +1,5 @@
2499+Wheel-Version: 1.0
2500+Generator: bdist_wheel (0.34.2)
2501+Root-Is-Purelib: true
2502+Tag: py3-none-any
2503+
2504diff --git a/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/pbr.json b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/pbr.json
2505new file mode 100644
2506index 0000000..c585d02
2507--- /dev/null
2508+++ b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/pbr.json
2509@@ -0,0 +1 @@
2510+{"git_version": "26efcd0d", "is_release": true}
2511\ No newline at end of file
2512diff --git a/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/top_level.txt b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/top_level.txt
2513new file mode 100644
2514index 0000000..93cc907
2515--- /dev/null
2516+++ b/charms/appstream-generator/venv/charmhelpers-0.20.23.dist-info/top_level.txt
2517@@ -0,0 +1 @@
2518+charmhelpers
2519diff --git a/charms/appstream-generator/venv/charmhelpers/__init__.py b/charms/appstream-generator/venv/charmhelpers/__init__.py
2520new file mode 100644
2521index 0000000..1f57ed2
2522--- /dev/null
2523+++ b/charms/appstream-generator/venv/charmhelpers/__init__.py
2524@@ -0,0 +1,99 @@
2525+# Copyright 2014-2015 Canonical Limited.
2526+#
2527+# Licensed under the Apache License, Version 2.0 (the "License");
2528+# you may not use this file except in compliance with the License.
2529+# You may obtain a copy of the License at
2530+#
2531+# http://www.apache.org/licenses/LICENSE-2.0
2532+#
2533+# Unless required by applicable law or agreed to in writing, software
2534+# distributed under the License is distributed on an "AS IS" BASIS,
2535+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2536+# See the License for the specific language governing permissions and
2537+# limitations under the License.
2538+
2539+# Bootstrap charm-helpers, installing its dependencies if necessary using
2540+# only standard libraries.
2541+from __future__ import print_function
2542+from __future__ import absolute_import
2543+
2544+import functools
2545+import inspect
2546+import subprocess
2547+import sys
2548+
2549+try:
2550+ import six # NOQA:F401
2551+except ImportError:
2552+ if sys.version_info.major == 2:
2553+ subprocess.check_call(['apt-get', 'install', '-y', 'python-six'])
2554+ else:
2555+ subprocess.check_call(['apt-get', 'install', '-y', 'python3-six'])
2556+ import six # NOQA:F401
2557+
2558+try:
2559+ import yaml # NOQA:F401
2560+except ImportError:
2561+ if sys.version_info.major == 2:
2562+ subprocess.check_call(['apt-get', 'install', '-y', 'python-yaml'])
2563+ else:
2564+ subprocess.check_call(['apt-get', 'install', '-y', 'python3-yaml'])
2565+ import yaml # NOQA:F401
2566+
2567+
2568+# Holds a list of mapping of mangled function names that have been deprecated
2569+# using the @deprecate decorator below. This is so that the warning is only
2570+# printed once for each usage of the function.
2571+__deprecated_functions = {}
2572+
2573+
2574+def deprecate(warning, date=None, log=None):
2575+ """Add a deprecation warning the first time the function is used.
2576+
2577+ The date which is a string in semi-ISO8660 format indicates the year-month
2578+ that the function is officially going to be removed.
2579+
2580+ usage:
2581+
2582+ @deprecate('use core/fetch/add_source() instead', '2017-04')
2583+ def contributed_add_source_thing(...):
2584+ ...
2585+
2586+ And it then prints to the log ONCE that the function is deprecated.
2587+ The reason for passing the logging function (log) is so that hookenv.log
2588+ can be used for a charm if needed.
2589+
2590+ :param warning: String to indicate what is to be used instead.
2591+ :param date: Optional string in YYYY-MM format to indicate when the
2592+ function will definitely (probably) be removed.
2593+ :param log: The log function to call in order to log. If None, logs to
2594+ stdout
2595+ """
2596+ def wrap(f):
2597+
2598+ @functools.wraps(f)
2599+ def wrapped_f(*args, **kwargs):
2600+ try:
2601+ module = inspect.getmodule(f)
2602+ file = inspect.getsourcefile(f)
2603+ lines = inspect.getsourcelines(f)
2604+ f_name = "{}-{}-{}..{}-{}".format(
2605+ module.__name__, file, lines[0], lines[-1], f.__name__)
2606+ except (IOError, TypeError):
2607+ # assume it was local, so just use the name of the function
2608+ f_name = f.__name__
2609+ if f_name not in __deprecated_functions:
2610+ __deprecated_functions[f_name] = True
2611+ s = "DEPRECATION WARNING: Function {} is being removed".format(
2612+ f.__name__)
2613+ if date:
2614+ s = "{} on/around {}".format(s, date)
2615+ if warning:
2616+ s = "{} : {}".format(s, warning)
2617+ if log:
2618+ log(s)
2619+ else:
2620+ print(s)
2621+ return f(*args, **kwargs)
2622+ return wrapped_f
2623+ return wrap
2624diff --git a/charms/appstream-generator/venv/charmhelpers/__pycache__/__init__.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/__pycache__/__init__.cpython-38.pyc
2625new file mode 100644
2626index 0000000..baa5ef4
2627Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/__pycache__/__init__.cpython-38.pyc differ
2628diff --git a/charms/appstream-generator/venv/charmhelpers/__pycache__/context.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/__pycache__/context.cpython-38.pyc
2629new file mode 100644
2630index 0000000..3fc7d87
2631Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/__pycache__/context.cpython-38.pyc differ
2632diff --git a/charms/appstream-generator/venv/charmhelpers/__pycache__/coordinator.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/__pycache__/coordinator.cpython-38.pyc
2633new file mode 100644
2634index 0000000..6a3e413
2635Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/__pycache__/coordinator.cpython-38.pyc differ
2636diff --git a/charms/appstream-generator/venv/charmhelpers/__pycache__/osplatform.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/__pycache__/osplatform.cpython-38.pyc
2637new file mode 100644
2638index 0000000..0b5fdfe
2639Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/__pycache__/osplatform.cpython-38.pyc differ
2640diff --git a/charms/appstream-generator/venv/charmhelpers/__pycache__/version.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/__pycache__/version.cpython-38.pyc
2641new file mode 100644
2642index 0000000..fad275c
2643Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/__pycache__/version.cpython-38.pyc differ
2644diff --git a/charms/appstream-generator/venv/charmhelpers/cli/README.rst b/charms/appstream-generator/venv/charmhelpers/cli/README.rst
2645new file mode 100644
2646index 0000000..4c211ee
2647--- /dev/null
2648+++ b/charms/appstream-generator/venv/charmhelpers/cli/README.rst
2649@@ -0,0 +1,57 @@
2650+==========
2651+Commandant
2652+==========
2653+
2654+-----------------------------------------------------
2655+Automatic command-line interfaces to Python functions
2656+-----------------------------------------------------
2657+
2658+One of the benefits of ``libvirt`` is the uniformity of the interface: the C API (as well as the bindings in other languages) is a set of functions that accept parameters that are nearly identical to the command-line arguments. If you run ``virsh``, you get an interactive command prompt that supports all of the same commands that your shell scripts use as ``virsh`` subcommands.
2659+
2660+Command execution and stdio manipulation is the greatest common factor across all development systems in the POSIX environment. By exposing your functions as commands that manipulate streams of text, you can make life easier for all the Ruby and Erlang and Go programmers in your life.
2661+
2662+Goals
2663+=====
2664+
2665+* Single decorator to expose a function as a command.
2666+ * now two decorators - one "automatic" and one that allows authors to manipulate the arguments for fine-grained control.(MW)
2667+* Automatic analysis of function signature through ``inspect.getargspec()`` on python 2 or ``inspect.getfullargspec()`` on python 3
2668+* Command argument parser built automatically with ``argparse``
2669+* Interactive interpreter loop object made with ``Cmd``
2670+* Options to output structured return value data via ``pprint``, ``yaml`` or ``json`` dumps.
2671+
2672+Other Important Features that need writing
2673+------------------------------------------
2674+
2675+* Help and Usage documentation can be automatically generated, but it will be important to let users override this behaviour
2676+* The decorator should allow specifying further parameters to the parser's add_argument() calls, to specify types or to make arguments behave as boolean flags, etc.
2677+ - Filename arguments are important, as good practice is for functions to accept file objects as parameters.
2678+ - choices arguments help to limit bad input before the function is called
2679+* Some automatic behaviour could make for better defaults, once the user can override them.
2680+ - We could automatically detect arguments that default to False or True, and automatically support --no-foo for foo=True.
2681+ - We could automatically support hyphens as alternates for underscores
2682+ - Arguments defaulting to sequence types could support the ``append`` action.
2683+
2684+
2685+-----------------------------------------------------
2686+Implementing subcommands
2687+-----------------------------------------------------
2688+
2689+(WIP)
2690+
2691+So as to avoid dependencies on the cli module, subcommands should be defined separately from their implementations. The recommmendation would be to place definitions into separate modules near the implementations which they expose.
2692+
2693+Some examples::
2694+
2695+ from charmhelpers.cli import CommandLine
2696+ from charmhelpers.payload import execd
2697+ from charmhelpers.foo import bar
2698+
2699+ cli = CommandLine()
2700+
2701+ cli.subcommand(execd.execd_run)
2702+
2703+ @cli.subcommand_builder("bar", help="Bar baz qux")
2704+ def barcmd_builder(subparser):
2705+ subparser.add_argument('argument1', help="yackety")
2706+ return bar
2707diff --git a/charms/appstream-generator/venv/charmhelpers/cli/__init__.py b/charms/appstream-generator/venv/charmhelpers/cli/__init__.py
2708new file mode 100644
2709index 0000000..74ea729
2710--- /dev/null
2711+++ b/charms/appstream-generator/venv/charmhelpers/cli/__init__.py
2712@@ -0,0 +1,196 @@
2713+# Copyright 2014-2015 Canonical Limited.
2714+#
2715+# Licensed under the Apache License, Version 2.0 (the "License");
2716+# you may not use this file except in compliance with the License.
2717+# You may obtain a copy of the License at
2718+#
2719+# http://www.apache.org/licenses/LICENSE-2.0
2720+#
2721+# Unless required by applicable law or agreed to in writing, software
2722+# distributed under the License is distributed on an "AS IS" BASIS,
2723+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2724+# See the License for the specific language governing permissions and
2725+# limitations under the License.
2726+
2727+import inspect
2728+import argparse
2729+import sys
2730+
2731+import six
2732+from six.moves import zip
2733+
2734+import charmhelpers.core.unitdata
2735+
2736+
2737+class OutputFormatter(object):
2738+ def __init__(self, outfile=sys.stdout):
2739+ self.formats = (
2740+ "raw",
2741+ "json",
2742+ "py",
2743+ "yaml",
2744+ "csv",
2745+ "tab",
2746+ )
2747+ self.outfile = outfile
2748+
2749+ def add_arguments(self, argument_parser):
2750+ formatgroup = argument_parser.add_mutually_exclusive_group()
2751+ choices = self.supported_formats
2752+ formatgroup.add_argument("--format", metavar='FMT',
2753+ help="Select output format for returned data, "
2754+ "where FMT is one of: {}".format(choices),
2755+ choices=choices, default='raw')
2756+ for fmt in self.formats:
2757+ fmtfunc = getattr(self, fmt)
2758+ formatgroup.add_argument("-{}".format(fmt[0]),
2759+ "--{}".format(fmt), action='store_const',
2760+ const=fmt, dest='format',
2761+ help=fmtfunc.__doc__)
2762+
2763+ @property
2764+ def supported_formats(self):
2765+ return self.formats
2766+
2767+ def raw(self, output):
2768+ """Output data as raw string (default)"""
2769+ if isinstance(output, (list, tuple)):
2770+ output = '\n'.join(map(str, output))
2771+ self.outfile.write(str(output))
2772+
2773+ def py(self, output):
2774+ """Output data as a nicely-formatted python data structure"""
2775+ import pprint
2776+ pprint.pprint(output, stream=self.outfile)
2777+
2778+ def json(self, output):
2779+ """Output data in JSON format"""
2780+ import json
2781+ json.dump(output, self.outfile)
2782+
2783+ def yaml(self, output):
2784+ """Output data in YAML format"""
2785+ import yaml
2786+ yaml.safe_dump(output, self.outfile)
2787+
2788+ def csv(self, output):
2789+ """Output data as excel-compatible CSV"""
2790+ import csv
2791+ csvwriter = csv.writer(self.outfile)
2792+ csvwriter.writerows(output)
2793+
2794+ def tab(self, output):
2795+ """Output data in excel-compatible tab-delimited format"""
2796+ import csv
2797+ csvwriter = csv.writer(self.outfile, dialect=csv.excel_tab)
2798+ csvwriter.writerows(output)
2799+
2800+ def format_output(self, output, fmt='raw'):
2801+ fmtfunc = getattr(self, fmt)
2802+ fmtfunc(output)
2803+
2804+
2805+class CommandLine(object):
2806+ argument_parser = None
2807+ subparsers = None
2808+ formatter = None
2809+ exit_code = 0
2810+
2811+ def __init__(self):
2812+ if not self.argument_parser:
2813+ self.argument_parser = argparse.ArgumentParser(description='Perform common charm tasks')
2814+ if not self.formatter:
2815+ self.formatter = OutputFormatter()
2816+ self.formatter.add_arguments(self.argument_parser)
2817+ if not self.subparsers:
2818+ self.subparsers = self.argument_parser.add_subparsers(help='Commands')
2819+
2820+ def subcommand(self, command_name=None):
2821+ """
2822+ Decorate a function as a subcommand. Use its arguments as the
2823+ command-line arguments"""
2824+ def wrapper(decorated):
2825+ cmd_name = command_name or decorated.__name__
2826+ subparser = self.subparsers.add_parser(cmd_name,
2827+ description=decorated.__doc__)
2828+ for args, kwargs in describe_arguments(decorated):
2829+ subparser.add_argument(*args, **kwargs)
2830+ subparser.set_defaults(func=decorated)
2831+ return decorated
2832+ return wrapper
2833+
2834+ def test_command(self, decorated):
2835+ """
2836+ Subcommand is a boolean test function, so bool return values should be
2837+ converted to a 0/1 exit code.
2838+ """
2839+ decorated._cli_test_command = True
2840+ return decorated
2841+
2842+ def no_output(self, decorated):
2843+ """
2844+ Subcommand is not expected to return a value, so don't print a spurious None.
2845+ """
2846+ decorated._cli_no_output = True
2847+ return decorated
2848+
2849+ def subcommand_builder(self, command_name, description=None):
2850+ """
2851+ Decorate a function that builds a subcommand. Builders should accept a
2852+ single argument (the subparser instance) and return the function to be
2853+ run as the command."""
2854+ def wrapper(decorated):
2855+ subparser = self.subparsers.add_parser(command_name)
2856+ func = decorated(subparser)
2857+ subparser.set_defaults(func=func)
2858+ subparser.description = description or func.__doc__
2859+ return wrapper
2860+
2861+ def run(self):
2862+ "Run cli, processing arguments and executing subcommands."
2863+ arguments = self.argument_parser.parse_args()
2864+ if six.PY2:
2865+ argspec = inspect.getargspec(arguments.func)
2866+ else:
2867+ argspec = inspect.getfullargspec(arguments.func)
2868+ vargs = []
2869+ for arg in argspec.args:
2870+ vargs.append(getattr(arguments, arg))
2871+ if argspec.varargs:
2872+ vargs.extend(getattr(arguments, argspec.varargs))
2873+ output = arguments.func(*vargs)
2874+ if getattr(arguments.func, '_cli_test_command', False):
2875+ self.exit_code = 0 if output else 1
2876+ output = ''
2877+ if getattr(arguments.func, '_cli_no_output', False):
2878+ output = ''
2879+ self.formatter.format_output(output, arguments.format)
2880+ if charmhelpers.core.unitdata._KV:
2881+ charmhelpers.core.unitdata._KV.flush()
2882+
2883+
2884+cmdline = CommandLine()
2885+
2886+
2887+def describe_arguments(func):
2888+ """
2889+ Analyze a function's signature and return a data structure suitable for
2890+ passing in as arguments to an argparse parser's add_argument() method."""
2891+
2892+ if six.PY2:
2893+ argspec = inspect.getargspec(func)
2894+ else:
2895+ argspec = inspect.getfullargspec(func)
2896+ # we should probably raise an exception somewhere if func includes **kwargs
2897+ if argspec.defaults:
2898+ positional_args = argspec.args[:-len(argspec.defaults)]
2899+ keyword_names = argspec.args[-len(argspec.defaults):]
2900+ for arg, default in zip(keyword_names, argspec.defaults):
2901+ yield ('--{}'.format(arg),), {'default': default}
2902+ else:
2903+ positional_args = argspec.args
2904+
2905+ for arg in positional_args:
2906+ yield (arg,), {}
2907+ if argspec.varargs:
2908+ yield (argspec.varargs,), {'nargs': '*'}
2909diff --git a/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/__init__.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/__init__.cpython-38.pyc
2910new file mode 100644
2911index 0000000..147dcce
2912Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/__init__.cpython-38.pyc differ
2913diff --git a/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/benchmark.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/benchmark.cpython-38.pyc
2914new file mode 100644
2915index 0000000..2f96b1e
2916Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/benchmark.cpython-38.pyc differ
2917diff --git a/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/commands.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/commands.cpython-38.pyc
2918new file mode 100644
2919index 0000000..dcb10a5
2920Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/commands.cpython-38.pyc differ
2921diff --git a/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/hookenv.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/hookenv.cpython-38.pyc
2922new file mode 100644
2923index 0000000..47bbd05
2924Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/hookenv.cpython-38.pyc differ
2925diff --git a/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/host.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/host.cpython-38.pyc
2926new file mode 100644
2927index 0000000..e2ed780
2928Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/host.cpython-38.pyc differ
2929diff --git a/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/unitdata.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/unitdata.cpython-38.pyc
2930new file mode 100644
2931index 0000000..28911a1
2932Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/cli/__pycache__/unitdata.cpython-38.pyc differ
2933diff --git a/charms/appstream-generator/venv/charmhelpers/cli/benchmark.py b/charms/appstream-generator/venv/charmhelpers/cli/benchmark.py
2934new file mode 100644
2935index 0000000..303af14
2936--- /dev/null
2937+++ b/charms/appstream-generator/venv/charmhelpers/cli/benchmark.py
2938@@ -0,0 +1,34 @@
2939+# Copyright 2014-2015 Canonical Limited.
2940+#
2941+# Licensed under the Apache License, Version 2.0 (the "License");
2942+# you may not use this file except in compliance with the License.
2943+# You may obtain a copy of the License at
2944+#
2945+# http://www.apache.org/licenses/LICENSE-2.0
2946+#
2947+# Unless required by applicable law or agreed to in writing, software
2948+# distributed under the License is distributed on an "AS IS" BASIS,
2949+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2950+# See the License for the specific language governing permissions and
2951+# limitations under the License.
2952+
2953+from . import cmdline
2954+from charmhelpers.contrib.benchmark import Benchmark
2955+
2956+
2957+@cmdline.subcommand(command_name='benchmark-start')
2958+def start():
2959+ Benchmark.start()
2960+
2961+
2962+@cmdline.subcommand(command_name='benchmark-finish')
2963+def finish():
2964+ Benchmark.finish()
2965+
2966+
2967+@cmdline.subcommand_builder('benchmark-composite', description="Set the benchmark composite score")
2968+def service(subparser):
2969+ subparser.add_argument("value", help="The composite score.")
2970+ subparser.add_argument("units", help="The units the composite score represents, i.e., 'reads/sec'.")
2971+ subparser.add_argument("direction", help="'asc' if a lower score is better, 'desc' if a higher score is better.")
2972+ return Benchmark.set_composite_score
2973diff --git a/charms/appstream-generator/venv/charmhelpers/cli/commands.py b/charms/appstream-generator/venv/charmhelpers/cli/commands.py
2974new file mode 100644
2975index 0000000..b931056
2976--- /dev/null
2977+++ b/charms/appstream-generator/venv/charmhelpers/cli/commands.py
2978@@ -0,0 +1,30 @@
2979+# Copyright 2014-2015 Canonical Limited.
2980+#
2981+# Licensed under the Apache License, Version 2.0 (the "License");
2982+# you may not use this file except in compliance with the License.
2983+# You may obtain a copy of the License at
2984+#
2985+# http://www.apache.org/licenses/LICENSE-2.0
2986+#
2987+# Unless required by applicable law or agreed to in writing, software
2988+# distributed under the License is distributed on an "AS IS" BASIS,
2989+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
2990+# See the License for the specific language governing permissions and
2991+# limitations under the License.
2992+
2993+"""
2994+This module loads sub-modules into the python runtime so they can be
2995+discovered via the inspect module. In order to prevent flake8 from (rightfully)
2996+telling us these are unused modules, throw a ' # noqa' at the end of each import
2997+so that the warning is suppressed.
2998+"""
2999+
3000+from . import CommandLine # noqa
3001+
3002+"""
3003+Import the sub-modules which have decorated subcommands to register with chlp.
3004+"""
3005+from . import host # noqa
3006+from . import benchmark # noqa
3007+from . import unitdata # noqa
3008+from . import hookenv # noqa
3009diff --git a/charms/appstream-generator/venv/charmhelpers/cli/hookenv.py b/charms/appstream-generator/venv/charmhelpers/cli/hookenv.py
3010new file mode 100644
3011index 0000000..bd72f44
3012--- /dev/null
3013+++ b/charms/appstream-generator/venv/charmhelpers/cli/hookenv.py
3014@@ -0,0 +1,21 @@
3015+# Copyright 2014-2015 Canonical Limited.
3016+#
3017+# Licensed under the Apache License, Version 2.0 (the "License");
3018+# you may not use this file except in compliance with the License.
3019+# You may obtain a copy of the License at
3020+#
3021+# http://www.apache.org/licenses/LICENSE-2.0
3022+#
3023+# Unless required by applicable law or agreed to in writing, software
3024+# distributed under the License is distributed on an "AS IS" BASIS,
3025+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3026+# See the License for the specific language governing permissions and
3027+# limitations under the License.
3028+
3029+from . import cmdline
3030+from charmhelpers.core import hookenv
3031+
3032+
3033+cmdline.subcommand('relation-id')(hookenv.relation_id._wrapped)
3034+cmdline.subcommand('service-name')(hookenv.service_name)
3035+cmdline.subcommand('remote-service-name')(hookenv.remote_service_name._wrapped)
3036diff --git a/charms/appstream-generator/venv/charmhelpers/cli/host.py b/charms/appstream-generator/venv/charmhelpers/cli/host.py
3037new file mode 100644
3038index 0000000..4039684
3039--- /dev/null
3040+++ b/charms/appstream-generator/venv/charmhelpers/cli/host.py
3041@@ -0,0 +1,29 @@
3042+# Copyright 2014-2015 Canonical Limited.
3043+#
3044+# Licensed under the Apache License, Version 2.0 (the "License");
3045+# you may not use this file except in compliance with the License.
3046+# You may obtain a copy of the License at
3047+#
3048+# http://www.apache.org/licenses/LICENSE-2.0
3049+#
3050+# Unless required by applicable law or agreed to in writing, software
3051+# distributed under the License is distributed on an "AS IS" BASIS,
3052+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3053+# See the License for the specific language governing permissions and
3054+# limitations under the License.
3055+
3056+from . import cmdline
3057+from charmhelpers.core import host
3058+
3059+
3060+@cmdline.subcommand()
3061+def mounts():
3062+ "List mounts"
3063+ return host.mounts()
3064+
3065+
3066+@cmdline.subcommand_builder('service', description="Control system services")
3067+def service(subparser):
3068+ subparser.add_argument("action", help="The action to perform (start, stop, etc...)")
3069+ subparser.add_argument("service_name", help="Name of the service to control")
3070+ return host.service
3071diff --git a/charms/appstream-generator/venv/charmhelpers/cli/unitdata.py b/charms/appstream-generator/venv/charmhelpers/cli/unitdata.py
3072new file mode 100644
3073index 0000000..acce846
3074--- /dev/null
3075+++ b/charms/appstream-generator/venv/charmhelpers/cli/unitdata.py
3076@@ -0,0 +1,46 @@
3077+# Copyright 2014-2015 Canonical Limited.
3078+#
3079+# Licensed under the Apache License, Version 2.0 (the "License");
3080+# you may not use this file except in compliance with the License.
3081+# You may obtain a copy of the License at
3082+#
3083+# http://www.apache.org/licenses/LICENSE-2.0
3084+#
3085+# Unless required by applicable law or agreed to in writing, software
3086+# distributed under the License is distributed on an "AS IS" BASIS,
3087+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3088+# See the License for the specific language governing permissions and
3089+# limitations under the License.
3090+
3091+from . import cmdline
3092+from charmhelpers.core import unitdata
3093+
3094+
3095+@cmdline.subcommand_builder('unitdata', description="Store and retrieve data")
3096+def unitdata_cmd(subparser):
3097+ nested = subparser.add_subparsers()
3098+
3099+ get_cmd = nested.add_parser('get', help='Retrieve data')
3100+ get_cmd.add_argument('key', help='Key to retrieve the value of')
3101+ get_cmd.set_defaults(action='get', value=None)
3102+
3103+ getrange_cmd = nested.add_parser('getrange', help='Retrieve multiple data')
3104+ getrange_cmd.add_argument('key', metavar='prefix',
3105+ help='Prefix of the keys to retrieve')
3106+ getrange_cmd.set_defaults(action='getrange', value=None)
3107+
3108+ set_cmd = nested.add_parser('set', help='Store data')
3109+ set_cmd.add_argument('key', help='Key to set')
3110+ set_cmd.add_argument('value', help='Value to store')
3111+ set_cmd.set_defaults(action='set')
3112+
3113+ def _unitdata_cmd(action, key, value):
3114+ if action == 'get':
3115+ return unitdata.kv().get(key)
3116+ elif action == 'getrange':
3117+ return unitdata.kv().getrange(key)
3118+ elif action == 'set':
3119+ unitdata.kv().set(key, value)
3120+ unitdata.kv().flush()
3121+ return ''
3122+ return _unitdata_cmd
3123diff --git a/charms/appstream-generator/venv/charmhelpers/context.py b/charms/appstream-generator/venv/charmhelpers/context.py
3124new file mode 100644
3125index 0000000..0186474
3126--- /dev/null
3127+++ b/charms/appstream-generator/venv/charmhelpers/context.py
3128@@ -0,0 +1,205 @@
3129+# Copyright 2015 Canonical Limited.
3130+#
3131+# Licensed under the Apache License, Version 2.0 (the "License");
3132+# you may not use this file except in compliance with the License.
3133+# You may obtain a copy of the License at
3134+#
3135+# http://www.apache.org/licenses/LICENSE-2.0
3136+#
3137+# Unless required by applicable law or agreed to in writing, software
3138+# distributed under the License is distributed on an "AS IS" BASIS,
3139+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3140+# See the License for the specific language governing permissions and
3141+# limitations under the License.
3142+
3143+'''
3144+A Pythonic API to interact with the charm hook environment.
3145+
3146+:author: Stuart Bishop <stuart.bishop@canonical.com>
3147+'''
3148+
3149+import six
3150+
3151+from charmhelpers.core import hookenv
3152+
3153+from collections import OrderedDict
3154+if six.PY3:
3155+ from collections import UserDict # pragma: nocover
3156+else:
3157+ from UserDict import IterableUserDict as UserDict # pragma: nocover
3158+
3159+
3160+class Relations(OrderedDict):
3161+ '''Mapping relation name -> relation id -> Relation.
3162+
3163+ >>> rels = Relations()
3164+ >>> rels['sprog']['sprog:12']['client/6']['widget']
3165+ 'remote widget'
3166+ >>> rels['sprog']['sprog:12'].local['widget'] = 'local widget'
3167+ >>> rels['sprog']['sprog:12'].local['widget']
3168+ 'local widget'
3169+ >>> rels.peer.local['widget']
3170+ 'local widget on the peer relation'
3171+ '''
3172+ def __init__(self):
3173+ super(Relations, self).__init__()
3174+ for relname in sorted(hookenv.relation_types()):
3175+ self[relname] = OrderedDict()
3176+ relids = hookenv.relation_ids(relname)
3177+ relids.sort(key=lambda x: int(x.split(':', 1)[-1]))
3178+ for relid in relids:
3179+ self[relname][relid] = Relation(relid)
3180+
3181+ @property
3182+ def peer(self):
3183+ peer_relid = hookenv.peer_relation_id()
3184+ for rels in self.values():
3185+ if peer_relid in rels:
3186+ return rels[peer_relid]
3187+
3188+
3189+class Relation(OrderedDict):
3190+ '''Mapping of unit -> remote RelationInfo for a relation.
3191+
3192+ This is an OrderedDict mapping, ordered numerically by
3193+ by unit number.
3194+
3195+ Also provides access to the local RelationInfo, and peer RelationInfo
3196+ instances by the 'local' and 'peers' attributes.
3197+
3198+ >>> r = Relation('sprog:12')
3199+ >>> r.keys()
3200+ ['client/9', 'client/10'] # Ordered numerically
3201+ >>> r['client/10']['widget'] # A remote RelationInfo setting
3202+ 'remote widget'
3203+ >>> r.local['widget'] # The local RelationInfo setting
3204+ 'local widget'
3205+ '''
3206+ relid = None # The relation id.
3207+ relname = None # The relation name (also known as relation type).
3208+ service = None # The remote service name, if known.
3209+ local = None # The local end's RelationInfo.
3210+ peers = None # Map of peer -> RelationInfo. None if no peer relation.
3211+
3212+ def __init__(self, relid):
3213+ remote_units = hookenv.related_units(relid)
3214+ remote_units.sort(key=lambda u: int(u.split('/', 1)[-1]))
3215+ super(Relation, self).__init__((unit, RelationInfo(relid, unit))
3216+ for unit in remote_units)
3217+
3218+ self.relname = relid.split(':', 1)[0]
3219+ self.relid = relid
3220+ self.local = RelationInfo(relid, hookenv.local_unit())
3221+
3222+ for relinfo in self.values():
3223+ self.service = relinfo.service
3224+ break
3225+
3226+ # If we have peers, and they have joined both the provided peer
3227+ # relation and this relation, we can peek at their data too.
3228+ # This is useful for creating consensus without leadership.
3229+ peer_relid = hookenv.peer_relation_id()
3230+ if peer_relid and peer_relid != relid:
3231+ peers = hookenv.related_units(peer_relid)
3232+ if peers:
3233+ peers.sort(key=lambda u: int(u.split('/', 1)[-1]))
3234+ self.peers = OrderedDict((peer, RelationInfo(relid, peer))
3235+ for peer in peers)
3236+ else:
3237+ self.peers = OrderedDict()
3238+ else:
3239+ self.peers = None
3240+
3241+ def __str__(self):
3242+ return '{} ({})'.format(self.relid, self.service)
3243+
3244+
3245+class RelationInfo(UserDict):
3246+ '''The bag of data at an end of a relation.
3247+
3248+ Every unit participating in a relation has a single bag of
3249+ data associated with that relation. This is that bag.
3250+
3251+ The bag of data for the local unit may be updated. Remote data
3252+ is immutable and will remain static for the duration of the hook.
3253+
3254+ Changes made to the local units relation data only become visible
3255+ to other units after the hook completes successfully. If the hook
3256+ does not complete successfully, the changes are rolled back.
3257+
3258+ Unlike standard Python mappings, setting an item to None is the
3259+ same as deleting it.
3260+
3261+ >>> relinfo = RelationInfo('db:12') # Default is the local unit.
3262+ >>> relinfo['user'] = 'fred'
3263+ >>> relinfo['user']
3264+ 'fred'
3265+ >>> relinfo['user'] = None
3266+ >>> 'fred' in relinfo
3267+ False
3268+
3269+ This class wraps hookenv.relation_get and hookenv.relation_set.
3270+ All caching is left up to these two methods to avoid synchronization
3271+ issues. Data is only loaded on demand.
3272+ '''
3273+ relid = None # The relation id.
3274+ relname = None # The relation name (also know as the relation type).
3275+ unit = None # The unit id.
3276+ number = None # The unit number (integer).
3277+ service = None # The service name.
3278+
3279+ def __init__(self, relid, unit):
3280+ self.relname = relid.split(':', 1)[0]
3281+ self.relid = relid
3282+ self.unit = unit
3283+ self.service, num = self.unit.split('/', 1)
3284+ self.number = int(num)
3285+
3286+ def __str__(self):
3287+ return '{} ({})'.format(self.relid, self.unit)
3288+
3289+ @property
3290+ def data(self):
3291+ return hookenv.relation_get(rid=self.relid, unit=self.unit)
3292+
3293+ def __setitem__(self, key, value):
3294+ if self.unit != hookenv.local_unit():
3295+ raise TypeError('Attempting to set {} on remote unit {}'
3296+ ''.format(key, self.unit))
3297+ if value is not None and not isinstance(value, six.string_types):
3298+ # We don't do implicit casting. This would cause simple
3299+ # types like integers to be read back as strings in subsequent
3300+ # hooks, and mutable types would require a lot of wrapping
3301+ # to ensure relation-set gets called when they are mutated.
3302+ raise ValueError('Only string values allowed')
3303+ hookenv.relation_set(self.relid, {key: value})
3304+
3305+ def __delitem__(self, key):
3306+ # Deleting a key and setting it to null is the same thing in
3307+ # Juju relations.
3308+ self[key] = None
3309+
3310+
3311+class Leader(UserDict):
3312+ def __init__(self):
3313+ pass # Don't call superclass initializer, as it will nuke self.data
3314+
3315+ @property
3316+ def data(self):
3317+ return hookenv.leader_get()
3318+
3319+ def __setitem__(self, key, value):
3320+ if not hookenv.is_leader():
3321+ raise TypeError('Not the leader. Cannot change leader settings.')
3322+ if value is not None and not isinstance(value, six.string_types):
3323+ # We don't do implicit casting. This would cause simple
3324+ # types like integers to be read back as strings in subsequent
3325+ # hooks, and mutable types would require a lot of wrapping
3326+ # to ensure leader-set gets called when they are mutated.
3327+ raise ValueError('Only string values allowed')
3328+ hookenv.leader_set({key: value})
3329+
3330+ def __delitem__(self, key):
3331+ # Deleting a key and setting it to null is the same thing in
3332+ # Juju leadership settings.
3333+ self[key] = None
3334diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/__init__.py b/charms/appstream-generator/venv/charmhelpers/contrib/__init__.py
3335new file mode 100644
3336index 0000000..d7567b8
3337--- /dev/null
3338+++ b/charms/appstream-generator/venv/charmhelpers/contrib/__init__.py
3339@@ -0,0 +1,13 @@
3340+# Copyright 2014-2015 Canonical Limited.
3341+#
3342+# Licensed under the Apache License, Version 2.0 (the "License");
3343+# you may not use this file except in compliance with the License.
3344+# You may obtain a copy of the License at
3345+#
3346+# http://www.apache.org/licenses/LICENSE-2.0
3347+#
3348+# Unless required by applicable law or agreed to in writing, software
3349+# distributed under the License is distributed on an "AS IS" BASIS,
3350+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3351+# See the License for the specific language governing permissions and
3352+# limitations under the License.
3353diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/__pycache__/__init__.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/__pycache__/__init__.cpython-38.pyc
3354new file mode 100644
3355index 0000000..ca347f8
3356Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/__pycache__/__init__.cpython-38.pyc differ
3357diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/__pycache__/python.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/__pycache__/python.cpython-38.pyc
3358new file mode 100644
3359index 0000000..43765f3
3360Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/__pycache__/python.cpython-38.pyc differ
3361diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/ansible/__init__.py b/charms/appstream-generator/venv/charmhelpers/contrib/ansible/__init__.py
3362new file mode 100644
3363index 0000000..8a56972
3364--- /dev/null
3365+++ b/charms/appstream-generator/venv/charmhelpers/contrib/ansible/__init__.py
3366@@ -0,0 +1,306 @@
3367+# Copyright 2014-2015 Canonical Limited.
3368+#
3369+# Licensed under the Apache License, Version 2.0 (the "License");
3370+# you may not use this file except in compliance with the License.
3371+# You may obtain a copy of the License at
3372+#
3373+# http://www.apache.org/licenses/LICENSE-2.0
3374+#
3375+# Unless required by applicable law or agreed to in writing, software
3376+# distributed under the License is distributed on an "AS IS" BASIS,
3377+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3378+# See the License for the specific language governing permissions and
3379+# limitations under the License.
3380+
3381+# Copyright 2013 Canonical Ltd.
3382+#
3383+# Authors:
3384+# Charm Helpers Developers <juju@lists.ubuntu.com>
3385+"""
3386+The ansible package enables you to easily use the configuration management
3387+tool `Ansible`_ to setup and configure your charm. All of your charm
3388+configuration options and relation-data are available as regular Ansible
3389+variables which can be used in your playbooks and templates.
3390+
3391+.. _Ansible: https://www.ansible.com/
3392+
3393+Usage
3394+=====
3395+
3396+Here is an example directory structure for a charm to get you started::
3397+
3398+ charm-ansible-example/
3399+ |-- ansible
3400+ | |-- playbook.yaml
3401+ | `-- templates
3402+ | `-- example.j2
3403+ |-- config.yaml
3404+ |-- copyright
3405+ |-- icon.svg
3406+ |-- layer.yaml
3407+ |-- metadata.yaml
3408+ |-- reactive
3409+ | `-- example.py
3410+ |-- README.md
3411+
3412+Running a playbook called ``playbook.yaml`` when the ``install`` hook is run
3413+can be as simple as::
3414+
3415+ from charmhelpers.contrib import ansible
3416+ from charms.reactive import hook
3417+
3418+ @hook('install')
3419+ def install():
3420+ ansible.install_ansible_support()
3421+ ansible.apply_playbook('ansible/playbook.yaml')
3422+
3423+Here is an example playbook that uses the ``template`` module to template the
3424+file ``example.j2`` to the charm host and then uses the ``debug`` module to
3425+print out all the host and Juju variables that you can use in your playbooks.
3426+Note that you must target ``localhost`` as the playbook is run locally on the
3427+charm host::
3428+
3429+ ---
3430+ - hosts: localhost
3431+ tasks:
3432+ - name: Template a file
3433+ template:
3434+ src: templates/example.j2
3435+ dest: /tmp/example.j2
3436+
3437+ - name: Print all variables available to Ansible
3438+ debug:
3439+ var: vars
3440+
3441+Read more online about `playbooks`_ and standard Ansible `modules`_.
3442+
3443+.. _playbooks: https://docs.ansible.com/ansible/latest/user_guide/playbooks.html
3444+.. _modules: https://docs.ansible.com/ansible/latest/user_guide/modules.html
3445+
3446+A further feature of the Ansible hooks is to provide a light weight "action"
3447+scripting tool. This is a decorator that you apply to a function, and that
3448+function can now receive cli args, and can pass extra args to the playbook::
3449+
3450+ @hooks.action()
3451+ def some_action(amount, force="False"):
3452+ "Usage: some-action AMOUNT [force=True]" # <-- shown on error
3453+ # process the arguments
3454+ # do some calls
3455+ # return extra-vars to be passed to ansible-playbook
3456+ return {
3457+ 'amount': int(amount),
3458+ 'type': force,
3459+ }
3460+
3461+You can now create a symlink to hooks.py that can be invoked like a hook, but
3462+with cli params::
3463+
3464+ # link actions/some-action to hooks/hooks.py
3465+
3466+ actions/some-action amount=10 force=true
3467+
3468+Install Ansible via pip
3469+=======================
3470+
3471+If you want to install a specific version of Ansible via pip instead of
3472+``install_ansible_support`` which uses APT, consider using the layer options
3473+of `layer-basic`_ to install Ansible in a virtualenv::
3474+
3475+ options:
3476+ basic:
3477+ python_packages: ['ansible==2.9.0']
3478+ include_system_packages: true
3479+ use_venv: true
3480+
3481+.. _layer-basic: https://charmsreactive.readthedocs.io/en/latest/layer-basic.html#layer-configuration
3482+
3483+"""
3484+import os
3485+import json
3486+import stat
3487+import subprocess
3488+import functools
3489+
3490+import charmhelpers.contrib.templating.contexts
3491+import charmhelpers.core.host
3492+import charmhelpers.core.hookenv
3493+import charmhelpers.fetch
3494+
3495+
3496+charm_dir = os.environ.get('CHARM_DIR', '')
3497+ansible_hosts_path = '/etc/ansible/hosts'
3498+# Ansible will automatically include any vars in the following
3499+# file in its inventory when run locally.
3500+ansible_vars_path = '/etc/ansible/host_vars/localhost'
3501+
3502+
3503+def install_ansible_support(from_ppa=True, ppa_location='ppa:ansible/ansible'):
3504+ """Installs Ansible via APT.
3505+
3506+ By default this installs Ansible from the `PPA`_ linked from
3507+ the Ansible `website`_ or from a PPA set in ``ppa_location``.
3508+
3509+ .. _PPA: https://launchpad.net/~ansible/+archive/ubuntu/ansible
3510+ .. _website: http://docs.ansible.com/intro_installation.html#latest-releases-via-apt-ubuntu
3511+
3512+ If ``from_ppa`` is ``False``, then Ansible will be installed from
3513+ Ubuntu's Universe repositories.
3514+ """
3515+ if from_ppa:
3516+ charmhelpers.fetch.add_source(ppa_location)
3517+ charmhelpers.fetch.apt_update(fatal=True)
3518+ charmhelpers.fetch.apt_install('ansible')
3519+ with open(ansible_hosts_path, 'w+') as hosts_file:
3520+ hosts_file.write('localhost ansible_connection=local ansible_remote_tmp=/root/.ansible/tmp')
3521+
3522+
3523+def apply_playbook(playbook, tags=None, extra_vars=None):
3524+ """Run a playbook.
3525+
3526+ This helper runs a playbook with juju state variables as context,
3527+ therefore variables set in application config can be used directly.
3528+ List of tags (--tags) and dictionary with extra_vars (--extra-vars)
3529+ can be passed as additional parameters.
3530+
3531+ Read more about playbook `_variables`_ online.
3532+
3533+ .. _variables: https://docs.ansible.com/ansible/latest/user_guide/playbooks_variables.html
3534+
3535+ Example::
3536+
3537+ # Run ansible/playbook.yaml with tag install and pass extra
3538+ # variables var_a and var_b
3539+ apply_playbook(
3540+ playbook='ansible/playbook.yaml',
3541+ tags=['install'],
3542+ extra_vars={'var_a': 'val_a', 'var_b': 'val_b'}
3543+ )
3544+
3545+ # Run ansible/playbook.yaml with tag config and extra variable nested,
3546+ # which is passed as json and can be used as dictionary in playbook
3547+ apply_playbook(
3548+ playbook='ansible/playbook.yaml',
3549+ tags=['config'],
3550+ extra_vars={'nested': {'a': 'value1', 'b': 'value2'}}
3551+ )
3552+
3553+ # Custom config file can be passed within extra_vars
3554+ apply_playbook(
3555+ playbook='ansible/playbook.yaml',
3556+ extra_vars="@some_file.json"
3557+ )
3558+
3559+ """
3560+ tags = tags or []
3561+ tags = ",".join(tags)
3562+ charmhelpers.contrib.templating.contexts.juju_state_to_yaml(
3563+ ansible_vars_path, namespace_separator='__',
3564+ allow_hyphens_in_keys=False, mode=(stat.S_IRUSR | stat.S_IWUSR))
3565+
3566+ # we want ansible's log output to be unbuffered
3567+ env = os.environ.copy()
3568+ proxy_settings = charmhelpers.core.hookenv.env_proxy_settings()
3569+ if proxy_settings:
3570+ env.update(proxy_settings)
3571+ env['PYTHONUNBUFFERED'] = "1"
3572+ call = [
3573+ 'ansible-playbook',
3574+ '-c',
3575+ 'local',
3576+ playbook,
3577+ ]
3578+ if tags:
3579+ call.extend(['--tags', '{}'.format(tags)])
3580+ if extra_vars:
3581+ call.extend(['--extra-vars', json.dumps(extra_vars)])
3582+ subprocess.check_call(call, env=env)
3583+
3584+
3585+class AnsibleHooks(charmhelpers.core.hookenv.Hooks):
3586+ """Run a playbook with the hook-name as the tag.
3587+
3588+ This helper builds on the standard hookenv.Hooks helper,
3589+ but additionally runs the playbook with the hook-name specified
3590+ using --tags (ie. running all the tasks tagged with the hook-name).
3591+
3592+ Example::
3593+
3594+ hooks = AnsibleHooks(playbook_path='ansible/my_machine_state.yaml')
3595+
3596+ # All the tasks within my_machine_state.yaml tagged with 'install'
3597+ # will be run automatically after do_custom_work()
3598+ @hooks.hook()
3599+ def install():
3600+ do_custom_work()
3601+
3602+ # For most of your hooks, you won't need to do anything other
3603+ # than run the tagged tasks for the hook:
3604+ @hooks.hook('config-changed', 'start', 'stop')
3605+ def just_use_playbook():
3606+ pass
3607+
3608+ # As a convenience, you can avoid the above noop function by specifying
3609+ # the hooks which are handled by ansible-only and they'll be registered
3610+ # for you:
3611+ # hooks = AnsibleHooks(
3612+ # 'ansible/my_machine_state.yaml',
3613+ # default_hooks=['config-changed', 'start', 'stop'])
3614+
3615+ if __name__ == "__main__":
3616+ # execute a hook based on the name the program is called by
3617+ hooks.execute(sys.argv)
3618+ """
3619+
3620+ def __init__(self, playbook_path, default_hooks=None):
3621+ """Register any hooks handled by ansible."""
3622+ super(AnsibleHooks, self).__init__()
3623+
3624+ self._actions = {}
3625+ self.playbook_path = playbook_path
3626+
3627+ default_hooks = default_hooks or []
3628+
3629+ def noop(*args, **kwargs):
3630+ pass
3631+
3632+ for hook in default_hooks:
3633+ self.register(hook, noop)
3634+
3635+ def register_action(self, name, function):
3636+ """Register a hook"""
3637+ self._actions[name] = function
3638+
3639+ def execute(self, args):
3640+ """Execute the hook followed by the playbook using the hook as tag."""
3641+ hook_name = os.path.basename(args[0])
3642+ extra_vars = None
3643+ if hook_name in self._actions:
3644+ extra_vars = self._actions[hook_name](args[1:])
3645+ else:
3646+ super(AnsibleHooks, self).execute(args)
3647+
3648+ charmhelpers.contrib.ansible.apply_playbook(
3649+ self.playbook_path, tags=[hook_name], extra_vars=extra_vars)
3650+
3651+ def action(self, *action_names):
3652+ """Decorator, registering them as actions"""
3653+ def action_wrapper(decorated):
3654+
3655+ @functools.wraps(decorated)
3656+ def wrapper(argv):
3657+ kwargs = dict(arg.split('=') for arg in argv)
3658+ try:
3659+ return decorated(**kwargs)
3660+ except TypeError as e:
3661+ if decorated.__doc__:
3662+ e.args += (decorated.__doc__,)
3663+ raise
3664+
3665+ self.register_action(decorated.__name__, wrapper)
3666+ if '_' in decorated.__name__:
3667+ self.register_action(
3668+ decorated.__name__.replace('_', '-'), wrapper)
3669+
3670+ return wrapper
3671+
3672+ return action_wrapper
3673diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/ansible/__pycache__/__init__.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/ansible/__pycache__/__init__.cpython-38.pyc
3674new file mode 100644
3675index 0000000..c21f6a9
3676Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/ansible/__pycache__/__init__.cpython-38.pyc differ
3677diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/benchmark/__init__.py b/charms/appstream-generator/venv/charmhelpers/contrib/benchmark/__init__.py
3678new file mode 100644
3679index 0000000..c35f7fe
3680--- /dev/null
3681+++ b/charms/appstream-generator/venv/charmhelpers/contrib/benchmark/__init__.py
3682@@ -0,0 +1,124 @@
3683+# Copyright 2014-2015 Canonical Limited.
3684+#
3685+# Licensed under the Apache License, Version 2.0 (the "License");
3686+# you may not use this file except in compliance with the License.
3687+# You may obtain a copy of the License at
3688+#
3689+# http://www.apache.org/licenses/LICENSE-2.0
3690+#
3691+# Unless required by applicable law or agreed to in writing, software
3692+# distributed under the License is distributed on an "AS IS" BASIS,
3693+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3694+# See the License for the specific language governing permissions and
3695+# limitations under the License.
3696+
3697+import subprocess
3698+import time
3699+import os
3700+from distutils.spawn import find_executable
3701+
3702+from charmhelpers.core.hookenv import (
3703+ in_relation_hook,
3704+ relation_ids,
3705+ relation_set,
3706+ relation_get,
3707+)
3708+
3709+
3710+def action_set(key, val):
3711+ if find_executable('action-set'):
3712+ action_cmd = ['action-set']
3713+
3714+ if isinstance(val, dict):
3715+ for k, v in iter(val.items()):
3716+ action_set('%s.%s' % (key, k), v)
3717+ return True
3718+
3719+ action_cmd.append('%s=%s' % (key, val))
3720+ subprocess.check_call(action_cmd)
3721+ return True
3722+ return False
3723+
3724+
3725+class Benchmark():
3726+ """
3727+ Helper class for the `benchmark` interface.
3728+
3729+ :param list actions: Define the actions that are also benchmarks
3730+
3731+ From inside the benchmark-relation-changed hook, you would
3732+ Benchmark(['memory', 'cpu', 'disk', 'smoke', 'custom'])
3733+
3734+ Examples:
3735+
3736+ siege = Benchmark(['siege'])
3737+ siege.start()
3738+ [... run siege ...]
3739+ # The higher the score, the better the benchmark
3740+ siege.set_composite_score(16.70, 'trans/sec', 'desc')
3741+ siege.finish()
3742+
3743+
3744+ """
3745+
3746+ BENCHMARK_CONF = '/etc/benchmark.conf' # Replaced in testing
3747+
3748+ required_keys = [
3749+ 'hostname',
3750+ 'port',
3751+ 'graphite_port',
3752+ 'graphite_endpoint',
3753+ 'api_port'
3754+ ]
3755+
3756+ def __init__(self, benchmarks=None):
3757+ if in_relation_hook():
3758+ if benchmarks is not None:
3759+ for rid in sorted(relation_ids('benchmark')):
3760+ relation_set(relation_id=rid, relation_settings={
3761+ 'benchmarks': ",".join(benchmarks)
3762+ })
3763+
3764+ # Check the relation data
3765+ config = {}
3766+ for key in self.required_keys:
3767+ val = relation_get(key)
3768+ if val is not None:
3769+ config[key] = val
3770+ else:
3771+ # We don't have all of the required keys
3772+ config = {}
3773+ break
3774+
3775+ if len(config):
3776+ with open(self.BENCHMARK_CONF, 'w') as f:
3777+ for key, val in iter(config.items()):
3778+ f.write("%s=%s\n" % (key, val))
3779+
3780+ @staticmethod
3781+ def start():
3782+ action_set('meta.start', time.strftime('%Y-%m-%dT%H:%M:%SZ'))
3783+
3784+ """
3785+ If the collectd charm is also installed, tell it to send a snapshot
3786+ of the current profile data.
3787+ """
3788+ COLLECT_PROFILE_DATA = '/usr/local/bin/collect-profile-data'
3789+ if os.path.exists(COLLECT_PROFILE_DATA):
3790+ subprocess.check_output([COLLECT_PROFILE_DATA])
3791+
3792+ @staticmethod
3793+ def finish():
3794+ action_set('meta.stop', time.strftime('%Y-%m-%dT%H:%M:%SZ'))
3795+
3796+ @staticmethod
3797+ def set_composite_score(value, units, direction='asc'):
3798+ """
3799+ Set the composite score for a benchmark run. This is a single number
3800+ representative of the benchmark results. This could be the most
3801+ important metric, or an amalgamation of metric scores.
3802+ """
3803+ return action_set(
3804+ "meta.composite",
3805+ {'value': value, 'units': units, 'direction': direction}
3806+ )
3807diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/benchmark/__pycache__/__init__.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/benchmark/__pycache__/__init__.cpython-38.pyc
3808new file mode 100644
3809index 0000000..b5d3978
3810Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/benchmark/__pycache__/__init__.cpython-38.pyc differ
3811diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/IMPORT b/charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/IMPORT
3812new file mode 100644
3813index 0000000..d41cb04
3814--- /dev/null
3815+++ b/charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/IMPORT
3816@@ -0,0 +1,4 @@
3817+Source lp:charm-tools/trunk
3818+
3819+charm-tools/helpers/python/charmhelpers/__init__.py -> charmhelpers/charmhelpers/contrib/charmhelpers/__init__.py
3820+charm-tools/helpers/python/charmhelpers/tests/test_charmhelpers.py -> charmhelpers/tests/contrib/charmhelpers/test_charmhelpers.py
3821diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/__init__.py b/charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/__init__.py
3822new file mode 100644
3823index 0000000..ed63e81
3824--- /dev/null
3825+++ b/charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/__init__.py
3826@@ -0,0 +1,203 @@
3827+# Copyright 2014-2015 Canonical Limited.
3828+#
3829+# Licensed under the Apache License, Version 2.0 (the "License");
3830+# you may not use this file except in compliance with the License.
3831+# You may obtain a copy of the License at
3832+#
3833+# http://www.apache.org/licenses/LICENSE-2.0
3834+#
3835+# Unless required by applicable law or agreed to in writing, software
3836+# distributed under the License is distributed on an "AS IS" BASIS,
3837+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
3838+# See the License for the specific language governing permissions and
3839+# limitations under the License.
3840+
3841+import warnings
3842+warnings.warn("contrib.charmhelpers is deprecated", DeprecationWarning) # noqa
3843+
3844+import operator
3845+import tempfile
3846+import time
3847+import yaml
3848+import subprocess
3849+
3850+import six
3851+if six.PY3:
3852+ from urllib.request import urlopen
3853+ from urllib.error import (HTTPError, URLError)
3854+else:
3855+ from urllib2 import (urlopen, HTTPError, URLError)
3856+
3857+"""Helper functions for writing Juju charms in Python."""
3858+
3859+__metaclass__ = type
3860+__all__ = [
3861+ # 'get_config', # core.hookenv.config()
3862+ # 'log', # core.hookenv.log()
3863+ # 'log_entry', # core.hookenv.log()
3864+ # 'log_exit', # core.hookenv.log()
3865+ # 'relation_get', # core.hookenv.relation_get()
3866+ # 'relation_set', # core.hookenv.relation_set()
3867+ # 'relation_ids', # core.hookenv.relation_ids()
3868+ # 'relation_list', # core.hookenv.relation_units()
3869+ # 'config_get', # core.hookenv.config()
3870+ # 'unit_get', # core.hookenv.unit_get()
3871+ # 'open_port', # core.hookenv.open_port()
3872+ # 'close_port', # core.hookenv.close_port()
3873+ # 'service_control', # core.host.service()
3874+ 'unit_info', # client-side, NOT IMPLEMENTED
3875+ 'wait_for_machine', # client-side, NOT IMPLEMENTED
3876+ 'wait_for_page_contents', # client-side, NOT IMPLEMENTED
3877+ 'wait_for_relation', # client-side, NOT IMPLEMENTED
3878+ 'wait_for_unit', # client-side, NOT IMPLEMENTED
3879+]
3880+
3881+
3882+SLEEP_AMOUNT = 0.1
3883+
3884+
3885+# We create a juju_status Command here because it makes testing much,
3886+# much easier.
3887+def juju_status():
3888+ subprocess.check_call(['juju', 'status'])
3889+
3890+# re-implemented as charmhelpers.fetch.configure_sources()
3891+# def configure_source(update=False):
3892+# source = config_get('source')
3893+# if ((source.startswith('ppa:') or
3894+# source.startswith('cloud:') or
3895+# source.startswith('http:'))):
3896+# run('add-apt-repository', source)
3897+# if source.startswith("http:"):
3898+# run('apt-key', 'import', config_get('key'))
3899+# if update:
3900+# run('apt-get', 'update')
3901+
3902+
3903+# DEPRECATED: client-side only
3904+def make_charm_config_file(charm_config):
3905+ charm_config_file = tempfile.NamedTemporaryFile(mode='w+')
3906+ charm_config_file.write(yaml.dump(charm_config))
3907+ charm_config_file.flush()
3908+ # The NamedTemporaryFile instance is returned instead of just the name
3909+ # because we want to take advantage of garbage collection-triggered
3910+ # deletion of the temp file when it goes out of scope in the caller.
3911+ return charm_config_file
3912+
3913+
3914+# DEPRECATED: client-side only
3915+def unit_info(service_name, item_name, data=None, unit=None):
3916+ if data is None:
3917+ data = yaml.safe_load(juju_status())
3918+ service = data['services'].get(service_name)
3919+ if service is None:
3920+ # XXX 2012-02-08 gmb:
3921+ # This allows us to cope with the race condition that we
3922+ # have between deploying a service and having it come up in
3923+ # `juju status`. We could probably do with cleaning it up so
3924+ # that it fails a bit more noisily after a while.
3925+ return ''
3926+ units = service['units']
3927+ if unit is not None:
3928+ item = units[unit][item_name]
3929+ else:
3930+ # It might seem odd to sort the units here, but we do it to
3931+ # ensure that when no unit is specified, the first unit for the
3932+ # service (or at least the one with the lowest number) is the
3933+ # one whose data gets returned.
3934+ sorted_unit_names = sorted(units.keys())
3935+ item = units[sorted_unit_names[0]][item_name]
3936+ return item
3937+
3938+
3939+# DEPRECATED: client-side only
3940+def get_machine_data():
3941+ return yaml.safe_load(juju_status())['machines']
3942+
3943+
3944+# DEPRECATED: client-side only
3945+def wait_for_machine(num_machines=1, timeout=300):
3946+ """Wait `timeout` seconds for `num_machines` machines to come up.
3947+
3948+ This wait_for... function can be called by other wait_for functions
3949+ whose timeouts might be too short in situations where only a bare
3950+ Juju setup has been bootstrapped.
3951+
3952+ :return: A tuple of (num_machines, time_taken). This is used for
3953+ testing.
3954+ """
3955+ # You may think this is a hack, and you'd be right. The easiest way
3956+ # to tell what environment we're working in (LXC vs EC2) is to check
3957+ # the dns-name of the first machine. If it's localhost we're in LXC
3958+ # and we can just return here.
3959+ if get_machine_data()[0]['dns-name'] == 'localhost':
3960+ return 1, 0
3961+ start_time = time.time()
3962+ while True:
3963+ # Drop the first machine, since it's the Zookeeper and that's
3964+ # not a machine that we need to wait for. This will only work
3965+ # for EC2 environments, which is why we return early above if
3966+ # we're in LXC.
3967+ machine_data = get_machine_data()
3968+ non_zookeeper_machines = [
3969+ machine_data[key] for key in list(machine_data.keys())[1:]]
3970+ if len(non_zookeeper_machines) >= num_machines:
3971+ all_machines_running = True
3972+ for machine in non_zookeeper_machines:
3973+ if machine.get('instance-state') != 'running':
3974+ all_machines_running = False
3975+ break
3976+ if all_machines_running:
3977+ break
3978+ if time.time() - start_time >= timeout:
3979+ raise RuntimeError('timeout waiting for service to start')
3980+ time.sleep(SLEEP_AMOUNT)
3981+ return num_machines, time.time() - start_time
3982+
3983+
3984+# DEPRECATED: client-side only
3985+def wait_for_unit(service_name, timeout=480):
3986+ """Wait `timeout` seconds for a given service name to come up."""
3987+ wait_for_machine(num_machines=1)
3988+ start_time = time.time()
3989+ while True:
3990+ state = unit_info(service_name, 'agent-state')
3991+ if 'error' in state or state == 'started':
3992+ break
3993+ if time.time() - start_time >= timeout:
3994+ raise RuntimeError('timeout waiting for service to start')
3995+ time.sleep(SLEEP_AMOUNT)
3996+ if state != 'started':
3997+ raise RuntimeError('unit did not start, agent-state: ' + state)
3998+
3999+
4000+# DEPRECATED: client-side only
4001+def wait_for_relation(service_name, relation_name, timeout=120):
4002+ """Wait `timeout` seconds for a given relation to come up."""
4003+ start_time = time.time()
4004+ while True:
4005+ relation = unit_info(service_name, 'relations').get(relation_name)
4006+ if relation is not None and relation['state'] == 'up':
4007+ break
4008+ if time.time() - start_time >= timeout:
4009+ raise RuntimeError('timeout waiting for relation to be up')
4010+ time.sleep(SLEEP_AMOUNT)
4011+
4012+
4013+# DEPRECATED: client-side only
4014+def wait_for_page_contents(url, contents, timeout=120, validate=None):
4015+ if validate is None:
4016+ validate = operator.contains
4017+ start_time = time.time()
4018+ while True:
4019+ try:
4020+ stream = urlopen(url)
4021+ except (HTTPError, URLError):
4022+ pass
4023+ else:
4024+ page = stream.read()
4025+ if validate(page, contents):
4026+ return page
4027+ if time.time() - start_time >= timeout:
4028+ raise RuntimeError('timeout waiting for contents of ' + url)
4029+ time.sleep(SLEEP_AMOUNT)
4030diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/__pycache__/__init__.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/__pycache__/__init__.cpython-38.pyc
4031new file mode 100644
4032index 0000000..797bc82
4033Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/charmhelpers/__pycache__/__init__.cpython-38.pyc differ
4034diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/IMPORT b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/IMPORT
4035new file mode 100644
4036index 0000000..554fddd
4037--- /dev/null
4038+++ b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/IMPORT
4039@@ -0,0 +1,14 @@
4040+Source: lp:charmsupport/trunk
4041+
4042+charmsupport/charmsupport/execd.py -> charm-helpers/charmhelpers/contrib/charmsupport/execd.py
4043+charmsupport/charmsupport/hookenv.py -> charm-helpers/charmhelpers/contrib/charmsupport/hookenv.py
4044+charmsupport/charmsupport/host.py -> charm-helpers/charmhelpers/contrib/charmsupport/host.py
4045+charmsupport/charmsupport/nrpe.py -> charm-helpers/charmhelpers/contrib/charmsupport/nrpe.py
4046+charmsupport/charmsupport/volumes.py -> charm-helpers/charmhelpers/contrib/charmsupport/volumes.py
4047+
4048+charmsupport/tests/test_execd.py -> charm-helpers/tests/contrib/charmsupport/test_execd.py
4049+charmsupport/tests/test_hookenv.py -> charm-helpers/tests/contrib/charmsupport/test_hookenv.py
4050+charmsupport/tests/test_host.py -> charm-helpers/tests/contrib/charmsupport/test_host.py
4051+charmsupport/tests/test_nrpe.py -> charm-helpers/tests/contrib/charmsupport/test_nrpe.py
4052+
4053+charmsupport/bin/charmsupport -> charm-helpers/bin/contrib/charmsupport/charmsupport
4054diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__init__.py b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__init__.py
4055new file mode 100644
4056index 0000000..d7567b8
4057--- /dev/null
4058+++ b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__init__.py
4059@@ -0,0 +1,13 @@
4060+# Copyright 2014-2015 Canonical Limited.
4061+#
4062+# Licensed under the Apache License, Version 2.0 (the "License");
4063+# you may not use this file except in compliance with the License.
4064+# You may obtain a copy of the License at
4065+#
4066+# http://www.apache.org/licenses/LICENSE-2.0
4067+#
4068+# Unless required by applicable law or agreed to in writing, software
4069+# distributed under the License is distributed on an "AS IS" BASIS,
4070+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4071+# See the License for the specific language governing permissions and
4072+# limitations under the License.
4073diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__pycache__/__init__.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__pycache__/__init__.cpython-38.pyc
4074new file mode 100644
4075index 0000000..e15454c
4076Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__pycache__/__init__.cpython-38.pyc differ
4077diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__pycache__/nrpe.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__pycache__/nrpe.cpython-38.pyc
4078new file mode 100644
4079index 0000000..2a370de
4080Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__pycache__/nrpe.cpython-38.pyc differ
4081diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__pycache__/volumes.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__pycache__/volumes.cpython-38.pyc
4082new file mode 100644
4083index 0000000..e1e32f3
4084Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/__pycache__/volumes.cpython-38.pyc differ
4085diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/nrpe.py b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/nrpe.py
4086new file mode 100644
4087index 0000000..8d1753c
4088--- /dev/null
4089+++ b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/nrpe.py
4090@@ -0,0 +1,522 @@
4091+# Copyright 2012-2021 Canonical Limited.
4092+#
4093+# Licensed under the Apache License, Version 2.0 (the "License");
4094+# you may not use this file except in compliance with the License.
4095+# You may obtain a copy of the License at
4096+#
4097+# http://www.apache.org/licenses/LICENSE-2.0
4098+#
4099+# Unless required by applicable law or agreed to in writing, software
4100+# distributed under the License is distributed on an "AS IS" BASIS,
4101+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4102+# See the License for the specific language governing permissions and
4103+# limitations under the License.
4104+
4105+"""Compatibility with the nrpe-external-master charm"""
4106+#
4107+# Authors:
4108+# Matthew Wedgwood <matthew.wedgwood@canonical.com>
4109+
4110+import glob
4111+import grp
4112+import os
4113+import pwd
4114+import re
4115+import shlex
4116+import shutil
4117+import subprocess
4118+import yaml
4119+
4120+from charmhelpers.core.hookenv import (
4121+ config,
4122+ hook_name,
4123+ local_unit,
4124+ log,
4125+ relation_get,
4126+ relation_ids,
4127+ relation_set,
4128+ relations_of_type,
4129+)
4130+
4131+from charmhelpers.core.host import service
4132+from charmhelpers.core import host
4133+
4134+# This module adds compatibility with the nrpe-external-master and plain nrpe
4135+# subordinate charms. To use it in your charm:
4136+#
4137+# 1. Update metadata.yaml
4138+#
4139+# provides:
4140+# (...)
4141+# nrpe-external-master:
4142+# interface: nrpe-external-master
4143+# scope: container
4144+#
4145+# and/or
4146+#
4147+# provides:
4148+# (...)
4149+# local-monitors:
4150+# interface: local-monitors
4151+# scope: container
4152+
4153+#
4154+# 2. Add the following to config.yaml
4155+#
4156+# nagios_context:
4157+# default: "juju"
4158+# type: string
4159+# description: |
4160+# Used by the nrpe subordinate charms.
4161+# A string that will be prepended to instance name to set the host name
4162+# in nagios. So for instance the hostname would be something like:
4163+# juju-myservice-0
4164+# If you're running multiple environments with the same services in them
4165+# this allows you to differentiate between them.
4166+# nagios_servicegroups:
4167+# default: ""
4168+# type: string
4169+# description: |
4170+# A comma-separated list of nagios servicegroups.
4171+# If left empty, the nagios_context will be used as the servicegroup
4172+#
4173+# 3. Add custom checks (Nagios plugins) to files/nrpe-external-master
4174+#
4175+# 4. Update your hooks.py with something like this:
4176+#
4177+# from charmsupport.nrpe import NRPE
4178+# (...)
4179+# def update_nrpe_config():
4180+# nrpe_compat = NRPE()
4181+# nrpe_compat.add_check(
4182+# shortname = "myservice",
4183+# description = "Check MyService",
4184+# check_cmd = "check_http -w 2 -c 10 http://localhost"
4185+# )
4186+# nrpe_compat.add_check(
4187+# "myservice_other",
4188+# "Check for widget failures",
4189+# check_cmd = "/srv/myapp/scripts/widget_check"
4190+# )
4191+# nrpe_compat.write()
4192+#
4193+# def config_changed():
4194+# (...)
4195+# update_nrpe_config()
4196+#
4197+# def nrpe_external_master_relation_changed():
4198+# update_nrpe_config()
4199+#
4200+# def local_monitors_relation_changed():
4201+# update_nrpe_config()
4202+#
4203+# 4.a If your charm is a subordinate charm set primary=False
4204+#
4205+# from charmsupport.nrpe import NRPE
4206+# (...)
4207+# def update_nrpe_config():
4208+# nrpe_compat = NRPE(primary=False)
4209+#
4210+# 5. ln -s hooks.py nrpe-external-master-relation-changed
4211+# ln -s hooks.py local-monitors-relation-changed
4212+
4213+
4214+class CheckException(Exception):
4215+ pass
4216+
4217+
4218+class Check(object):
4219+ shortname_re = '[A-Za-z0-9-_.@]+$'
4220+ service_template = ("""
4221+#---------------------------------------------------
4222+# This file is Juju managed
4223+#---------------------------------------------------
4224+define service {{
4225+ use active-service
4226+ host_name {nagios_hostname}
4227+ service_description {nagios_hostname}[{shortname}] """
4228+ """{description}
4229+ check_command check_nrpe!{command}
4230+ servicegroups {nagios_servicegroup}
4231+{service_config_overrides}
4232+}}
4233+""")
4234+
4235+ def __init__(self, shortname, description, check_cmd, max_check_attempts=None):
4236+ super(Check, self).__init__()
4237+ # XXX: could be better to calculate this from the service name
4238+ if not re.match(self.shortname_re, shortname):
4239+ raise CheckException("shortname must match {}".format(
4240+ Check.shortname_re))
4241+ self.shortname = shortname
4242+ self.command = "check_{}".format(shortname)
4243+ # Note: a set of invalid characters is defined by the
4244+ # Nagios server config
4245+ # The default is: illegal_object_name_chars=`~!$%^&*"|'<>?,()=
4246+ self.description = description
4247+ self.check_cmd = self._locate_cmd(check_cmd)
4248+ self.max_check_attempts = max_check_attempts
4249+
4250+ def _get_check_filename(self):
4251+ return os.path.join(NRPE.nrpe_confdir, '{}.cfg'.format(self.command))
4252+
4253+ def _get_service_filename(self, hostname):
4254+ return os.path.join(NRPE.nagios_exportdir,
4255+ 'service__{}_{}.cfg'.format(hostname, self.command))
4256+
4257+ def _locate_cmd(self, check_cmd):
4258+ search_path = (
4259+ '/usr/lib/nagios/plugins',
4260+ '/usr/local/lib/nagios/plugins',
4261+ )
4262+ parts = shlex.split(check_cmd)
4263+ for path in search_path:
4264+ if os.path.exists(os.path.join(path, parts[0])):
4265+ command = os.path.join(path, parts[0])
4266+ if len(parts) > 1:
4267+ command += " " + " ".join(parts[1:])
4268+ return command
4269+ log('Check command not found: {}'.format(parts[0]))
4270+ return ''
4271+
4272+ def _remove_service_files(self):
4273+ if not os.path.exists(NRPE.nagios_exportdir):
4274+ return
4275+ for f in os.listdir(NRPE.nagios_exportdir):
4276+ if f.endswith('_{}.cfg'.format(self.command)):
4277+ os.remove(os.path.join(NRPE.nagios_exportdir, f))
4278+
4279+ def remove(self, hostname):
4280+ nrpe_check_file = self._get_check_filename()
4281+ if os.path.exists(nrpe_check_file):
4282+ os.remove(nrpe_check_file)
4283+ self._remove_service_files()
4284+
4285+ def write(self, nagios_context, hostname, nagios_servicegroups):
4286+ nrpe_check_file = self._get_check_filename()
4287+ with open(nrpe_check_file, 'w') as nrpe_check_config:
4288+ nrpe_check_config.write("# check {}\n".format(self.shortname))
4289+ if nagios_servicegroups:
4290+ nrpe_check_config.write(
4291+ "# The following header was added automatically by juju\n")
4292+ nrpe_check_config.write(
4293+ "# Modifying it will affect nagios monitoring and alerting\n")
4294+ nrpe_check_config.write(
4295+ "# servicegroups: {}\n".format(nagios_servicegroups))
4296+ nrpe_check_config.write("command[{}]={}\n".format(
4297+ self.command, self.check_cmd))
4298+
4299+ if not os.path.exists(NRPE.nagios_exportdir):
4300+ log('Not writing service config as {} is not accessible'.format(
4301+ NRPE.nagios_exportdir))
4302+ else:
4303+ self.write_service_config(nagios_context, hostname,
4304+ nagios_servicegroups)
4305+
4306+ def write_service_config(self, nagios_context, hostname,
4307+ nagios_servicegroups):
4308+ self._remove_service_files()
4309+
4310+ if self.max_check_attempts:
4311+ service_config_overrides = ' max_check_attempts {}'.format(
4312+ self.max_check_attempts
4313+ ) # Note indentation is here rather than in the template to avoid trailing spaces
4314+ else:
4315+ service_config_overrides = '' # empty string to avoid printing 'None'
4316+ templ_vars = {
4317+ 'nagios_hostname': hostname,
4318+ 'nagios_servicegroup': nagios_servicegroups,
4319+ 'description': self.description,
4320+ 'shortname': self.shortname,
4321+ 'command': self.command,
4322+ 'service_config_overrides': service_config_overrides,
4323+ }
4324+ nrpe_service_text = Check.service_template.format(**templ_vars)
4325+ nrpe_service_file = self._get_service_filename(hostname)
4326+ with open(nrpe_service_file, 'w') as nrpe_service_config:
4327+ nrpe_service_config.write(str(nrpe_service_text))
4328+
4329+ def run(self):
4330+ subprocess.call(self.check_cmd)
4331+
4332+
4333+class NRPE(object):
4334+ nagios_logdir = '/var/log/nagios'
4335+ nagios_exportdir = '/var/lib/nagios/export'
4336+ nrpe_confdir = '/etc/nagios/nrpe.d'
4337+ homedir = '/var/lib/nagios' # home dir provided by nagios-nrpe-server
4338+
4339+ def __init__(self, hostname=None, primary=True):
4340+ super(NRPE, self).__init__()
4341+ self.config = config()
4342+ self.primary = primary
4343+ self.nagios_context = self.config['nagios_context']
4344+ if 'nagios_servicegroups' in self.config and self.config['nagios_servicegroups']:
4345+ self.nagios_servicegroups = self.config['nagios_servicegroups']
4346+ else:
4347+ self.nagios_servicegroups = self.nagios_context
4348+ self.unit_name = local_unit().replace('/', '-')
4349+ if hostname:
4350+ self.hostname = hostname
4351+ else:
4352+ nagios_hostname = get_nagios_hostname()
4353+ if nagios_hostname:
4354+ self.hostname = nagios_hostname
4355+ else:
4356+ self.hostname = "{}-{}".format(self.nagios_context, self.unit_name)
4357+ self.checks = []
4358+ # Iff in an nrpe-external-master relation hook, set primary status
4359+ relation = relation_ids('nrpe-external-master')
4360+ if relation:
4361+ log("Setting charm primary status {}".format(primary))
4362+ for rid in relation:
4363+ relation_set(relation_id=rid, relation_settings={'primary': self.primary})
4364+ self.remove_check_queue = set()
4365+
4366+ @classmethod
4367+ def does_nrpe_conf_dir_exist(cls):
4368+ """Return True if th nrpe_confdif directory exists."""
4369+ return os.path.isdir(cls.nrpe_confdir)
4370+
4371+ def add_check(self, *args, **kwargs):
4372+ shortname = None
4373+ if kwargs.get('shortname') is None:
4374+ if len(args) > 0:
4375+ shortname = args[0]
4376+ else:
4377+ shortname = kwargs['shortname']
4378+
4379+ self.checks.append(Check(*args, **kwargs))
4380+ try:
4381+ self.remove_check_queue.remove(shortname)
4382+ except KeyError:
4383+ pass
4384+
4385+ def remove_check(self, *args, **kwargs):
4386+ if kwargs.get('shortname') is None:
4387+ raise ValueError('shortname of check must be specified')
4388+
4389+ # Use sensible defaults if they're not specified - these are not
4390+ # actually used during removal, but they're required for constructing
4391+ # the Check object; check_disk is chosen because it's part of the
4392+ # nagios-plugins-basic package.
4393+ if kwargs.get('check_cmd') is None:
4394+ kwargs['check_cmd'] = 'check_disk'
4395+ if kwargs.get('description') is None:
4396+ kwargs['description'] = ''
4397+
4398+ check = Check(*args, **kwargs)
4399+ check.remove(self.hostname)
4400+ self.remove_check_queue.add(kwargs['shortname'])
4401+
4402+ def write(self):
4403+ try:
4404+ nagios_uid = pwd.getpwnam('nagios').pw_uid
4405+ nagios_gid = grp.getgrnam('nagios').gr_gid
4406+ except Exception:
4407+ log("Nagios user not set up, nrpe checks not updated")
4408+ return
4409+
4410+ if not os.path.exists(NRPE.nagios_logdir):
4411+ os.mkdir(NRPE.nagios_logdir)
4412+ os.chown(NRPE.nagios_logdir, nagios_uid, nagios_gid)
4413+
4414+ nrpe_monitors = {}
4415+ monitors = {"monitors": {"remote": {"nrpe": nrpe_monitors}}}
4416+
4417+ # check that the charm can write to the conf dir. If not, then nagios
4418+ # probably isn't installed, and we can defer.
4419+ if not self.does_nrpe_conf_dir_exist():
4420+ return
4421+
4422+ for nrpecheck in self.checks:
4423+ nrpecheck.write(self.nagios_context, self.hostname,
4424+ self.nagios_servicegroups)
4425+ nrpe_monitors[nrpecheck.shortname] = {
4426+ "command": nrpecheck.command,
4427+ }
4428+ # If we were passed max_check_attempts, add that to the relation data
4429+ if nrpecheck.max_check_attempts is not None:
4430+ nrpe_monitors[nrpecheck.shortname]['max_check_attempts'] = nrpecheck.max_check_attempts
4431+
4432+ # update-status hooks are configured to firing every 5 minutes by
4433+ # default. When nagios-nrpe-server is restarted, the nagios server
4434+ # reports checks failing causing unnecessary alerts. Let's not restart
4435+ # on update-status hooks.
4436+ if not hook_name() == 'update-status':
4437+ service('restart', 'nagios-nrpe-server')
4438+
4439+ monitor_ids = relation_ids("local-monitors") + \
4440+ relation_ids("nrpe-external-master")
4441+ for rid in monitor_ids:
4442+ reldata = relation_get(unit=local_unit(), rid=rid)
4443+ if 'monitors' in reldata:
4444+ # update the existing set of monitors with the new data
4445+ old_monitors = yaml.safe_load(reldata['monitors'])
4446+ old_nrpe_monitors = old_monitors['monitors']['remote']['nrpe']
4447+ # remove keys that are in the remove_check_queue
4448+ old_nrpe_monitors = {k: v for k, v in old_nrpe_monitors.items()
4449+ if k not in self.remove_check_queue}
4450+ # update/add nrpe_monitors
4451+ old_nrpe_monitors.update(nrpe_monitors)
4452+ old_monitors['monitors']['remote']['nrpe'] = old_nrpe_monitors
4453+ # write back to the relation
4454+ relation_set(relation_id=rid, monitors=yaml.dump(old_monitors))
4455+ else:
4456+ # write a brand new set of monitors, as no existing ones.
4457+ relation_set(relation_id=rid, monitors=yaml.dump(monitors))
4458+
4459+ self.remove_check_queue.clear()
4460+
4461+
4462+def get_nagios_hostcontext(relation_name='nrpe-external-master'):
4463+ """
4464+ Query relation with nrpe subordinate, return the nagios_host_context
4465+
4466+ :param str relation_name: Name of relation nrpe sub joined to
4467+ """
4468+ for rel in relations_of_type(relation_name):
4469+ if 'nagios_host_context' in rel:
4470+ return rel['nagios_host_context']
4471+
4472+
4473+def get_nagios_hostname(relation_name='nrpe-external-master'):
4474+ """
4475+ Query relation with nrpe subordinate, return the nagios_hostname
4476+
4477+ :param str relation_name: Name of relation nrpe sub joined to
4478+ """
4479+ for rel in relations_of_type(relation_name):
4480+ if 'nagios_hostname' in rel:
4481+ return rel['nagios_hostname']
4482+
4483+
4484+def get_nagios_unit_name(relation_name='nrpe-external-master'):
4485+ """
4486+ Return the nagios unit name prepended with host_context if needed
4487+
4488+ :param str relation_name: Name of relation nrpe sub joined to
4489+ """
4490+ host_context = get_nagios_hostcontext(relation_name)
4491+ if host_context:
4492+ unit = "%s:%s" % (host_context, local_unit())
4493+ else:
4494+ unit = local_unit()
4495+ return unit
4496+
4497+
4498+def add_init_service_checks(nrpe, services, unit_name, immediate_check=True):
4499+ """
4500+ Add checks for each service in list
4501+
4502+ :param NRPE nrpe: NRPE object to add check to
4503+ :param list services: List of services to check
4504+ :param str unit_name: Unit name to use in check description
4505+ :param bool immediate_check: For sysv init, run the service check immediately
4506+ """
4507+ for svc in services:
4508+ # Don't add a check for these services from neutron-gateway
4509+ if svc in ['ext-port', 'os-charm-phy-nic-mtu']:
4510+ next
4511+
4512+ upstart_init = '/etc/init/%s.conf' % svc
4513+ sysv_init = '/etc/init.d/%s' % svc
4514+
4515+ if host.init_is_systemd(service_name=svc):
4516+ nrpe.add_check(
4517+ shortname=svc,
4518+ description='process check {%s}' % unit_name,
4519+ check_cmd='check_systemd.py %s' % svc
4520+ )
4521+ elif os.path.exists(upstart_init):
4522+ nrpe.add_check(
4523+ shortname=svc,
4524+ description='process check {%s}' % unit_name,
4525+ check_cmd='check_upstart_job %s' % svc
4526+ )
4527+ elif os.path.exists(sysv_init):
4528+ cronpath = '/etc/cron.d/nagios-service-check-%s' % svc
4529+ checkpath = '%s/service-check-%s.txt' % (nrpe.homedir, svc)
4530+ croncmd = (
4531+ '/usr/local/lib/nagios/plugins/check_exit_status.pl '
4532+ '-e -s /etc/init.d/%s status' % svc
4533+ )
4534+ cron_file = '*/5 * * * * root %s > %s\n' % (croncmd, checkpath)
4535+ f = open(cronpath, 'w')
4536+ f.write(cron_file)
4537+ f.close()
4538+ nrpe.add_check(
4539+ shortname=svc,
4540+ description='service check {%s}' % unit_name,
4541+ check_cmd='check_status_file.py -f %s' % checkpath,
4542+ )
4543+ # if /var/lib/nagios doesn't exist open(checkpath, 'w') will fail
4544+ # (LP: #1670223).
4545+ if immediate_check and os.path.isdir(nrpe.homedir):
4546+ f = open(checkpath, 'w')
4547+ subprocess.call(
4548+ croncmd.split(),
4549+ stdout=f,
4550+ stderr=subprocess.STDOUT
4551+ )
4552+ f.close()
4553+ os.chmod(checkpath, 0o644)
4554+
4555+
4556+def copy_nrpe_checks(nrpe_files_dir=None):
4557+ """
4558+ Copy the nrpe checks into place
4559+
4560+ """
4561+ NAGIOS_PLUGINS = '/usr/local/lib/nagios/plugins'
4562+ if nrpe_files_dir is None:
4563+ # determine if "charmhelpers" is in CHARMDIR or CHARMDIR/hooks
4564+ for segment in ['.', 'hooks']:
4565+ nrpe_files_dir = os.path.abspath(os.path.join(
4566+ os.getenv('CHARM_DIR'),
4567+ segment,
4568+ 'charmhelpers',
4569+ 'contrib',
4570+ 'openstack',
4571+ 'files'))
4572+ if os.path.isdir(nrpe_files_dir):
4573+ break
4574+ else:
4575+ raise RuntimeError("Couldn't find charmhelpers directory")
4576+ if not os.path.exists(NAGIOS_PLUGINS):
4577+ os.makedirs(NAGIOS_PLUGINS)
4578+ for fname in glob.glob(os.path.join(nrpe_files_dir, "check_*")):
4579+ if os.path.isfile(fname):
4580+ shutil.copy2(fname,
4581+ os.path.join(NAGIOS_PLUGINS, os.path.basename(fname)))
4582+
4583+
4584+def add_haproxy_checks(nrpe, unit_name):
4585+ """
4586+ Add checks for each service in list
4587+
4588+ :param NRPE nrpe: NRPE object to add check to
4589+ :param str unit_name: Unit name to use in check description
4590+ """
4591+ nrpe.add_check(
4592+ shortname='haproxy_servers',
4593+ description='Check HAProxy {%s}' % unit_name,
4594+ check_cmd='check_haproxy.sh')
4595+ nrpe.add_check(
4596+ shortname='haproxy_queue',
4597+ description='Check HAProxy queue depth {%s}' % unit_name,
4598+ check_cmd='check_haproxy_queue_depth.sh')
4599+
4600+
4601+def remove_deprecated_check(nrpe, deprecated_services):
4602+ """
4603+ Remove checks for deprecated services in list
4604+
4605+ :param nrpe: NRPE object to remove check from
4606+ :type nrpe: NRPE
4607+ :param deprecated_services: List of deprecated services that are removed
4608+ :type deprecated_services: list
4609+ """
4610+ for dep_svc in deprecated_services:
4611+ log('Deprecated service: {}'.format(dep_svc))
4612+ nrpe.remove_check(shortname=dep_svc)
4613diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/volumes.py b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/volumes.py
4614new file mode 100644
4615index 0000000..f7c6fbd
4616--- /dev/null
4617+++ b/charms/appstream-generator/venv/charmhelpers/contrib/charmsupport/volumes.py
4618@@ -0,0 +1,173 @@
4619+# Copyright 2014-2021 Canonical Limited.
4620+#
4621+# Licensed under the Apache License, Version 2.0 (the "License");
4622+# you may not use this file except in compliance with the License.
4623+# You may obtain a copy of the License at
4624+#
4625+# http://www.apache.org/licenses/LICENSE-2.0
4626+#
4627+# Unless required by applicable law or agreed to in writing, software
4628+# distributed under the License is distributed on an "AS IS" BASIS,
4629+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4630+# See the License for the specific language governing permissions and
4631+# limitations under the License.
4632+
4633+'''
4634+Functions for managing volumes in juju units. One volume is supported per unit.
4635+Subordinates may have their own storage, provided it is on its own partition.
4636+
4637+Configuration stanzas::
4638+
4639+ volume-ephemeral:
4640+ type: boolean
4641+ default: true
4642+ description: >
4643+ If false, a volume is mounted as specified in "volume-map"
4644+ If true, ephemeral storage will be used, meaning that log data
4645+ will only exist as long as the machine. YOU HAVE BEEN WARNED.
4646+ volume-map:
4647+ type: string
4648+ default: {}
4649+ description: >
4650+ YAML map of units to device names, e.g:
4651+ "{ rsyslog/0: /dev/vdb, rsyslog/1: /dev/vdb }"
4652+ Service units will raise a configure-error if volume-ephemeral
4653+ is 'true' and no volume-map value is set. Use 'juju set' to set a
4654+ value and 'juju resolved' to complete configuration.
4655+
4656+Usage::
4657+
4658+ from charmsupport.volumes import configure_volume, VolumeConfigurationError
4659+ from charmsupport.hookenv import log, ERROR
4660+ def post_mount_hook():
4661+ stop_service('myservice')
4662+ def post_mount_hook():
4663+ start_service('myservice')
4664+
4665+ if __name__ == '__main__':
4666+ try:
4667+ configure_volume(before_change=pre_mount_hook,
4668+ after_change=post_mount_hook)
4669+ except VolumeConfigurationError:
4670+ log('Storage could not be configured', ERROR)
4671+
4672+'''
4673+
4674+# XXX: Known limitations
4675+# - fstab is neither consulted nor updated
4676+
4677+import os
4678+from charmhelpers.core import hookenv
4679+from charmhelpers.core import host
4680+import yaml
4681+
4682+
4683+MOUNT_BASE = '/srv/juju/volumes'
4684+
4685+
4686+class VolumeConfigurationError(Exception):
4687+ '''Volume configuration data is missing or invalid'''
4688+ pass
4689+
4690+
4691+def get_config():
4692+ '''Gather and sanity-check volume configuration data'''
4693+ volume_config = {}
4694+ config = hookenv.config()
4695+
4696+ errors = False
4697+
4698+ if config.get('volume-ephemeral') in (True, 'True', 'true', 'Yes', 'yes'):
4699+ volume_config['ephemeral'] = True
4700+ else:
4701+ volume_config['ephemeral'] = False
4702+
4703+ try:
4704+ volume_map = yaml.safe_load(config.get('volume-map', '{}'))
4705+ except yaml.YAMLError as e:
4706+ hookenv.log("Error parsing YAML volume-map: {}".format(e),
4707+ hookenv.ERROR)
4708+ errors = True
4709+ if volume_map is None:
4710+ # probably an empty string
4711+ volume_map = {}
4712+ elif not isinstance(volume_map, dict):
4713+ hookenv.log("Volume-map should be a dictionary, not {}".format(
4714+ type(volume_map)))
4715+ errors = True
4716+
4717+ volume_config['device'] = volume_map.get(os.environ['JUJU_UNIT_NAME'])
4718+ if volume_config['device'] and volume_config['ephemeral']:
4719+ # asked for ephemeral storage but also defined a volume ID
4720+ hookenv.log('A volume is defined for this unit, but ephemeral '
4721+ 'storage was requested', hookenv.ERROR)
4722+ errors = True
4723+ elif not volume_config['device'] and not volume_config['ephemeral']:
4724+ # asked for permanent storage but did not define volume ID
4725+ hookenv.log('Ephemeral storage was requested, but there is no volume '
4726+ 'defined for this unit.', hookenv.ERROR)
4727+ errors = True
4728+
4729+ unit_mount_name = hookenv.local_unit().replace('/', '-')
4730+ volume_config['mountpoint'] = os.path.join(MOUNT_BASE, unit_mount_name)
4731+
4732+ if errors:
4733+ return None
4734+ return volume_config
4735+
4736+
4737+def mount_volume(config):
4738+ if os.path.exists(config['mountpoint']):
4739+ if not os.path.isdir(config['mountpoint']):
4740+ hookenv.log('Not a directory: {}'.format(config['mountpoint']))
4741+ raise VolumeConfigurationError()
4742+ else:
4743+ host.mkdir(config['mountpoint'])
4744+ if os.path.ismount(config['mountpoint']):
4745+ unmount_volume(config)
4746+ if not host.mount(config['device'], config['mountpoint'], persist=True):
4747+ raise VolumeConfigurationError()
4748+
4749+
4750+def unmount_volume(config):
4751+ if os.path.ismount(config['mountpoint']):
4752+ if not host.umount(config['mountpoint'], persist=True):
4753+ raise VolumeConfigurationError()
4754+
4755+
4756+def managed_mounts():
4757+ '''List of all mounted managed volumes'''
4758+ return filter(lambda mount: mount[0].startswith(MOUNT_BASE), host.mounts())
4759+
4760+
4761+def configure_volume(before_change=lambda: None, after_change=lambda: None):
4762+ '''Set up storage (or don't) according to the charm's volume configuration.
4763+ Returns the mount point or "ephemeral". before_change and after_change
4764+ are optional functions to be called if the volume configuration changes.
4765+ '''
4766+
4767+ config = get_config()
4768+ if not config:
4769+ hookenv.log('Failed to read volume configuration', hookenv.CRITICAL)
4770+ raise VolumeConfigurationError()
4771+
4772+ if config['ephemeral']:
4773+ if os.path.ismount(config['mountpoint']):
4774+ before_change()
4775+ unmount_volume(config)
4776+ after_change()
4777+ return 'ephemeral'
4778+ else:
4779+ # persistent storage
4780+ if os.path.ismount(config['mountpoint']):
4781+ mounts = dict(managed_mounts())
4782+ if mounts.get(config['mountpoint']) != config['device']:
4783+ before_change()
4784+ unmount_volume(config)
4785+ mount_volume(config)
4786+ after_change()
4787+ else:
4788+ before_change()
4789+ mount_volume(config)
4790+ after_change()
4791+ return config['mountpoint']
4792diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/database/__init__.py b/charms/appstream-generator/venv/charmhelpers/contrib/database/__init__.py
4793new file mode 100644
4794index 0000000..64fac9d
4795--- /dev/null
4796+++ b/charms/appstream-generator/venv/charmhelpers/contrib/database/__init__.py
4797@@ -0,0 +1,11 @@
4798+# Licensed under the Apache License, Version 2.0 (the "License");
4799+# you may not use this file except in compliance with the License.
4800+# You may obtain a copy of the License at
4801+#
4802+# http://www.apache.org/licenses/LICENSE-2.0
4803+#
4804+# Unless required by applicable law or agreed to in writing, software
4805+# distributed under the License is distributed on an "AS IS" BASIS,
4806+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4807+# See the License for the specific language governing permissions and
4808+# limitations under the License.
4809diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/database/__pycache__/__init__.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/database/__pycache__/__init__.cpython-38.pyc
4810new file mode 100644
4811index 0000000..5611c92
4812Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/database/__pycache__/__init__.cpython-38.pyc differ
4813diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/database/__pycache__/mysql.cpython-38.pyc b/charms/appstream-generator/venv/charmhelpers/contrib/database/__pycache__/mysql.cpython-38.pyc
4814new file mode 100644
4815index 0000000..a59ead9
4816Binary files /dev/null and b/charms/appstream-generator/venv/charmhelpers/contrib/database/__pycache__/mysql.cpython-38.pyc differ
4817diff --git a/charms/appstream-generator/venv/charmhelpers/contrib/database/mysql.py b/charms/appstream-generator/venv/charmhelpers/contrib/database/mysql.py
4818new file mode 100644
4819index 0000000..ca79924
4820--- /dev/null
4821+++ b/charms/appstream-generator/venv/charmhelpers/contrib/database/mysql.py
4822@@ -0,0 +1,840 @@
4823+# Licensed under the Apache License, Version 2.0 (the "License");
4824+# you may not use this file except in compliance with the License.
4825+# You may obtain a copy of the License at
4826+#
4827+# http://www.apache.org/licenses/LICENSE-2.0
4828+#
4829+# Unless required by applicable law or agreed to in writing, software
4830+# distributed under the License is distributed on an "AS IS" BASIS,
4831+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
4832+# See the License for the specific language governing permissions and
4833+# limitations under the License.
4834+
4835+"""Helper for working with a MySQL database"""
4836+import collections
4837+import copy
4838+import json
4839+import re
4840+import sys
4841+import platform
4842+import os
4843+import glob
4844+import six
4845+
4846+# from string import upper
4847+
4848+from charmhelpers.core.host import (
4849+ CompareHostReleases,
4850+ lsb_release,
4851+ mkdir,
4852+ pwgen,
4853+ write_file
4854+)
4855+from charmhelpers.core.hookenv import (
4856+ config as config_get,
4857+ relation_get,
4858+ related_units,
4859+ unit_get,
4860+ log,
4861+ DEBUG,
4862+ ERROR,
4863+ INFO,
4864+ WARNING,
4865+ leader_get,
4866+ leader_set,
4867+ is_leader,
4868+)
4869+from charmhelpers.fetch import (
4870+ apt_install,
4871+ apt_update,
4872+ filter_installed_packages,
4873+)
4874+from charmhelpers.contrib.network.ip import get_host_ip
4875+
4876+try:
4877+ import MySQLdb
4878+except ImportError:
4879+ apt_update(fatal=True)
4880+ if six.PY2:
4881+ apt_install(filter_installed_packages(['python-mysqldb']), fatal=True)
4882+ else:
4883+ apt_install(filter_installed_packages(['python3-mysqldb']), fatal=True)
4884+ import MySQLdb
4885+
4886+
4887+class MySQLSetPasswordError(Exception):
4888+ pass
4889+
4890+
4891+class MySQLHelper(object):
4892+
4893+ def __init__(self, rpasswdf_template, upasswdf_template, host='localhost',
4894+ migrate_passwd_to_leader_storage=True,
4895+ delete_ondisk_passwd_file=True, user="root", password=None,
4896+ port=None, connect_timeout=None):
4897+ self.user = user
4898+ self.host = host
4899+ self.password = password
4900+ self.port = port
4901+ # default timeout of 30 seconds.
4902+ self.connect_timeout = connect_timeout or 30
4903+
4904+ # Password file path templates
4905+ self.root_passwd_file_template = rpasswdf_template
4906+ self.user_passwd_file_template = upasswdf_template
4907+
4908+ self.migrate_passwd_to_leader_storage = migrate_passwd_to_leader_storage
4909+ # If we migrate we have the option to delete local copy of root passwd
4910+ self.delete_ondisk_passwd_file = delete_ondisk_passwd_file
4911+ self.connection = None
4912+
4913+ def connect(self, user='root', password=None, host=None, port=None,
4914+ connect_timeout=None):
4915+ _connection_info = {
4916+ "user": user or self.user,
4917+ "passwd": password or self.password,
4918+ "host": host or self.host
4919+ }
4920+ # set the connection timeout; for mysql8 it can hang forever, so some
4921+ # timeout is required.
4922+ timeout = connect_timeout or self.connect_timeout
4923+ if timeout:
4924+ _connection_info["connect_timeout"] = timeout
4925+ # port cannot be None but we also do not want to specify it unless it
4926+ # has been explicit set.
4927+ port = port or self.port
4928+ if port is not None:
4929+ _connection_info["port"] = port
4930+
4931+ log("Opening db connection for %s@%s" % (user, host), level=DEBUG)
4932+ try:
4933+ self.connection = MySQLdb.connect(**_connection_info)
4934+ except Exception as e:
4935+ log("Failed to connect to database due to '{}'".format(str(e)),
4936+ level=ERROR)
4937+ raise
4938+
4939+ def database_exists(self, db_name):
4940+ cursor = self.connection.cursor()
4941+ try:
4942+ cursor.execute("SHOW DATABASES")
4943+ databases = [i[0] for i in cursor.fetchall()]
4944+ finally:
4945+ cursor.close()
4946+
4947+ return db_name in databases
4948+
4949+ def create_database(self, db_name):
4950+ cursor = self.connection.cursor()
4951+ try:
4952+ cursor.execute("CREATE DATABASE `{}` CHARACTER SET UTF8"
4953+ .format(db_name))
4954+ finally:
4955+ cursor.close()
4956+
4957+ def grant_exists(self, db_name, db_user, remote_ip):
4958+ cursor = self.connection.cursor()
4959+ priv_string = "GRANT ALL PRIVILEGES ON `{}`.* " \
4960+ "TO '{}'@'{}'".format(db_name, db_user, remote_ip)
4961+ try:
4962+ cursor.execute("SHOW GRANTS for '{}'@'{}'".format(db_user,
4963+ remote_ip))
4964+ grants = [i[0] for i in cursor.fetchall()]
4965+ except MySQLdb.OperationalError:
4966+ return False
4967+ finally:
4968+ cursor.close()
4969+
4970+ # TODO: review for different grants
4971+ return priv_string in grants
4972+
4973+ def create_grant(self, db_name, db_user, remote_ip, password):
4974+ cursor = self.connection.cursor()
4975+ try:
4976+ # TODO: review for different grants
4977+ cursor.execute("GRANT ALL PRIVILEGES ON `{}`.* TO '{}'@'{}' "
4978+ "IDENTIFIED BY '{}'".format(db_name,
4979+ db_user,
4980+ remote_ip,
4981+ password))
4982+ finally:
4983+ cursor.close()
4984+
4985+ def create_admin_grant(self, db_user, remote_ip, password):
4986+ cursor = self.connection.cursor()
4987+ try:
4988+ cursor.execute("GRANT ALL PRIVILEGES ON *.* TO '{}'@'{}' "
4989+ "IDENTIFIED BY '{}'".format(db_user,
4990+ remote_ip,
4991+ password))
4992+ finally:
4993+ cursor.close()
4994+
4995+ def cleanup_grant(self, db_user, remote_ip):
4996+ cursor = self.connection.cursor()
4997+ try:
4998+ cursor.execute("DROP FROM mysql.user WHERE user='{}' "
4999+ "AND HOST='{}'".format(db_user,
5000+ remote_ip))
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches