Add support for Ceph dashboard support

This change adds an admin-access interface to pass the admin
credentials and api endpoint details to another charm. The
dashboard charm uses this information registern the iscsi
gateways with the dashboard.

Change-Id: I5336ecab6a08e71dda22ba0347b8bee2f5683c3f
This commit is contained in:
Liam Young 2021-08-20 14:18:43 +00:00
parent 2027e50020
commit 0434825ea9
6 changed files with 148 additions and 23 deletions

View File

@ -23,6 +23,9 @@ requires:
interface: ceph-client
certificates:
interface: tls-certificates
provides:
admin-access:
interface: ceph-iscsi-admin-access
peers:
cluster:
interface: ceph-iscsi-peer

29
osci.yaml Normal file
View File

@ -0,0 +1,29 @@
- project:
templates:
- charm-unit-jobs
check:
jobs:
- ceph-iscsi-focal-octopus
- ceph-iscsi-focal-octopus-ec
vars:
needs_charm_build: true
charm_build_name: ceph-iscsi
build_type: charmcraft
- job:
name: ceph-iscsi-focal-octopus
parent: func-target
dependencies:
- osci-lint
- tox-py35
- tox-py36
- tox-py37
- tox-py38
vars:
tox_extra_args: focal
- job:
name: ceph-iscsi-focal-octopus-ec
parent: func-target
dependencies: &smoke-jobs
- ceph-iscsi-focal-octopus
vars:
tox_extra_args: focal-ec

View File

@ -4,3 +4,4 @@ git+https://github.com/canonical/operator.git#egg=ops
git+https://opendev.org/openstack/charm-ops-interface-ceph-client#egg=interface_ceph_client
git+https://opendev.org/openstack/charm-ops-openstack#egg=ops_openstack
git+https://opendev.org/openstack/charm-ops-interface-tls-certificates#egg=interface_tls_certificates
git+https://github.com/openstack-charmers/ops-interface-ceph-iscsi-admin-access#egg=interface_ceph_iscsi_admin_access

View File

