Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Sunbeam support to OpenStack plugin #996

Merged
merged 1 commit into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions examples/hotsos-example-kubernetes.summary.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,15 @@ kubernetes:
- kube-proxy (1)
- kube-scheduler (1)
snaps:
- cdk-addons 1.23.0
- core 16-2.54.2
- core18 20211215
- core20 20220114
- kube-apiserver 1.23.3
- kube-controller-manager 1.23.3
- kube-proxy 1.23.3
- kube-scheduler 1.23.3
- kubectl 1.23.3
- cdk-addons 1.23.0 (latest/stable)
- core 16-2.54.2 (latest/stable)
- core18 20211215 (latest/stable)
- core20 20220114 (latest/stable)
- kube-apiserver 1.23.3 (latest/stable)
- kube-controller-manager 1.23.3 (latest/stable)
- kube-proxy 1.23.3 (latest/stable)
- kube-scheduler 1.23.3 (latest/stable)
- kubectl 1.23.3 (latest/stable)
dpkg:
- vaultlocker 1.0.6-0ubuntu1
flannel:
Expand Down
2 changes: 1 addition & 1 deletion examples/hotsos-example-vault.summary.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,4 @@ vault:
- vault-mysql-router
ps: []
snaps:
- vault 1.5.9
- vault 1.5.9 (1.5/stable)
3 changes: 2 additions & 1 deletion hotsos/core/host_helpers/packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -654,7 +654,8 @@ def all_formatted(self):
if not _all:
return []

return [f"{name} {info['version']}" for name, info in _all.items()]
return [f"{name} {info['version']} ({info['channel']})"
for name, info in _all.items()]

@property
def core(self):
Expand Down
22 changes: 15 additions & 7 deletions hotsos/core/plugins/kubernetes.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
# Snap-only deps
K8S_PACKAGE_DEPS_SNAP = [r'core[0-9]*']
K8S_SNAP_DEPS = K8S_PACKAGE_DEPS + K8S_PACKAGE_DEPS_SNAP
MICROK8S_COMMON = 'var/snap/microk8s/common'


@dataclass
Expand Down Expand Up @@ -88,9 +89,12 @@ def pods(self):
pods = []
pods_path = os.path.join(HotSOSConfig.data_root,
"var/log/pods")
if os.path.exists(pods_path):
for pod in os.listdir(pods_path):
pods.append(pod)
mk8s_pods_path = os.path.join(HotSOSConfig.data_root,
MICROK8S_COMMON, '/var/log/pods')
for path in [pods_path, mk8s_pods_path]:
if os.path.exists(path):
for pod in os.listdir(path):
pods.append(pod)

return sorted(pods)

Expand All @@ -99,10 +103,14 @@ def containers(self):
containers = []
containers_path = os.path.join(HotSOSConfig.data_root,
"var/log/containers")
if os.path.exists(containers_path):
for pod in os.listdir(containers_path):
pod = pod.partition('.log')[0]
containers.append(pod)
mk8s_containers_path = os.path.join(HotSOSConfig.data_root,
MICROK8S_COMMON,
'var/log/containers')
for path in [containers_path, mk8s_containers_path]:
if os.path.exists(path):
for ctr in os.listdir(path):
ctr = ctr.partition('.log')[0]
containers.append(ctr)

return sorted(containers)

Expand Down
43 changes: 31 additions & 12 deletions hotsos/core/plugins/openstack/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
DPKGVersion,
InstallInfoBase,
PebbleHelper,
SnapPackageHelper,
SystemdHelper,
SSLCertificate,
SSLCertificatesHelper,
Expand All @@ -22,6 +23,8 @@
OSTProjectCatalog,
OST_EOL_INFO,
OST_REL_INFO,
OST_SUNBEAM_REL_INFO,
OST_SUNBEAM_SNAP_NAMES,
)
from hotsos.core.plugins.openstack.neutron import NeutronBase
from hotsos.core.plugins.openstack.nova import NovaBase
Expand All @@ -47,26 +50,29 @@ class OpenStackInstallInfo(InstallInfoBase):

def __post_init__(self):
service_exprs = self.project_catalog.service_exprs
core_pkgs = self.project_catalog.packages_core_exprs
other_pkgs = self.project_catalog.packages_dep_exprs
snap_core_pkgs = self.project_catalog.snap_core_exprs
core_pkgs = self.project_catalog.apt_core_exprs
other_pkgs = self.project_catalog.apt_dep_exprs

