Add support for systemd

Systemd support is in corosync, but currently the add_init_service
handles adding an upstart service only. This results in Charmed
Kubernetes having incorrectly monitored services.

Change-Id: I935613292ce6b78cf645469fda6d21b0aa695c28
Closes-Bug: #1843933
This commit is contained in:
Mike Wilson 2019-09-13 14:10:35 -04:00
parent 10af229842
commit e1d44c258a
4 changed files with 176 additions and 11 deletions

View File

@ -53,6 +53,7 @@ class CRM(dict):
self['clones'] = {}
self['locations'] = {}
self['init_services'] = []
self['systemd_services'] = []
super(CRM, self).__init__(*args, **kwargs)
def primitive(self, name, agent, description=None, **kwargs):
@ -363,6 +364,23 @@ class CRM(dict):
"""
self['init_services'] = resources
def systemd_services(self, *resources):
"""Specifies that the service(s) is a systemd service.
Services (resources) which are noted as systemd services are
disabled, stopped, and left to pacemaker to manage the resource.
Parameters
----------
resources: str or list of str, varargs
The resources which should be noted as systemd services.
Returns
-------
None
"""
self['systemd_services'] = resources
def ms(self, name, resource, description=None, **kwargs):
"""Create a master/slave resource type.
@ -669,3 +687,34 @@ class DNSEntry(ResourceDescriptor):
if self.ip:
res_params = '{} ip_address="{}"'.format(res_params, self.ip)
crm.primitive(res_key, res_type, params=res_params)
class SystemdService(ResourceDescriptor):
def __init__(self, service_name, systemd_service_name, clone=True):
"""Class for managing systemd resource
:param service_name: string - Name of service
:param systemd_service_name: string - Name service uses in
systemd system
:param clone: bool - clone service across all units
:returns: None
"""
self.service_name = service_name
self.systemd_service_name = systemd_service_name
self.clone = clone
def configure_resource(self, crm):
""""Configure new systemd system service resource in crm
:param crm: CRM() instance - Config object for Pacemaker resources
:returns: None
"""
res_key = 'res_{}_{}'.format(
self.service_name.replace('-', '_'),
self.systemd_service_name.replace('-', '_'))
res_type = 'systemd:{}'.format(self.systemd_service_name)
crm.primitive(res_key, res_type, op='monitor interval="5s"')
crm.systemd_services(self.systemd_service_name)
if self.clone:
clone_key = 'cl_{}'.format(res_key)
crm.clone(clone_key, res_key)

View File

