Initial Cookiecutter Commit.

Change-Id: Icc6bc9f3a991f100cc3a3e83ef673b52c16e0504
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..e6425e1
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,9 @@
+build
+layers
+.tox
+interfaces
+.testrepository
+.stestr
+*__pycache__*
+*.pyc
+/*.charm
diff --git a/.stestr.conf b/.stestr.conf
new file mode 100644
index 0000000..5fcccac
--- /dev/null
+++ b/.stestr.conf
@@ -0,0 +1,3 @@
+[DEFAULT]
+test_path=./unit_tests
+top_dir=./
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6ac1507
--- /dev/null
+++ b/README.md
@@ -0,0 +1,19 @@
+StorPool Storage Backend for Cinder
+-------------------------------
+
+Overview
+========
+
+This charm provides a StorPool storage backend for use with the Cinder
+charm.
+
+To use:
+
+    juju deploy cinder
+    juju deploy cinder-storpool
+    juju add-relation cinder-storpool cinder
+
+Configuration
+=============
+
+See config.yaml for details of configuration options.
diff --git a/build-requirements.txt b/build-requirements.txt
new file mode 100644
index 0000000..b6d2452
--- /dev/null
+++ b/build-requirements.txt
@@ -0,0 +1,7 @@
+# NOTES(lourot):
+# * We don't install charmcraft via pip anymore because it anyway spins up a
+#   container and scp the system's charmcraft snap inside it. So the charmcraft
+#   snap is necessary on the system anyway.
+# * `tox -e build` successfully validated with charmcraft 1.2.1
+
+cffi==1.14.6; python_version < '3.6'  # cffi 1.15.0 drops support for py35.
diff --git a/charmcraft.yaml b/charmcraft.yaml
new file mode 100644
index 0000000..ff59b02
--- /dev/null
+++ b/charmcraft.yaml
@@ -0,0 +1,35 @@
+type: charm
+
+parts:
+  charm:
+    after:
+      - update-certificates
+    charm-python-packages:
+      # NOTE(lourot): see
+      # * https://github.com/canonical/charmcraft/issues/551
+      - setuptools
+    build-packages:
+      - git
+
+  update-certificates:
+    plugin: nil
+    # See https://github.com/canonical/charmcraft/issues/658
+    override-build: |
+      apt update
+      apt install -y ca-certificates
+      update-ca-certificates
+
+bases:
+  - build-on:
+      - name: ubuntu
+        channel: "20.04"
+        architectures:
+          - amd64
+          - s390x
+          - ppc64el
+          - arm64
+    run-on:
+      - name: ubuntu
+        channel: "20.04"
+      - name: ubuntu
+        channel: "21.10"
diff --git a/config.yaml b/config.yaml
new file mode 100644
index 0000000..37c3b74
--- /dev/null
+++ b/config.yaml
@@ -0,0 +1,38 @@
+options:
+  driver-source:
+    type: string
+    default:
+    description: |
+      Optional configuration to support use of additional sources such as:
+        - ppa:myteam/ppa
+        - cloud:trusty-proposed/kilo
+        - http://my.archive.com/ubuntu main
+      The last option should be used in conjunction with the key configuration
+      option.
+  driver-key:
+    type: string
+    default:
+    description: |
+      Key ID to import to the apt keyring to support use with arbitary source
+      configuration from outside of Launchpad archives or PPA's.
+  use-multipath:
+    type: boolean
+    default: True
+    description: |
+      Whether to use a multipath connection for iSCSI or FC in Cinder
+      volume service. Enabling multipath for VMs is managed by the
+      "use-multipath" option in the nova-compute charm.
+  protocol:
+    type: string
+    default:
+    description: |
+      SAN protocol to use. Choose between iscsi or fc.
+  volume-backend-name:
+    type: string
+    description: |
+        Volume backend name for the backend. The default value is the
+        application name in the Juju model, e.g. "cinder-mybackend"
+        if it's deployed as `juju deploy cinder-storpool cinder-mybackend`.
+        A common backend name can be set to multiple backends with the
+        same characters so that those can be treated as a single virtual
+        backend associated with a single volume type.
diff --git a/metadata.yaml b/metadata.yaml
new file mode 100644
index 0000000..203a5db
--- /dev/null
+++ b/metadata.yaml
@@ -0,0 +1,23 @@
+name: cinder-storpool
+summary: StorPool integration for OpenStack Block Storage
+maintainer: OpenStack Charmers <openstack-charmers@lists.ubuntu.com>
+description: |
+ Cinder is the block storage service for the Openstack project.
+ .
+ This charm provides a StorPool backend for Cinder
+tags:
+  - openstack
+  - storage
+  - file-servers
+  - misc
+series:
+  - focal
+subordinate: true
+provides:
+  storage-backend:
+    interface: cinder-backend
+    scope: container
+requires:
+  juju-info:
+    interface: juju-info
+    scope: container
diff --git a/osci.yaml b/osci.yaml
new file mode 100644
index 0000000..018c4a6
--- /dev/null
+++ b/osci.yaml
@@ -0,0 +1,8 @@
+- project:
+    templates:
+      - charm-unit-jobs-py38
+      - charm-unit-jobs-py39
+    vars:
+      needs_charm_build: true
+      charm_build_name: storpool
+      build_type: charmcraft
diff --git a/pip.sh b/pip.sh
new file mode 100755
index 0000000..9a7e6b0
--- /dev/null
+++ b/pip.sh
@@ -0,0 +1,18 @@
+#!/usr/bin/env bash
+#
+# This file is managed centrally by release-tools and should not be modified
+# within individual charm repos.  See the 'global' dir contents for available
+# choices of tox.ini for OpenStack Charms:
+#     https://github.com/openstack-charmers/release-tools
+#
+# setuptools 58.0 dropped the support for use_2to3=true which is needed to
+# install blessings (an indirect dependency of charm-tools).
+#
+# More details on the beahvior of tox and virtualenv creation can be found at
+# https://github.com/tox-dev/tox/issues/448
+#
+# This script is wrapper to force the use of the pinned versions early in the
+# process when the virtualenv was created and upgraded before installing the
+# depedencies declared in the target.
+pip install 'pip<20.3' 'setuptools<50.0.0'
+pip "$@"
diff --git a/rename.sh b/rename.sh
new file mode 100755
index 0000000..d0c35c9
--- /dev/null
+++ b/rename.sh
@@ -0,0 +1,13 @@
+#!/bin/bash
+charm=$(grep "charm_build_name" osci.yaml | awk '{print $2}')
+echo "renaming ${charm}_*.charm to ${charm}.charm"
+echo -n "pwd: "
+pwd
+ls -al
+echo "Removing bad downloaded charm maybe?"
+if [[ -e "${charm}.charm" ]];
+then
+    rm "${charm}.charm"
+fi
+echo "Renaming charm here."
+mv ${charm}_*.charm ${charm}.charm
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..86315ca
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,2 @@
+ops
+git+https://opendev.org/openstack/charm-ops-openstack#egg=ops_openstack
diff --git a/src/charm.py b/src/charm.py
new file mode 100755
index 0000000..e7ec370
--- /dev/null
+++ b/src/charm.py
@@ -0,0 +1,59 @@
+#! /usr/bin/env python3
+
+# Copyright 2021 Canonical Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+from ops_openstack.plugins.classes import CinderStoragePluginCharm
+from ops_openstack.core import charm_class, get_charm_class_for_release
+from ops.main import main
+
+
+class CinderCharmBase(CinderStoragePluginCharm):
+
+    PACKAGES = ['cinder-common']
+    MANDATORY_CONFIG = ['protocol']
+    # Overriden from the parent. May be set depending on the charm's properties
+    stateless = True
+    active_active = True
+
+    def __init__(self, *args, **kwargs):
+        super().__init__(*args, **kwargs)
+
+    def cinder_configuration(self, config):
+        # Return the configuration to be set by the principal.
+        backend_name = config.get('volume-backend-name',
+                                  self.framework.model.app.name)
+        volume_driver = ''
+        options = [
+            ('volume_driver', volume_driver),
+            ('volume_backend_name', backend_name),
+        ]
+
+        if config.get('use-multipath'):
+            options.extend([
+                ('use_multipath_for_image_xfer', True),
+                ('enforce_multipath_for_image_xfer', True)
+            ])
+
+        return options
+
+
+@charm_class
+class CinderStorPoolCharm(CinderCharmBase):
+    release = 'yoga'
+
+
+if __name__ == '__main__':
+    main(get_charm_class_for_release())
diff --git a/test-requirements.txt b/test-requirements.txt
new file mode 100644
index 0000000..e6f71d3
--- /dev/null
+++ b/test-requirements.txt
@@ -0,0 +1,17 @@
+# This file is managed centrally.  If you find the need to modify this as a
+# one-off, please don't.  Intead, consult #openstack-charms and ask about
+# requirements management in charms via bot-control.  Thank you.
+charm-tools>=2.4.4
+coverage>=3.6
+mock>=1.2
+flake8>=4.0.1; python_version >= '3.6'
+stestr>=2.2.0
+requests>=2.18.4
+psutil
+# oslo.i18n dropped py35 support
+oslo.i18n<4.0.0
+git+https://github.com/openstack-charmers/zaza.git#egg=zaza
+git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack
+pytz    # workaround for 14.04 pip/tox
+pyudev  # for ceph-* charm unit tests (not mocked?)
+cffi==1.14.6; python_version < '3.6'  # cffi 1.15.0 drops support for py35.
diff --git a/tests/README.md b/tests/README.md
new file mode 100644
index 0000000..d002a1e
--- /dev/null
+++ b/tests/README.md
@@ -0,0 +1,18 @@
+# Overview
+
+This directory provides Zaza test definitions and bundles to verify basic
+deployment functionality from the perspective of this charm, its requirements
+and its features, as exercised in a subset of the full OpenStack deployment
+test bundle topology.
+
+Run the smoke tests with:
+
+```bash
+cd ../
+tox -e build
+tox -e func-smoke
+```
+
+For full details on functional testing of OpenStack charms please refer to
+the [functional testing](https://docs.openstack.org/charm-guide/latest/reference/testing.html#functional-testing)
+section of the OpenStack Charm Guide.
diff --git a/tests/bundles/focal-yoga.yaml b/tests/bundles/focal-yoga.yaml
new file mode 100644
index 0000000..f9a4df4
--- /dev/null
+++ b/tests/bundles/focal-yoga.yaml
@@ -0,0 +1,76 @@
+series: focal
+variables:
+  openstack-origin: &openstack-origin distro
+comment:
+- 'machines section to decide order of deployment. database sooner = faster'
+machines:
+  '0':
+    constraints: mem=3072M
+  '1':
+    constraints: mem=3072M
+  '2':
+    constraints: mem=3072M
+  '3':
+  '4':
+  '5':
+    constraints: mem=4G root-disk=16G
+local_overlay_enabled: false
+relations:
+  - - keystone:shared-db
+    - keystone-mysql-router:shared-db
+  - - keystone-mysql-router:db-router
+    - mysql-innodb-cluster:db-router
+  - - cinder:shared-db
+    - cinder-mysql-router:shared-db
+  - - cinder-mysql-router:db-router
+    - mysql-innodb-cluster:db-router
+  - - cinder:identity-service
+    - keystone:identity-service
+  - - cinder:amqp
+    - rabbitmq-server:amqp
+  - - cinder:storage-backend
+    - cinder-storpool:storage-backend
+applications:
+  mysql-innodb-cluster:
+    charm: cs:~openstack-charmers-next/mysql-innodb-cluster
+    num_units: 3
+    options:
+      source: *openstack-origin
+    to:
+      - '0'
+      - '1'
+      - '2'
+  rabbitmq-server:
+    charm: cs:~openstack-charmers-next/rabbitmq-server
+    num_units: 1
+    options:
+      source: *openstack-origin
+    to:
+      - '3'
+  keystone:
+    charm: cs:~openstack-charmers-next/keystone
+    options:
+      openstack-origin: *openstack-origin
+    num_units: 1
+    to:
+      - '4'
+  keystone-mysql-router:
+    charm: cs:~openstack-charmers-next/mysql-router
+  cinder:
+    charm: cs:~openstack-charmers-next/cinder
+    num_units: 1
+    storage:
+      block-devices: '40G'
+    options:
+      openstack-origin: *openstack-origin
+      block-device: None
+      overwrite: "true"
+      ephemeral-unmount: /mnt
+    to:
+      - '5'
+  cinder-storpool:
+    charm: ../../cinder-storpool.charm
+    options:
+# Add config options here
+  cinder-mysql-router:
+    charm: cs:~openstack-charmers-next/mysql-router
diff --git a/tests/tests.py b/tests/tests.py
new file mode 100644
index 0000000..69507e1
--- /dev/null
+++ b/tests/tests.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+
+# Copyright 2022 Canonical Ltd.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Encapsulate cinder-storpool testing."""
+
+from zaza.openstack.charm_tests.cinder_backend.tests import CinderBackendTest
+
+
+class CinderStorPoolTest(CinderBackendTest):
+    """Encapsulate StorPool tests."""
+
+    backend_name = 'StorPool'
+
+    expected_config_content = {
+        'StorPool': {
+            'volume-backend-name': ['StorPool'],
+        }}
diff --git a/tests/tests.yaml b/tests/tests.yaml
new file mode 100644
index 0000000..3787ac4
--- /dev/null
+++ b/tests/tests.yaml
@@ -0,0 +1,9 @@
+charm_name: cinder-storpool
+tests:
+  - tests.tests.CinderStorPoolTest
+configure:
+  - zaza.openstack.charm_tests.keystone.setup.add_demo_user
+gate_bundles:
+  - focal-yoga
+smoke_bundles:
+  - focal-yoga
diff --git a/tox.ini b/tox.ini
new file mode 100644
index 0000000..6a95b66
--- /dev/null
+++ b/tox.ini
@@ -0,0 +1,150 @@
+# Operator charm (with zaza): tox.ini
+
+[tox]
+envlist = pep8,py3
+skipsdist = True
+# NOTE: Avoid build/test env pollution by not enabling sitepackages.
+sitepackages = False
+# NOTE: Avoid false positives by not skipping missing interpreters.
+skip_missing_interpreters = False
+# NOTES:
+# * We avoid the new dependency resolver by pinning pip < 20.3, see
+#   https://github.com/pypa/pip/issues/9187
+# * Pinning dependencies requires tox >= 3.2.0, see
+#   https://tox.readthedocs.io/en/latest/config.html#conf-requires
+# * It is also necessary to pin virtualenv as a newer virtualenv would still
+#   lead to fetching the latest pip in the func* tox targets, see
+#   https://stackoverflow.com/a/38133283
+# * It is necessary to declare setuptools as a dependency otherwise tox will
+#   fail very early at not being able to load it. The version pinning is in
+#   line with `pip.sh`.
+requires = pip < 20.3
+           virtualenv < 20.0
+           setuptools < 50.0.0
+# NOTE: https://wiki.canonical.com/engineering/OpenStack/InstallLatestToxOnOsci
+minversion = 3.2.0
+
+[testenv]
+setenv = VIRTUAL_ENV={envdir}
+         PYTHONHASHSEED=0
+         CHARM_DIR={envdir}
+install_command =
+  pip install {opts} {packages}
+commands = stestr run --slowest {posargs}
+allowlist_externals =
+    git
+    bash
+    charmcraft
+    rename.sh
+passenv = HOME TERM CS_* OS_* TEST_*
+deps = -r{toxinidir}/test-requirements.txt
+
+[testenv:py35]
+basepython = python3.5
+# python3.5 is irrelevant on a focal+ charm.
+commands = /bin/true
+
+[testenv:py36]
+basepython = python3.6
+deps = -r{toxinidir}/requirements.txt
+       -r{toxinidir}/test-requirements.txt
+
+[testenv:py37]
+basepython = python3.7
+deps = -r{toxinidir}/requirements.txt
+       -r{toxinidir}/test-requirements.txt
+
+[testenv:py38]
+basepython = python3.8
+deps = -r{toxinidir}/requirements.txt
+       -r{toxinidir}/test-requirements.txt
+
+[testenv:py39]
+basepython = python3.9
+deps = -r{toxinidir}/requirements.txt
+       -r{toxinidir}/test-requirements.txt
+
+[testenv:py3]
+basepython = python3
+deps = -r{toxinidir}/requirements.txt
+       -r{toxinidir}/test-requirements.txt
+
+[testenv:pep8]
+basepython = python3
+deps = -r{toxinidir}/requirements.txt
+       -r{toxinidir}/test-requirements.txt
+commands = flake8 {posargs} src unit_tests tests
+
+[testenv:cover]
+# Technique based heavily upon
+# https://github.com/openstack/nova/blob/master/tox.ini
+basepython = python3
+deps = -r{toxinidir}/requirements.txt
+       -r{toxinidir}/test-requirements.txt
+setenv =
+    {[testenv]setenv}
+    PYTHON=coverage run
+commands =
+    coverage erase
+    stestr run --slowest {posargs}
+    coverage combine
+    coverage html -d cover
+    coverage xml -o cover/coverage.xml
+    coverage report
+
+[coverage:run]
+branch = True
+concurrency = multiprocessing
+parallel = True
+source =
+    .
+omit =
+    .tox/*
+    */charmhelpers/*
+    unit_tests/*
+
+[testenv:venv]
+basepython = python3
+commands = {posargs}
+
+[testenv:build]
+basepython = python3
+deps = -r{toxinidir}/build-requirements.txt
+# NOTE(lourot): charmcraft 1.0.0 used to generate
+# cinder-StorPool.charm, which is the behaviour expected
+# by OSCI. However charmcraft 1.2.1 now generates
+# cinder-StorPool_ubuntu-20.04-amd64.charm instead.
+# In order to keep the old behaviour we rename the file at the end.
+commands =
+    charmcraft clean
+    charmcraft -v build
+    {toxinidir}/rename.sh
+
+[testenv:func-noop]
+basepython = python3
+commands =
+    functest-run-suite --help
+
+[testenv:func]
+basepython = python3
+commands =
+    functest-run-suite --keep-model
+
+[testenv:func-smoke]
+basepython = python3
+commands =
+    functest-run-suite --keep-model --smoke
+
+[testenv:func-dev]
+basepython = python3
+commands =
+    functest-run-suite --keep-model --dev
+
+[testenv:func-target]
+basepython = python3
+commands =
+    functest-run-suite --keep-model --bundle {posargs}
+
+[flake8]
+# Ignore E902 because the unit_tests directory is missing in the built charm.
+ignore = E402,E226,W503,W504,E902
diff --git a/unit_tests/__init__.py b/unit_tests/__init__.py
new file mode 100644
index 0000000..8381d13
--- /dev/null
+++ b/unit_tests/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2021 Canonical Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/unit_tests/test_cinder_storpool_charm.py b/unit_tests/test_cinder_storpool_charm.py
new file mode 100644
index 0000000..44b44cd
--- /dev/null
+++ b/unit_tests/test_cinder_storpool_charm.py
@@ -0,0 +1,51 @@
+# Copyright 2016 Canonical Ltd
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#  http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+from src.charm import CinderCharmBase
+from ops.model import ActiveStatus
+from ops.testing import Harness
+
+
+class TestCinderStorPoolCharm(unittest.TestCase):
+
+    def setUp(self):
+        self.harness = Harness(CinderCharmBase)
+        self.addCleanup(self.harness.cleanup)
+        self.harness.begin()
+        self.harness.set_leader(True)
+        backend = self.harness.add_relation('storage-backend', 'cinder')
+        self.harness.update_config({'volume-backend-name': 'test'})
+        self.harness.add_relation_unit(backend, 'cinder/0')
+
+    def test_cinder_base(self):
+        self.assertEqual(
+            self.harness.framework.model.app.name,
+            'cinder-storpool')
+        # Test that charm is active upon installation.
+        self.harness.update_config({})
+        self.assertTrue(isinstance(
+            self.harness.model.unit.status, ActiveStatus))
+
+    def test_multipath_config(self):
+        self.harness.update_config({'use-multipath': True})
+        conf = dict(self.harness.charm.cinder_configuration(
+            dict(self.harness.model.config)))
+        self.assertEqual(conf['volume_backend_name'], 'test')
+        self.assertTrue(conf.get('use_multipath_for_image_xfer'))
+        self.assertTrue(conf.get('enforce_multipath_for_image_xfer'))
+
+    def test_cinder_configuration(self):
+        # Add check here that configuration is as expected.
+        pass