self.apt = APTPackageHelper(core_pkgs=core_pkgs,
other_pkgs=other_pkgs)
self.docker = DockerImageHelper(core_pkgs=core_pkgs,
other_pkgs=other_pkgs)
self.snaps = SnapPackageHelper(core_snaps=snap_core_pkgs)
self.pebble = PebbleHelper(service_exprs=service_exprs)
self.systemd = SystemdHelper(service_exprs=service_exprs)

def mixin(self, _self):
_self.apt = self.apt
_self.docker = self.docker
_self.pebble = self.pebble
_self.snaps = self.snaps
_self.systemd = self.systemd


class OpenstackBase():
class OpenstackBase(): # pylint: disable=too-many-instance-attributes
"""
Base class for Openstack checks.
Base class for OpenStack checks.

Contains per-service information such as packages, versions, config etc.
"""
Expand All @@ -77,19 +83,14 @@ def __init__(self, *args, **kwargs):
OctaviaBase())
self.project_catalog = OSTProjectCatalog()
# Keep pylint happy
self.apt = self.pebble = self.docker = self.systemd = None
self.apt = self.pebble = self.docker = self.snaps = self.systemd = None
OpenStackInstallInfo(project_catalog=self.project_catalog).mixin(self)

@cached_property
def apt_source_path(self):
return os.path.join(HotSOSConfig.data_root, 'etc/apt/sources.list.d')

@cached_property
def installed_pkg_release_names(self):
"""
Get release name for each installed package that we are tracking and
return as a list of names. The list should normally have length 1.
"""
def _get_apt_relnames(self):
relnames = set()
for pkg, values in OST_REL_INFO.items():
if pkg in self.apt.core:
Expand All @@ -110,6 +111,23 @@ def installed_pkg_release_names(self):
if r_lt:
relnames.add(r_lt)

return relnames

@cached_property
def installed_pkg_release_names(self):
"""
Get release name for each installed package that we are tracking and
return as a list of names. The list should normally have length 1.
"""
if self.apt.core:
relnames = self._get_apt_relnames()
else:
relnames = set()
for pkg in OST_SUNBEAM_SNAP_NAMES:
if pkg in self.snaps.core:
ver = self.snaps.get_version(pkg)
relnames.add(OST_SUNBEAM_REL_INFO[ver])

log.debug("release name(s) found: %s", ','.join(relnames))
return list(relnames)

Expand Down Expand Up @@ -277,7 +295,8 @@ def is_runnable(cls):