@ -199,6 +199,33 @@ class HAClusterRequires(RelationBase):
service.replace('-', '_'))
self.delete_resource(res_key)
def add_systemd_service(self, name, service, clone=True):
"""Add a SystemdService object to self.resources
:param name: string - Name of service
:param service: string - Name service uses in systemd
:returns: None
"""
resource_dict = self.get_local('resources')
if resource_dict:
resources = relations.hacluster.common.CRM(**resource_dict)
else:
resources = relations.hacluster.common.CRM()
resources.add(
relations.hacluster.common.SystemdService(name, service, clone))
self.set_local(resources=resources)
def remove_systemd_service(self, name, service):
"""Remove a systemd service
:param name: string - Name of service
:param service: string - Name of service used in systemd
"""
res_key = 'res_{}_{}'.format(
name.replace('-', '_'),
service.replace('-', '_'))
self.delete_resource(res_key)
def add_dnsha(self, name, ip, fqdn, endpoint_type):
"""Add a DNS entry to self.resources

View File

@ -30,7 +30,8 @@ class TestHAClusterCommonCRM(unittest.TestCase):
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
'init_services': [],
'systemd_services': []}
self.assertEqual(
crm,
@ -259,6 +260,20 @@ class TestHAClusterCommonCRM(unittest.TestCase):
crm['ms']['disk1'],
'drbd1 description="useful desc"')
def test_systemd_services(self):
crm = common.CRM()
crm.systemd_services('haproxy')
self.assertEqual(
crm['systemd_services'],
('haproxy',))
def test_systemd_services_multi(self):
crm = common.CRM()
crm.systemd_services('haproxy', 'apache2')
self.assertEqual(
crm['systemd_services'],
('haproxy', 'apache2'))
# The method signature of 'order' seems broken. Leaving out unit tests for
# it as they would just confirm broken behaviour.
@ -384,3 +399,48 @@ class TestHAClusterCommonDNSEntry(unittest.TestCase):
self.assertEqual(
crm['resource_params']['res_keystone_admin_hostname'],
' params fqdn="keystone.admin" ip_address="10.110.1.1"')
class TestHAClusterCommonSystemdService(unittest.TestCase):
def test_systemd(self):
systemd_svc = common.SystemdService('apache', 'apache2')
self.assertEqual(
systemd_svc.service_name,
'apache')
self.assertEqual(
systemd_svc.systemd_service_name,
'apache2')
self.assertTrue(systemd_svc.clone)
def test_systemd_no_clone(self):
systemd_svc = common.SystemdService('apache', 'apache2', clone=False)
self.assertFalse(systemd_svc.clone)
def test_configure_resource(self):
crm = common.CRM()
systemd_svc = common.SystemdService('apache', 'apache2')
systemd_svc.configure_resource(crm)
self.assertEqual(
crm['resources']['res_apache_apache2'],
'systemd:apache2')
self.assertEqual(
crm['resource_params']['res_apache_apache2'],
(' op monitor interval="5s"'))
self.assertEqual(crm['systemd_services'], ('apache2',))
self.assertEqual(
crm['clones']['cl_res_apache_apache2'],
'res_apache_apache2')
def test_configure_resource_no_clone(self):
crm = common.CRM()
systemd_svc = common.SystemdService('apache', 'apache2', clone=False)
systemd_svc.configure_resource(crm)
self.assertEqual(
crm['resources']['res_apache_apache2'],
'systemd:apache2')
self.assertEqual(
crm['resource_params']['res_apache_apache2'],
(' op monitor interval="5s"'))
self.assertEqual(crm['systemd_services'], ('apache2',))
self.assertFalse(crm['clones'].get('cl_res_apache_apache2'))

View File

@ -246,7 +246,8 @@ class TestHAClusterRequires(unittest.TestCase):
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
'init_services': [],
'systemd_services': []}
self.mock_reactive_db(existing_data)
self.cr.delete_resource('res_mysql_ens3_vip')
self.assertEqual(
@ -275,7 +276,8 @@ class TestHAClusterRequires(unittest.TestCase):
'colocations': {},
'clones': {},
'locations': {},
'init_services': ('telnetd',)}
'init_services': ('telnetd',),
'systemd_services': []}
self.mock_reactive_db(existing_data)
self.cr.delete_resource('res_mysql_ens3_vip')
self.cr.delete_resource('res_mysql_ens4_vip')
@ -309,7 +311,8 @@ class TestHAClusterRequires(unittest.TestCase):
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
'init_services': [],
'systemd_services': []}
self.mock_reactive_db()
self.cr.add_vip('mysql', '10.110.5.43')
@ -330,7 +333,8 @@ class TestHAClusterRequires(unittest.TestCase):
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
'init_services': [],
'systemd_services': []}
expected = {
'resources': {
@ -352,7 +356,8 @@ class TestHAClusterRequires(unittest.TestCase):
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
'init_services': [],
'systemd_services': []}
self.mock_reactive_db(existing_resource)
self.cr.add_vip('mysql', '10.120.5.43')
@ -371,7 +376,8 @@ class TestHAClusterRequires(unittest.TestCase):
'colocations': {},
'clones': {'cl_res_mysql_telnetd': 'res_mysql_telnetd'},
'locations': {},
'init_services': ('telnetd',)}
'init_services': ('telnetd',),
'systemd_services': []}
self.mock_reactive_db()
self.cr.add_init_service('mysql', 'telnetd')
self.set_local.assert_called_once_with(resources=expected)
@ -391,7 +397,8 @@ class TestHAClusterRequires(unittest.TestCase):
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
'init_services': [],
'systemd_services': []}
self.mock_reactive_db()
self.cr.add_dnsha(
@ -416,7 +423,8 @@ class TestHAClusterRequires(unittest.TestCase):
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
'init_services': [],
'systemd_services': []}
expected = {
'resources': {
'res_keystone_public_hostname': 'ocf:maas:dns',
@ -437,7 +445,8 @@ class TestHAClusterRequires(unittest.TestCase):
'colocations': {},
'clones': {},
'locations': {},
'init_services': []}
'init_services': [],
'systemd_services': []}
self.mock_reactive_db(existing_resource)
self.cr.add_dnsha(
@ -462,7 +471,8 @@ class TestHAClusterRequires(unittest.TestCase):
'app2/0': {
'key1': 'value1',
'key2': 'value3'}},
'rid:3': {}}
'rid:3': {},
'systemd_services': []}
def get_unit_data(key, unit, relation_id):
return unit_data[relation_id].get(unit, {}).get(key, {})
@ -489,3 +499,22 @@ class TestHAClusterRequires(unittest.TestCase):
self.assertEqual(
self.cr.get_remote_all('key100', default='defaultvalue'),
['defaultvalue'])
def test_add_systemd_service(self):
expected = {
'resources': {
'res_mysql_telnetd': 'systemd:telnetd'},
'delete_resources': [],
'resource_params': {
'res_mysql_telnetd': ' op monitor interval="5s"'},
'groups': {},
'ms': {},
'orders': {},
'colocations': {},
'clones': {'cl_res_mysql_telnetd': 'res_mysql_telnetd'},
'locations': {},
'init_services': [],
'systemd_services': ('telnetd',)}
self.mock_reactive_db()
self.cr.add_systemd_service('mysql', 'telnetd')
self.set_local.assert_called_once_with(resources=expected)