@ -17,12 +17,12 @@
"""Charm for deploying and maintaining the Ceph iSCSI service."""
import copy
import socket
import logging
import os
import subprocess
import sys
import string
import socket
import secrets
from pathlib import Path
@ -36,6 +36,7 @@ import ops.model
import charmhelpers.core.host as ch_host
import charmhelpers.core.templating as ch_templating
import interface_ceph_client.ceph_client as ceph_client
import interface_ceph_iscsi_admin_access.admin_access as admin_access
import interface_ceph_iscsi_peer
import interface_tls_certificates.ca_client as ca_client
@ -102,7 +103,20 @@ class GatewayClientPeerAdapter(
"""
ips = copy.deepcopy(self.allowed_ips)
ips.extend(self.relation.peer_addresses)
return ' '.join(sorted(ips))
return ','.join(sorted(ips))
class AdminAccessAdapter(
ops_openstack.adapters.OpenStackOperRelationAdapter):
@property
def trusted_ips(self):
"""List of IP addresses permitted to use API.
:returns: Ceph iSCSI clients
:rtype: str
"""
return ','.join(sorted(self.relation.client_addresses))
class TLSCertificatesAdapter(
@ -130,6 +144,7 @@ class CephISCSIGatewayAdapters(
'ceph-client': CephClientAdapter,
'cluster': GatewayClientPeerAdapter,
'certificates': TLSCertificatesAdapter,
'admin-access': AdminAccessAdapter,
}
@ -184,12 +199,19 @@ class CephISCSIGatewayCharmBase(
self.peers = interface_ceph_iscsi_peer.CephISCSIGatewayPeers(
self,
'cluster')
self.admin_access = \
admin_access.CephISCSIAdminAccessProvides(
self,
'admin-access')
self.ca_client = ca_client.CAClient(
self,
'certificates')
self.adapters = CephISCSIGatewayAdapters(
(self.ceph_client, self.peers, self.ca_client),
(self.ceph_client, self.peers, self.ca_client, self.admin_access),
self)
self.framework.observe(
self.admin_access.on.admin_access_request,
self.publish_admin_access_info)
self.framework.observe(
self.ceph_client.on.broker_available,
self.request_ceph_pool)
@ -240,6 +262,7 @@ class CephISCSIGatewayCharmBase(
alphabet = string.ascii_letters + string.digits
password = ''.join(secrets.choice(alphabet) for i in range(8))
self.peers.set_admin_password(password)
self.publish_admin_access_info(event)
def config_get(self, key):
"""Retrieve config option.
@ -274,7 +297,6 @@ class CephISCSIGatewayCharmBase(
def request_ceph_pool(self, event):
"""Request pools from Ceph cluster."""
print("request_ceph_pool")
if not self.ceph_client.broker_available:
logging.info("Cannot request ceph setup at this time")
return
@ -440,8 +462,26 @@ class CephISCSIGatewayCharmBase(
encoding=serialization.Encoding.PEM))
subprocess.check_call(['update-ca-certificates'])
self._stored.enable_tls = True
# Endpoint has switch to TLS, need to inform users.
self.publish_admin_access_info(event)
self.render_config(event)
def publish_admin_access_info(self, event):
"""Publish creds and endpoint to related charms"""
if not self.peers.admin_password:
logging.info("Defering setup")
event.defer()
return
if self._stored.enable_tls:
scheme = 'https'
else:
scheme = 'http'
self.admin_access.publish_gateway(
socket.getfqdn(),
'admin',
self.peers.admin_password,
scheme)
def custom_status_check(self):
"""Custom update status checks."""
if ch_host.is_container():

View File

@ -11,4 +11,8 @@ api_secure = {{ certificates.enable_tls }}
api_user = admin
api_password = {{ cluster.admin_password }}
api_port = 5000
{% if admin_access.trusted_ips -%}
trusted_ip_list = {{ cluster.trusted_ips }},{{ admin_access.trusted_ips }}
{% else -%}
trusted_ip_list = {{ cluster.trusted_ips }}
{% endif -%}

View File

@ -139,8 +139,10 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
PATCHES = [
'ch_templating',
'gwcli_client',
'subprocess',
'os',
'secrets',
'socket',
'subprocess',
]
def setUp(self):
@ -148,6 +150,12 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
self.harness = Harness(
_CephISCSIGatewayCharmBase,
)
self.test_hostname = 'server1'
self.socket.gethostname.return_value = self.test_hostname
self.test_fqdn = self.test_hostname + '.foo'
self.socket.getfqdn.return_value = self.test_fqdn
self.secrets.choice.return_value = 'r'
self.test_admin_password = 'rrrrrrrr'
self.gwc = MagicMock()
self.gwcli_client.GatewayClient.return_value = self.gwc
@ -182,7 +190,7 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
self.assertFalse(self.harness.charm._stored.target_created)
self.assertFalse(self.harness.charm._stored.enable_tls)
def add_cluster_relation(self):
def add_base_cluster_relation(self):
rel_id = self.harness.add_relation('cluster', 'ceph-iscsi')
self.harness.add_relation_unit(
rel_id,
@ -197,10 +205,33 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
})
return rel_id
def complete_cluster_relation(self, rel_id):
self.harness.update_relation_data(
rel_id,
'ceph-iscsi/1',
{
'ingress-address': '10.0.0.2',
'gateway_ready': 'True',
'gateway_fqdn': 'ceph-iscsi-1.example'
})
def add_admin_access_relation(self):
rel_id = self.harness.add_relation('admin-access', 'ceph-dashboard')
self.harness.add_relation_unit(
rel_id,
'ceph-dashboard/0')
self.harness.update_relation_data(
rel_id,
'ceph-dashboard/0',
{
'ingress-address': '10.0.0.2',
})
return rel_id
@patch('socket.getfqdn')
def test_on_create_target_action(self, _getfqdn):
_getfqdn.return_value = 'ceph-iscsi-0.example'
self.add_cluster_relation()
self.add_base_cluster_relation()
self.harness.begin()
action_event = MagicMock()
action_event.params = {
@ -245,7 +276,7 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
@patch('socket.getfqdn')
def test_on_create_target_action_ec(self, _getfqdn):
_getfqdn.return_value = 'ceph-iscsi-0.example'
self.add_cluster_relation()
self.add_base_cluster_relation()
self.harness.begin()
action_event = MagicMock()
action_event.params = {
@ -296,10 +327,8 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
'iscsi-metapool',
'disk1')
@patch.object(charm.secrets, 'choice')
def test_on_has_peers(self, _choice):
def test_on_has_peers(self):
rel_id = self.harness.add_relation('cluster', 'ceph-iscsi')
_choice.return_value = 'r'
self.harness.begin()
self.harness.add_relation_unit(
rel_id,
@ -316,10 +345,10 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
'gateway_fqdn': 'ceph-iscsi-1.example'
})
self.assertEqual(
self.harness.charm.peers.admin_password, 'rrrrrrrr')
self.harness.charm.peers.admin_password, self.test_admin_password)
def test_on_has_peers_not_leader(self):
self.add_cluster_relation()
self.add_base_cluster_relation()
self.harness.begin()
self.assertIsNone(
self.harness.charm.peers.admin_password)
@ -329,7 +358,7 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
self.harness.charm.peers.admin_password)
def test_on_has_peers_existing_password(self):
rel_id = self.add_cluster_relation()
rel_id = self.add_base_cluster_relation()
self.harness.update_relation_data(
rel_id,
'ceph-iscsi',
@ -370,7 +399,7 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
def test_on_pools_available(self):
self.os.path.exists.return_value = False
self.os.path.basename = os.path.basename
rel_id = self.add_cluster_relation()
rel_id = self.add_base_cluster_relation()
self.harness.update_relation_data(
rel_id,
'ceph-iscsi',
@ -392,9 +421,7 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
rel_data = self.harness.get_relation_data(rel_id, 'ceph-iscsi/0')
self.assertEqual(rel_data['gateway_ready'], 'True')
@patch('socket.gethostname')
def test_on_certificates_relation_joined(self, _gethostname):
_gethostname.return_value = 'server1'
def test_on_certificates_relation_joined(self):
rel_id = self.harness.add_relation('certificates', 'vault')
self.harness.begin()
self.harness.add_relation_unit(
@ -407,19 +434,17 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
rel_data = self.harness.get_relation_data(rel_id, 'ceph-iscsi/0')
self.assertEqual(
rel_data['application_cert_requests'],
'{"server1": {"sans": ["10.0.0.10", "server1"]}}')
'{"server1.foo": {"sans": ["10.0.0.10", "server1"]}}')
@patch('socket.gethostname')
def test_on_certificates_relation_changed(self, _gethostname):
def test_on_certificates_relation_changed(self):
mock_TLS_CERT_PATH = MagicMock()
mock_TLS_CA_CERT_PATH = MagicMock()
mock_TLS_KEY_PATH = MagicMock()
mock_KEY_AND_CERT_PATH = MagicMock()
mock_TLS_PUB_KEY_PATH = MagicMock()
_gethostname.return_value = 'server1'
self.subprocess.check_output.return_value = b'pubkey'
rel_id = self.harness.add_relation('certificates', 'vault')
self.add_cluster_relation()
self.add_base_cluster_relation()
self.harness.begin()
self.harness.charm.TLS_CERT_PATH = mock_TLS_CERT_PATH
self.harness.charm.TLS_CA_CERT_PATH = mock_TLS_CA_CERT_PATH
@ -460,3 +485,26 @@ class TestCephISCSIGatewayCharmBase(CharmTestCase):
self.assertIsInstance(
self.harness.charm.unit.status,
BlockedStatus)
def test_publish_admin_access_info(self):
cluster_rel_id = self.add_base_cluster_relation()
admin_access_rel_id = self.add_admin_access_relation()
self.harness.begin()
self.harness.set_leader()
self.complete_cluster_relation(cluster_rel_id)
self.assertEqual(
self.harness.get_relation_data(
admin_access_rel_id,
'ceph-iscsi/0'),
{
'host': '10.0.0.10',
'name': self.test_fqdn,
'port': '5000',
'scheme': 'http'})
self.assertEqual(
self.harness.get_relation_data(
admin_access_rel_id,
'ceph-iscsi'),
{
'password': self.test_admin_password,
'username': 'admin'})