@return: True or False
"""
if OpenstackBase().apt.core:
ost_common = OpenstackBase()
if ost_common.apt.core or ost_common.snaps.core:
return True

return False
Expand Down
21 changes: 19 additions & 2 deletions hotsos/core/plugins/openstack/openstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,16 @@
'stein': '1.0.0'}
}

OST_SUNBEAM_SNAP_NAMES = ['openstack', 'openstack-hypervisor']

OST_SUNBEAM_REL_INFO = {
'2025.1': 'epoxy',
'2024.2': 'dalmation',
'2024.1': 'caracal',
'2023.2': 'bobcat',
'2023.1': 'antelope',
}

OST_EXCEPTIONS = {'barbican': BARBICAN_EXCEPTIONS + CASTELLAN_EXCEPTIONS +
OSLO_MESSAGING_EXCEPTIONS,
'cinder': CINDER_EXCEPTIONS + CASTELLAN_EXCEPTIONS +
Expand Down Expand Up @@ -496,6 +506,9 @@ class OSTProjectCatalog():
'radvd',
]

# ref: https://github.com/orgs/canonical/repositories?q=%22snap-open%22
SNAP_DEPS_SUNBEAM = [r'openstack\S*']

def __init__(self):
self._projects = {}
self.add('aodh', config={'main': 'aodh.conf'},
Expand Down Expand Up @@ -621,15 +634,19 @@ def add(self, name, *args, **kwargs):
self._projects[name] = OSTProject(params)

@cached_property
def packages_core_exprs(self):
def snap_core_exprs(self):
return self.SNAP_DEPS_SUNBEAM

@cached_property
def apt_core_exprs(self):
core = set()
for p in self.all.values():
core.update(p.apt_params.core)

return list(core)

@cached_property
def packages_dep_exprs(self):
def apt_dep_exprs(self):
deps = set(self.APT_DEPS_COMMON)
for p in self.all.values():
deps.update(p.apt_params.deps)
Expand Down
18 changes: 10 additions & 8 deletions hotsos/plugin_extensions/juju/summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,15 +136,17 @@ def summary_units(self):
name, _, ver = u.name.rpartition('-')
u_name = f"{name}/{ver}"
unit_info[u_name] = {}
if u.repo_info:
c_name = u.charm_name
if c_name:
unit_info[u_name]['charm'] = {'name': c_name}
c_name = u.charm_name
if c_name:
charm = {'name': c_name}
unit_info[u_name]['charm'] = charm
if u.repo_info:
sha1 = u.repo_info.get('commit')
unit_info[u_name]['charm']['repo-info'] = sha1
if c_name in self.charms:
unit_info[u_name]['charm']['version'] = \
self.charms[c_name].version
charm['repo-info'] = sha1

if c_name in self.charms:
charm['version'] = \
self.charms[c_name].version

if u.name in loginfo:
unit_info[u_name]['logs'] = loginfo[u.name]
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/host_helpers/test_packaging.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def test_all(self):
self.assertEqual(obj.get_version("lxd"), "4.22")

def test_formatted(self):
expected = ['core20 20220114']
expected = ['core20 20220114 (latest/stable)']
obj = host_pack.SnapPackageHelper(["core20"])
self.assertEqual(obj.all_formatted, expected)

Expand Down
11 changes: 11 additions & 0 deletions tests/unit/test_juju.py
Original file line number Diff line number Diff line change
Expand Up @@ -185,6 +185,17 @@ def test_get_unit_info(self):
self.assertEqual(self.part_output_to_actual(inst.output)['units'],
expected)

@utils.create_data_root(
{('var/lib/juju/agents/unit-sunbeam-machine-1/'
'state/deployer/manifests/'
'ch_3a_amd64_2f_jammy_2f_sunbeam-machine-32'): ''})
def test_get_unit_info_no_repo_info(self):
expected = {'sunbeam-machine/1':
{'charm': {'name': 'sunbeam-machine', 'version': 32}}}
inst = summary.JujuSummary()
self.assertEqual(self.part_output_to_actual(inst.output)['units'],
expected)


@utils.load_templated_tests('scenarios/juju')
class TestJujuScenarios(JujuTestsBase):
Expand Down
26 changes: 13 additions & 13 deletions tests/unit/test_kubernetes.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,15 +62,15 @@ def test_services(self):
expected)

def test_snaps(self):
result = ['cdk-addons 1.23.0',
'core 16-2.54.2',
'core18 20211215',
'core20 20220114',
'kube-apiserver 1.23.3',
'kube-controller-manager 1.23.3',
'kube-proxy 1.23.3',
'kube-scheduler 1.23.3',
'kubectl 1.23.3']
result = ['cdk-addons 1.23.0 (latest/stable)',
'core 16-2.54.2 (latest/stable)',
'core18 20211215 (latest/stable)',
'core20 20220114 (latest/stable)',
'kube-apiserver 1.23.3 (latest/stable)',
'kube-controller-manager 1.23.3 (latest/stable)',
'kube-proxy 1.23.3 (latest/stable)',
'kube-scheduler 1.23.3 (latest/stable)',
'kubectl 1.23.3 (latest/stable)']
inst = summary.KubernetesSummary()
self.assertEqual(self.part_output_to_actual(inst.output)['snaps'],
result)
Expand All @@ -97,10 +97,10 @@ def test_snaps_microk8s(self, mock_helper):
mock_helper.return_value.snap_list_all.return_value = \
SNAP_LIST_ALL_MICROK8S.splitlines()
inst = summary.KubernetesSummary()
result = ['core18 20230320',
'core20 20230308',
'core22 20230404',
'microk8s v1.26.4']
result = ['core18 20230320 (latest/stable)',
'core20 20230308 (latest/stable)',
'core22 20230404 (latest/stable)',
'microk8s v1.26.4 (1.26/stable)']
self.assertTrue(inst.is_runnable())
self.assertEqual(self.part_output_to_actual(inst.output)['snaps'],
result)
Expand Down
2 changes: 1 addition & 1 deletion tests/unit/test_lxd.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ def test_summary_keys(self):
'snap.lxd.user-daemon'],
'transient': ['snap.lxd.workaround']}},

'snaps': ['lxd 4.22']}
'snaps': ['lxd 4.22 (latest/stable)']}
inst = summary.LXDSummary()
self.assertEqual(self.part_output_to_actual(inst.output), expected)

Expand Down
37 changes: 34 additions & 3 deletions tests/unit/test_openstack.py
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,22 @@

ARM_QEMU_PS_OUT = """libvirt+ 23849 6.8 0.0 6083100 1237380 ? - Apr29 670:43 /usr/bin/qemu-system-aarch64 -name guest=instance-00037cd9,debug-threads=on -S -object {"qom-type":"secret","id":"masterKey0","format":"raw","file":"/var/lib/libvirt/qemu/domain-1-instance-00037cd9/master-key.aes"} -blockdev {"driver":"file","filename":"/usr/share/AAVMF/AAVMF_CODE.fd","node-name":"libvirt-pflash0-storage","auto-read-only":true,"discard":"unmap"} -blockdev {"node-name":"libvirt-pflash0-format","read-only":true,"driver":"raw","file":"libvirt-pflash0-storage"} -blockdev {"driver":"file","filename":"/var/lib/libvirt/qemu/nvram/instance-00037cd9_VARS.fd","node-name":"libvirt-pflash1-storage","auto-read-only":true,"discard":"unmap"} -blockdev {"node-name":"libvirt-pflash1-format","read-only":false,"driver":"raw","file":"libvirt-pflash1-storage"} -machine virt-6.2,usb=off,dump-guest-core=off,gic-version=3,pflash0=libvirt-pflash0-format,pflash1=libvirt-pflash1-format,memory-backend=mach-virt.ram -accel kvm -cpu host -m 1024 -object {"qom-type":"memory-backend-ram","id":"mach-virt.ram","size":1073741824} -overcommit mem-lock=off -smp 1,sockets=1,dies=1,cores=1,threads=1 -uuid 66a20f33-f273-49bc-b738-936eebfbd8c5 -no-user-config -nodefaults -chardev socket,id=charmonitor,fd=33,server=on,wait=off -mon chardev=charmonitor,id=monitor,mode=control -rtc base=utc,driftfix=slew -no-shutdown -boot strict=on -device pcie-root-port,port=8,chassis=1,id=pci.1,bus=pcie.0,multifunction=on,addr=0x1 -device pcie-root-port,port=9,chassis=2,id=pci.2,bus=pcie.0,addr=0x1.0x1 -device pcie-root-port,port=10,chassis=3,id=pci.3,bus=pcie.0,addr=0x1.0x2 -device pcie-root-port,port=11,chassis=4,id=pci.4,bus=pcie.0,addr=0x1.0x3 -device pcie-root-port,port=12,chassis=5,id=pci.5,bus=pcie.0,addr=0x1.0x4 -device pcie-root-port,port=13,chassis=6,id=pci.6,bus=pcie.0,addr=0x1.0x5 -device pcie-root-port,port=14,chassis=7,id=pci.7,bus=pcie.0,addr=0x1.0x6 -device pcie-root-port,port=15,chassis=8,id=pci.8,bus=pcie.0,addr=0x1.0x7 -device qemu-xhci,id=usb,bus=pci.2,addr=0x0 -device virtio-serial-pci,id=virtio-serial0,bus=pci.3,addr=0x0 -blockdev {"driver":"file","filename":"/var/lib/nova/instances/_base/5b9f93fbfce101950757660101d52f139dcbdeb1","node-name":"libvirt-2-storage","cache":{"direct":true,"no-flush":false},"auto-read-only":true,"discard":"unmap"} -blockdev {"node-name":"libvirt-2-format","read-only":true,"discard":"unmap","cache":{"direct":true,"no-flush":false},"driver":"raw","file":"libvirt-2-storage"} -blockdev {"driver":"file","filename":"/var/lib/nova/instances/66a20f33-f273-49bc-b738-936eebfbd8c5/disk","node-name":"libvirt-1-storage","cache":{"direct":true,"no-flush":false},"auto-read-only":true,"discard":"unmap"} -blockdev {"node-name":"libvirt-1-format","read-only":false,"discard":"unmap","cache":{"direct":true,"no-flush":false},"driver":"qcow2","file":"libvirt-1-storage","backing":"libvirt-2-format"} -device virtio-blk-pci,bus=pci.4,addr=0x0,drive=libvirt-1-format,id=virtio-disk0,bootindex=1,write-cache=on -netdev tap,fd=35,id=hostnet0,vhost=on,vhostfd=37 -device virtio-net-pci,host_mtu=1500,netdev=hostnet0,id=net0,mac=fa:16:5d:40:33:0d,bus=pci.1,addr=0x0 -add-fd set=3,fd=34 -chardev pty,id=charserial0,logfile=/dev/fdset/3,logappend=on -serial chardev:charserial0 -chardev spicevmc,id=charchannel0,name=vdagent -device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=com.redhat.spice.0 -device usb-kbd,id=input0,bus=usb.0,port=1 -audiodev {"id":"audio1","driver":"spice"} -spice port=5900,addr=10.140.52.133,disable-ticketing=on,seamless-migration=on -device virtio-vga,id=video0,max_outputs=1,bus=pci.7,addr=0x0 -device virtio-balloon-pci,id=balloon0,bus=pci.5,addr=0x0 -object {"qom-type":"rng-random","id":"objrng0","filename":"/dev/urandom"} -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pci.6,addr=0x0 -device vmcoreinfo -sandbox on,obsolete=deny,elevateprivileges=deny,spawn=deny,resourcecontrol=deny -msg timestamp=on""" # noqa,pylint: disable=line-too-long

SUNBEAM_SNAP_LIST = """
Name Version Rev Tracking Publisher Notes
bare 1.0 5 latest/stable canonical** base
core20 20240911 2434 latest/stable canonical** base
core22 20241119 1722 latest/stable canonical** base
core24 20240920 609 latest/stable canonical** base
juju 3.5.5 29058 3.5/stable canonical** -
lxd 5.0.4-497fe1e 31333 5.0/stable/… canonical** -
microceph 18.2.4+snapa97ae91192 1234 reef/edge canonical** held
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need a followup issue to track these "edges" and convert them to stable at some point?

