Guillaume Boutry 5e13d3a679
[hypervisor] Add hostname as subject alternative names
Qemu blockdev-add command is unhappy when the hostname is not part of
the sAN in the certificates (even if it's already in the CN). Add
hostname to certificate's subject alternative names.

If there is a migration address, it will be the prefered one for qemu
migration, therefore also include hostname exposed on this address.

Change-Id: I7a1f0e9e0a21f8dbc4bab94acec4f1c5b445a054
Signed-off-by: Guillaume Boutry <guillaume.boutry@canonical.com>
2024-10-08 16:07:12 +02:00

271 lines
11 KiB
Python

# Copyright 2023 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.
"""Tests for Openstack hypervisor charm."""
import base64
from unittest.mock import (
MagicMock,
)
import charm
import ops_sunbeam.test_utils as test_utils
class _HypervisorOperatorCharm(charm.HypervisorOperatorCharm):
"""Neutron test charm."""
def __init__(self, framework):
"""Setup event logging."""
self.seen_events = []
super().__init__(framework)
class TestCharm(test_utils.CharmTestCase):
"""Test charm to test relations."""
PATCHES = ["socket", "snap", "get_local_ip_by_default_route", "os"]
def setUp(self):
"""Setup OpenStack Hypervisor tests."""
super().setUp(charm, self.PATCHES)
self.harness = test_utils.get_harness(
_HypervisorOperatorCharm,
container_calls=self.container_calls,
)
self.addCleanup(self.harness.cleanup)
def initial_setup(self):
"""Setting up relations."""
self.harness.update_config({"snap-channel": "essex/stable"})
self.harness.begin_with_initial_hooks()
test_utils.add_complete_certificates_relation(self.harness)
ovs_rel_id = self.harness.add_relation("ovsdb-cms", "ovn-relay")
self.harness.add_relation_unit(ovs_rel_id, "ovn-relay/0")
self.harness.update_relation_data(
ovs_rel_id,
"ovn-relay/0",
{
"bound-address": "10.1.176.143",
"bound-hostname": "ovn-relay-0.ovn-relay-endpoints.openstack.svc.cluster.local",
"egress-subnets": "10.20.21.10/32",
"ingress-address": "10.20.21.10",
"ingress-bound-address": "10.20.21.10",
"private-address": "10.20.21.10",
},
)
ceph_rel_id = self.harness.add_relation("ceph-access", "cinder-ceph")
self.harness.add_relation_unit(ceph_rel_id, "cinder-ceph/0")
credentials_content = {"uuid": "ddd", "key": "eee"}
credentials_id = self.harness.add_model_secret(
"cinder-ceph", credentials_content
)
self.harness.grant_secret(credentials_id, self.harness.charm.app.name)
self.harness.update_relation_data(
ceph_rel_id,
"cinder-ceph",
{"access-credentials": credentials_id},
)
def test_mandatory_relations(self):
"""Test all the charms relations."""
self.get_local_ip_by_default_route.return_value = "10.0.0.10"
hypervisor_snap_mock = MagicMock()
hypervisor_snap_mock.present = False
self.snap.SnapState.Latest = "latest"
self.snap.SnapCache.return_value = {
"openstack-hypervisor": hypervisor_snap_mock
}
self.socket.getfqdn.return_value = "test.local"
self.socket.gethostname.return_value = "test"
self.initial_setup()
self.harness.set_leader()
test_utils.add_complete_amqp_relation(self.harness)
test_utils.add_complete_identity_credentials_relation(self.harness)
# Add nova-service relation
self.harness.add_relation(
"nova-service",
"nova",
app_data={
"spice-proxy-url": "http://INGRESS_IP/nova-spiceproxy/spice_auto.html"
},
)
hypervisor_snap_mock.ensure.assert_any_call(
"latest", channel="essex/stable"
)
metadata = self.harness.charm.metadata_secret()
cacert = test_utils.TEST_CA
cacert_with_intermediates = (
test_utils.TEST_CA + "\n" + "\n".join(test_utils.TEST_CHAIN)
)
cacert = base64.b64encode(cacert.encode()).decode()
cacert_with_intermediates = base64.b64encode(
cacert_with_intermediates.encode()
).decode()
private_key = base64.b64encode(
self.harness.charm.contexts().certificates.key.encode()
).decode()
certificate = base64.b64encode(
test_utils.TEST_SERVER_CERT.encode()
).decode()
expect_settings = {
"compute.cpu-mode": "host-model",
"compute.spice-proxy-address": "10.0.0.10",
"compute.cacert": cacert,
"compute.cert": certificate,
"compute.key": private_key,
"compute.migration-address": "10.0.0.10",
"compute.resume-on-boot": True,
"compute.rbd-user": "nova",
"compute.rbd-secret-uuid": "ddd",
"compute.rbd-key": "eee",
"compute.spice-proxy-url": "http://INGRESS_IP/nova-spiceproxy/spice_auto.html",
"credentials.ovn-metadata-proxy-shared-secret": metadata,
"identity.admin-role": None,
"identity.auth-url": "http://10.153.2.45:80/openstack-keystone",
"identity.password": "user-password",
"identity.project-domain-id": "pdomain-id",
"identity.project-domain-name": "pdomain_-ame",
"identity.project-name": "user-project",
"identity.region-name": "region12",
"identity.user-domain-id": "udomain-id",
"identity.user-domain-name": "udomain-name",
"identity.username": "username",
"logging.debug": False,
"monitoring.enable": False,
"network.dns-servers": "8.8.8.8",
"network.external-bridge": "br-ex",
"network.external-bridge-address": "10.20.20.1/24",
"network.ip-address": "10.0.0.10",
"network.ovn-cacert": cacert_with_intermediates,
"network.ovn-cert": certificate,
"network.ovn-key": private_key,
"network.ovn-sb-connection": "ssl:10.20.21.10:6642",
"network.physnet-name": "physnet1",
"node.fqdn": "test.local",
"node.ip-address": "10.0.0.10",
"rabbitmq.url": "rabbit://hypervisor:rabbit.pass@10.0.0.13:5672/openstack",
"telemetry.enable": False,
"ca.bundle": None,
}
hypervisor_snap_mock.set.assert_any_call(expect_settings, typed=True)
def test_all_relations(self):
"""Test all the charms relations."""
# Add cos-agent relation
self.harness.add_relation(
"cos-agent",
"grafana-agent",
unit_data={
"config": '{"metrics_alert_rules": {}, "log_alert_rules": {}, "dashboards": ["/Td6WFoAAATm1rRGAAAAABzfRCEftvN9AQAAAAAEWVo="], "metrics_scrape_jobs": [{"metrics_path": "/metrics", "static_configs": [{"targets": ["localhost:9177"]}]], "log_slots": []}',
"egress-subnets": "10.1.171.64/32",
"ingress-address": "10.1.171.64",
"private-address": "10.1.171.64",
},
)
# Add ceilometer-service relation
self.harness.add_relation(
"ceilometer-service",
"ceilometer",
app_data={"telemetry-secret": "FAKE_SECRET"},
)
# Add nova-service relation
self.harness.add_relation(
"nova-service",
"nova",
app_data={
"spice-proxy-url": "http://INGRESS_IP/nova-spiceproxy/spiceauto.html"
},
)
self.get_local_ip_by_default_route.return_value = "10.0.0.10"
hypervisor_snap_mock = MagicMock()
hypervisor_snap_mock.present = False
self.snap.SnapState.Latest = "latest"
self.snap.SnapCache.return_value = {
"openstack-hypervisor": hypervisor_snap_mock
}
self.socket.getfqdn.return_value = "test.local"
self.socket.gethostname.return_value = "test"
self.initial_setup()
self.harness.set_leader()
test_utils.add_complete_amqp_relation(self.harness)
test_utils.add_complete_identity_credentials_relation(self.harness)
hypervisor_snap_mock.ensure.assert_any_call(
"latest", channel="essex/stable"
)
metadata = self.harness.charm.metadata_secret()
cacert = test_utils.TEST_CA
cacert_with_intermediates = (
test_utils.TEST_CA + "\n" + "\n".join(test_utils.TEST_CHAIN)
)
cacert = base64.b64encode(cacert.encode()).decode()
cacert_with_intermediates = base64.b64encode(
cacert_with_intermediates.encode()
).decode()
private_key = base64.b64encode(
self.harness.charm.contexts().certificates.key.encode()
).decode()
certificate = base64.b64encode(
test_utils.TEST_SERVER_CERT.encode()
).decode()
expect_settings = {
"compute.cpu-mode": "host-model",
"compute.spice-proxy-address": "10.0.0.10",
"compute.cacert": cacert,
"compute.cert": certificate,
"compute.key": private_key,
"compute.migration-address": "10.0.0.10",
"compute.resume-on-boot": True,
"compute.rbd-user": "nova",
"compute.rbd-secret-uuid": "ddd",
"compute.rbd-key": "eee",
"compute.spice-proxy-url": "http://INGRESS_IP/nova-spiceproxy/spiceauto.html",
"credentials.ovn-metadata-proxy-shared-secret": metadata,
"identity.admin-role": None,
"identity.auth-url": "http://10.153.2.45:80/openstack-keystone",
"identity.password": "user-password",
"identity.project-domain-id": "pdomain-id",
"identity.project-domain-name": "pdomain_-ame",
"identity.project-name": "user-project",
"identity.region-name": "region12",
"identity.user-domain-id": "udomain-id",
"identity.user-domain-name": "udomain-name",
"identity.username": "username",
"logging.debug": False,
"monitoring.enable": True,
"network.dns-servers": "8.8.8.8",
"network.external-bridge": "br-ex",
"network.external-bridge-address": "10.20.20.1/24",
"network.ip-address": "10.0.0.10",
"network.ovn-cacert": cacert_with_intermediates,
"network.ovn-cert": certificate,
"network.ovn-key": private_key,
"network.ovn-sb-connection": "ssl:10.20.21.10:6642",
"network.physnet-name": "physnet1",
"node.fqdn": "test.local",
"node.ip-address": "10.0.0.10",
"rabbitmq.url": "rabbit://hypervisor:rabbit.pass@10.0.0.13:5672/openstack",
"telemetry.enable": True,
"telemetry.publisher-secret": "FAKE_SECRET",
"ca.bundle": None,
}
hypervisor_snap_mock.set.assert_any_call(expect_settings, typed=True)