Add snap optimisations

Add optimisations to only install snap if it is absent and to
only update snap settings if they have changed.

Depends-On: If8086efcf7df4dcbe02be7454578dbbfb2d7945a
Change-Id: Icf2e9834cca6330eec92239aa5a5b76503c7c0f1
This commit is contained in:
Liam Young 2023-04-21 13:17:53 +00:00
parent 744491d03b
commit 123b9af8f2
3 changed files with 1139 additions and 49 deletions

File diff suppressed because it is too large Load Diff

View File

@ -27,9 +27,9 @@ import os
import secrets
import socket
import string
import subprocess
from typing import List
import charms.operator_libs_linux.v1.snap as snap
import ops.framework
import ops_sunbeam.charm as sunbeam_charm
import ops_sunbeam.guard as sunbeam_guard
@ -121,20 +121,47 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
self._state.metadata_secret = secret
return secret
def set_snap_data(self, snap_data):
"""Set snap setting if needed.
Update the snap with any settings that have changed.
"""
cache = snap.SnapCache()
hypervisor = cache["openstack-hypervisor"]
new_settings = {}
for k in sorted(snap_data.keys()):
try:
if snap_data[k] != hypervisor.get(k):
new_settings[k] = snap_data[k]
except snap.SnapError:
# Trying to retrieve an unset parameter results in a snapError
# so assume the snap.SnapError means there is missing config
# that needs setting.
new_settings[k] = snap_data[k]
if new_settings:
logger.debug(f"Applying new snap settings {new_settings}")
hypervisor.set(new_settings)
else:
logger.debug("Snap settings do not need updating")
def ensure_snap_present(self):
"""Install snap if it is not already present."""
config = self.model.config.get
try:
cache = snap.SnapCache()
hypervisor = cache["openstack-hypervisor"]
if not hypervisor.present:
hypervisor.ensure(snap.SnapState.Latest, channel=config("snap-channel"))
except snap.SnapError as e:
logger.error("An exception occurred when installing charmcraft. Reason: %s", e.message)
def configure_unit(self, event) -> None:
"""Run configuration on this unit."""
self.check_leader_ready()
self.check_relation_handlers_ready()
config = self.model.config.get
subprocess.check_call(
[
"snap",
"install",
"openstack-hypervisor",
"--channel",
config("snap-channel"),
]
)
self.ensure_snap_present()
local_ip = _get_local_ip_by_default_route()
try:
contexts = self.contexts()
@ -174,10 +201,7 @@ class HypervisorOperatorCharm(sunbeam_charm.OSBaseOperatorCharm):
}
except AttributeError as e:
raise sunbeam_guard.WaitingExceptionError("Data missing: {}".format(e.name))
cmd = ["snap", "set", "openstack-hypervisor"]
for k in sorted(snap_data.keys()):
cmd.append(f"{k}={snap_data[k]}")
subprocess.check_call(cmd)
self.set_snap_data(snap_data)
self.ensure_services_running()
self._state.unit_bootstrapped = True

View File

@ -16,6 +16,7 @@
import base64
import json
from unittest import mock
import ops_sunbeam.test_utils as test_utils
@ -32,7 +33,7 @@ class _HypervisorOperatorCharm(charm.HypervisorOperatorCharm):
class TestCharm(test_utils.CharmTestCase):
PATCHES = ["subprocess", "socket"]
PATCHES = ["socket", "snap"]
def setUp(self):
"""Setup OpenStack Hypervisor tests."""
@ -78,12 +79,14 @@ class TestCharm(test_utils.CharmTestCase):
def test_all_relations(self):
"""Test all the charms relations."""
hypervisor_snap_mock = 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.initial_setup()
self.harness.set_leader()
self.subprocess.check_call.assert_any_call(
["snap", "install", "openstack-hypervisor", "--channel", "essex/stable"]
)
hypervisor_snap_mock.ensure.assert_any_call("latest", channel="essex/stable")
test_utils.add_complete_amqp_relation(self.harness)
test_utils.add_complete_identity_credentials_relation(self.harness)
metadata = self.harness.charm.metadata_secret()
@ -93,34 +96,32 @@ class TestCharm(test_utils.CharmTestCase):
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.virt-type=kvm",
f"credentials.ovn-metadata-proxy-shared-secret={metadata}",
"identity.auth-url=None",
"identity.password=user-password",
"identity.project-domain-name=pdomain_-ame",
"identity.project-name=user-project",
"identity.region-name=region12",
"identity.user-domain-name=udomain-name",
"identity.username=username",
"logging.debug=false",
"network.dns-domain=openstack.local",
"network.dns-servers=8.8.8.8",
"network.enable-gateway=false",
"network.external-bridge=br-ex",
"network.external-bridge-address=10.20.20.1/24",
"network.ip-address=10.0.0.10",
f"network.ovn-cacert={ovn_cacert}",
f"network.ovn-cert={certificate}",
f"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",
]
expect_set_cmd = ["snap", "set", "openstack-hypervisor"]
expect_set_cmd.extend(expect_settings)
self.subprocess.check_call.assert_any_call(expect_set_cmd)
expect_settings = {
"compute.cpu-mode": "host-model",
"compute.spice-proxy-address": "10.0.0.10",
"compute.virt-type": "kvm",
"credentials.ovn-metadata-proxy-shared-secret": metadata,
"identity.auth-url": "http://10.20.21.11:80/openstack-keystone",
"identity.password": "user-password",
"identity.project-domain-name": "pdomain_-ame",
"identity.project-name": "user-project",
"identity.region-name": "region12",
"identity.user-domain-name": "udomain-name",
"identity.username": "username",
"logging.debug": "false",
"network.dns-domain": "openstack.local",
"network.dns-servers": "8.8.8.8",
"network.enable-gateway": "false",
"network.external-bridge": "br-ex",
"network.external-bridge-address": "10.20.20.1/24",
"network.ip-address": "10.0.0.10",
"network.ovn-cacert": ovn_cacert,
"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",
}
hypervisor_snap_mock.set.assert_any_call(expect_settings)