Copy link
Member Author

@dosaboy dosaboy Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sunbeam 2024.1 is not released yet and so has no 2024/1/stable channel. I think adding a scenario to detect usage of the edge snap makes sense but we need wait until a stable snap is available.

microk8s v1.28.15 7366 1.28-strict/stable canonical** -
openstack 2024.1 614 2024.1/edge canonical** disabled,held
openstack 2024.1 576 2024.1/edge canonical** held
openstack-hypervisor 2024.1 184 2024.1/edge canonical** held
snapd 2.66.1 23258 latest/stable canonical** snapd
""" # noqa


class TestOpenstackBase(utils.BaseTestCase):
""" Custom base testcase that sets openstack plugin context. """
Expand Down Expand Up @@ -289,7 +305,7 @@ def test_get_release_not_eol(self, mock_date):

self.assertGreater(inst.days_to_eol, 0)

def test_project_catalog_package_exprs(self):
def test_project_catalog_apt_exprs(self):
c = openstack_core.openstack.OSTProjectCatalog()
core = ['ceilometer',
'octavia',
Expand Down Expand Up @@ -343,8 +359,23 @@ def test_project_catalog_package_exprs(self):
'dnsmasq',
'python3?-designate\\S*']

self.assertEqual(sorted(c.packages_core_exprs), sorted(core))
self.assertEqual(sorted(c.packages_dep_exprs), sorted(deps))
self.assertEqual(sorted(c.apt_core_exprs), sorted(core))
self.assertEqual(sorted(c.apt_dep_exprs), sorted(deps))

def test_project_catalog_snap_exprs(self):
c = openstack_core.openstack.OSTProjectCatalog()
core = [r'openstack\S*']
self.assertEqual(sorted(c.snap_core_exprs), sorted(core))

@utils.create_data_root({'sos_commands/snap/snap_list_--all':
SUNBEAM_SNAP_LIST})
def test_project_catalog_snap_packages(self):
ost_base = openstack_core.OpenstackBase()
core = {'openstack':
{'version': '2024.1', 'channel': '2024.1/edge'},
'openstack-hypervisor':
{'version': '2024.1', 'channel': '2024.1/edge'}}
self.assertEqual(ost_base.snaps.core, core)

def test_project_catalog_packages(self):
ost_base = openstack_core.OpenstackBase()
Expand Down
Loading
Loading