rougail/src/rougail/annotator/service.py

321 lines
14 KiB
Python
Raw Normal View History

2021-01-10 21:10:19 +01:00
"""Annotate services
2021-01-30 08:15:26 +01:00
Created by:
EOLE (http://eole.orion.education.fr)
Copyright (C) 2005-2018
Forked by:
Cadoles (http://www.cadoles.com)
Copyright (C) 2019-2021
distribued with GPL-2 or later license
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
2021-01-10 21:10:19 +01:00
"""
2020-12-24 07:39:51 +01:00
from os.path import basename
2021-01-10 21:10:19 +01:00
from typing import Tuple
2020-12-24 07:39:51 +01:00
2021-04-12 15:04:38 +02:00
from rougail.i18n import _
2021-05-14 07:00:55 +02:00
from rougail.utils import normalize_family
2021-04-12 15:04:38 +02:00
from rougail.error import DictConsistencyError
2021-02-22 19:28:51 +01:00
# a object's attribute has some annotations
2020-12-24 07:39:51 +01:00
# that shall not be present in the exported (flatened) XML
2021-05-13 22:30:58 +02:00
ERASED_ATTRIBUTES = ('redefine', 'namespace', 'xmlfiles', 'disabled', 'name', 'manage')
2021-12-04 22:07:51 +01:00
ERASED_ATTRIBUTES2 = ('redefine', 'namespace', 'xmlfiles', 'disabled')
2021-05-13 22:30:58 +02:00
ALLOW_ATTRIBUT_NOT_MANAGE = ['file', 'engine', 'target']
2020-12-24 07:39:51 +01:00
2021-04-12 15:04:38 +02:00
class Annotator:
2020-12-24 07:39:51 +01:00
"""Manage service's object
for example::
<services>
<service name="test">
<service_access service='ntp'>
</service_access>
</service>
</services>
"""
2021-04-12 15:04:38 +02:00
level = 20
def __init__(self,
objectspace,
*args,
) -> None:
2020-12-24 07:39:51 +01:00
self.objectspace = objectspace
self.uniq_overrides = []
2021-02-20 10:41:53 +01:00
if 'network_type' not in self.objectspace.types:
self.objectspace.types['network_type'] = self.objectspace.types['ip_type']
2021-01-10 21:10:19 +01:00
if hasattr(self.objectspace.space, 'services'):
if not hasattr(self.objectspace.space.services, 'service'):
del self.objectspace.space.services
else:
self.convert_services()
2020-12-24 07:39:51 +01:00
def convert_services(self):
2021-01-10 21:10:19 +01:00
"""convert services to variables
"""
2020-12-24 07:39:51 +01:00
self.objectspace.space.services.hidden = True
self.objectspace.space.services.name = 'services'
self.objectspace.space.services.doc = 'services'
2021-01-18 17:46:21 +01:00
self.objectspace.space.services.path = 'services'
2021-01-23 11:57:46 +01:00
for service_name, service in self.objectspace.space.services.service.items():
2021-05-14 07:00:55 +02:00
service.name = normalize_family(service_name)
2021-02-21 19:48:30 +01:00
activate_obj = self._generate_element('boolean',
None,
None,
'activate',
2021-04-29 18:20:05 +02:00
not service.disabled,
2021-02-21 19:48:30 +01:00
service,
2021-05-14 07:00:55 +02:00
'.'.join(['services', service.name, 'activate']),
2021-02-21 19:48:30 +01:00
)
2021-04-29 18:20:05 +02:00
service.disabled = None
2021-01-23 11:57:46 +01:00
for elttype, values in dict(vars(service)).items():
2021-02-21 19:48:30 +01:00
if elttype == 'servicelist':
self.objectspace.list_conditions.setdefault('servicelist',
{}).setdefault(
values,
[]).append(activate_obj)
continue
2021-05-13 22:30:58 +02:00
if elttype in ERASED_ATTRIBUTES:
2020-12-24 07:39:51 +01:00
continue
2021-02-19 12:31:12 +01:00
if not service.manage and elttype not in ALLOW_ATTRIBUT_NOT_MANAGE:
msg = _(f'unmanage service cannot have "{elttype}"')
raise DictConsistencyError(msg, 66, service.xmlfiles)
2021-05-13 22:30:58 +02:00
if isinstance(values, (dict, list)):
if elttype != 'ip':
eltname = elttype + 's'
else:
eltname = elttype
2021-05-14 07:00:55 +02:00
path = '.'.join(['services', service.name, eltname])
2021-05-13 22:30:58 +02:00
family = self._gen_family(eltname,
path,
service.xmlfiles,
with_informations=False,
)
if isinstance(values, dict):
values = list(values.values())
family.family = self.make_group_from_elts(service_name,
elttype,
values,
path,
)
setattr(service, elttype, family)
2021-02-19 12:31:12 +01:00
else:
2021-05-13 22:30:58 +02:00
if not hasattr(service, 'information'):
service.information = self.objectspace.information(service.xmlfiles)
setattr(service.information, elttype, values)
2022-01-19 18:24:00 +01:00
service.path = '.'.join(['services', service.name])
2021-02-21 19:48:30 +01:00
manage = self._generate_element('boolean',
None,
None,
'manage',
service.manage,
service,
2021-05-14 07:00:55 +02:00
'.'.join(['services', service.name, 'manage']),
2021-02-21 19:48:30 +01:00
)
service.variable = [activate_obj, manage]
2021-05-14 07:00:55 +02:00
service.doc = service_name
2020-12-24 07:39:51 +01:00
def make_group_from_elts(self,
service_name,
2021-01-10 21:10:19 +01:00
elttype,
2020-12-24 07:39:51 +01:00
elts,
path,
):
"""Splits each objects into a group (and `OptionDescription`, in tiramisu terms)
and build elements and its attributes (the `Options` in tiramisu terms)
"""
families = []
2021-01-17 17:15:58 +01:00
listname = '{}list'.format(elttype)
2021-01-10 21:10:19 +01:00
for elt in elts:
2020-12-24 07:39:51 +01:00
# try to launch _update_xxxx() function
2021-01-10 21:10:19 +01:00
update_elt = '_update_' + elttype
2020-12-24 07:39:51 +01:00
if hasattr(self, update_elt):
getattr(self, update_elt)(elt,
service_name,
)
2021-01-10 21:10:19 +01:00
c_name, subpath = self._get_name_path(elt,
path,
)
family = self._gen_family(c_name,
subpath,
elt.xmlfiles,
)
2020-12-24 07:39:51 +01:00
family.variable = []
2021-12-04 22:07:51 +01:00
if hasattr(elt, 'disabled'):
disabled = elt.disabled
else:
disabled = False
2021-01-17 17:15:58 +01:00
activate_obj = self._generate_element('boolean',
None,
None,
2021-01-17 17:15:58 +01:00
'activate',
2021-12-04 22:07:51 +01:00
not disabled,
elt,
2021-01-17 17:15:58 +01:00
'.'.join([subpath, 'activate']),
)
2020-12-24 07:39:51 +01:00
for key in dir(elt):
2021-05-13 22:30:58 +02:00
if key.startswith('_') or key.endswith('_type') or key in ERASED_ATTRIBUTES2:
2020-12-24 07:39:51 +01:00
continue
value = getattr(elt, key)
if key == listname:
self.objectspace.list_conditions.setdefault(listname,
{}).setdefault(
value,
2021-01-17 17:15:58 +01:00
[]).append(activate_obj)
2020-12-24 07:39:51 +01:00
continue
if key == 'name':
dtd_key_type = elttype + '_type'
else:
dtd_key_type = key + '_type'
elt_type = getattr(elt, dtd_key_type, None)
if elt_type:
if elt_type == 'variable':
elt_type = 'symlink'
family.variable.append(self._generate_element(elt_type,
dtd_key_type,
elttype,
key,
value,
elt,
f'{subpath}.{key}'
))
else:
setattr(family.information, key, value)
2021-01-17 17:15:58 +01:00
family.variable.append(activate_obj)
2020-12-24 07:39:51 +01:00
families.append(family)
return families
2021-01-10 21:10:19 +01:00
def _get_name_path(self,
elt,
path: str,
) -> Tuple[str, str]:
# create element name, if already exists, add _xx to be uniq
2021-02-20 10:41:53 +01:00
if hasattr(elt, 'source') and elt.source:
2021-01-10 21:10:19 +01:00
name = elt.source
else:
name = elt.name
idx = 0
while True:
c_name = name
if idx:
c_name += f'_{idx}'
2021-01-18 17:46:21 +01:00
subpath = '{}.{}'.format(path, normalize_family(c_name))
2021-01-17 17:15:58 +01:00
try:
self.objectspace.paths.get_family(subpath, 'services')
except DictConsistencyError as err:
if err.errno == 42:
return c_name, subpath
2021-01-10 21:10:19 +01:00
idx += 1
def _gen_family(self,
name,
path,
xmlfiles,
with_informations=True,
2021-01-10 21:10:19 +01:00
):
family = self.objectspace.family(xmlfiles)
family.name = normalize_family(name)
family.doc = name
family.mode = None
self.objectspace.paths.add_family('services',
path,
family,
2021-02-14 10:10:48 +01:00
None,
2021-01-10 21:10:19 +01:00
)
if with_informations:
family.information = self.objectspace.information(xmlfiles)
2021-01-10 21:10:19 +01:00
return family
2020-12-24 07:39:51 +01:00
def _generate_element(self,
2021-01-10 21:10:19 +01:00
type_,
dtd_key_type,
elttype,
2020-12-24 07:39:51 +01:00
key,
value,
elt,
2020-12-24 07:39:51 +01:00
path,
2021-01-19 19:28:29 +01:00
): # pylint: disable=R0913
variable = self.objectspace.variable(elt.xmlfiles)
2020-12-24 07:39:51 +01:00
variable.name = normalize_family(key)
variable.mode = None
variable.type = type_
if type_ == 'symlink':
variable.opt = self.objectspace.paths.get_variable(value, xmlfiles=elt.xmlfiles)
2020-12-24 07:39:51 +01:00
variable.multi = None
2021-02-19 10:50:28 +01:00
needed_type = self.objectspace.types[dtd_key_type]
2021-02-20 16:07:38 +01:00
if needed_type not in ('variable', variable.opt.type):
2021-02-19 10:50:28 +01:00
msg = _(f'"{value}" in "{elttype}" must be a variable with type '
f'"{needed_type}" not "{variable.opt.type}"')
raise DictConsistencyError(msg, 58, elt.xmlfiles)
2020-12-24 07:39:51 +01:00
else:
variable.doc = key
2021-01-23 21:15:26 +01:00
variable.default = value
2021-01-11 22:34:16 +01:00
variable.namespace = 'services'
2020-12-24 07:39:51 +01:00
self.objectspace.paths.add_variable('services',
path,
'service',
False,
variable,
)
return variable
def _update_override(self,
override,
2020-12-24 07:39:51 +01:00
service_name,
):
if service_name in self.uniq_overrides:
2021-02-20 16:07:38 +01:00
msg = _('only one override is allowed by service, '
'please use a variable multiple if you want have more than one IP')
raise DictConsistencyError(msg, 69, override.xmlfiles)
self.uniq_overrides.append(service_name)
override.name = service_name
if not hasattr(override, 'source'):
override.source = service_name
2020-12-24 07:39:51 +01:00
2021-02-18 17:00:12 +01:00
@staticmethod
def _update_file(file_,
2020-12-24 07:39:51 +01:00
service_name,
):
2021-02-17 09:52:17 +01:00
if not hasattr(file_, 'file_type') or file_.file_type == "filename":
2020-12-24 07:39:51 +01:00
if not hasattr(file_, 'source'):
file_.source = basename(file_.name)
elif not hasattr(file_, 'source'):
2021-01-10 21:10:19 +01:00
msg = _(f'attribute "source" is mandatory for the file "{file_.name}" '
f'"({service_name})"')
raise DictConsistencyError(msg, 34, file_.xmlfiles)
2021-02-19 10:50:28 +01:00
def _update_ip(self,
ip,
service_name,
) -> None:
variable = self.objectspace.paths.get_variable(ip.name, ip.xmlfiles)
2021-02-20 16:07:38 +01:00
if variable.type not in ['ip', 'network', 'network_cidr']:
msg = _(f'ip cannot be linked to "{variable.type}" variable "{ip.name}"')
2021-02-20 16:07:38 +01:00
raise DictConsistencyError(msg, 70, ip.xmlfiles)
2021-02-19 10:50:28 +01:00
if variable.type in ['ip', 'network_cidr'] and hasattr(ip, 'netmask'):
msg = _(f'ip with ip_type "{variable.type}" must not have netmask')
raise DictConsistencyError(msg, 59, ip.xmlfiles)
if variable.type == 'network' and not hasattr(ip, 'netmask'):
msg = _(f'ip with ip_type "{variable.type}" must have netmask')
raise DictConsistencyError(msg, 64, ip.xmlfiles)
if hasattr(ip, 'netmask'):
netmask = self.objectspace.paths.get_variable(ip.netmask, ip.xmlfiles)
2021-02-19 10:50:28 +01:00
if netmask.type != 'netmask':
msg = _(f'netmask in ip must have type "netmask", not "{netmask.type}"')
raise DictConsistencyError(msg, 65, ip.xmlfiles)