From 37a8ebdd792ed279fff14944570e09896c616e3e Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Thu, 10 Jun 2021 20:18:44 +0200 Subject: debconf: Add schema --- schemas/bundle.xsd | 14 ++++++++++++++ schemas/rules.xsd | 7 +++++++ schemas/types.xsd | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 59 insertions(+) diff --git a/schemas/bundle.xsd b/schemas/bundle.xsd index 4a11a1d1b..7d7a141b7 100644 --- a/schemas/bundle.xsd +++ b/schemas/bundle.xsd @@ -69,6 +69,13 @@ + + + + Abstract description of a Conf entry. + + + @@ -238,6 +245,13 @@ + + + + Fully bound description of a Conf entry. + + + diff --git a/schemas/rules.xsd b/schemas/rules.xsd index fb41ad9d4..7afc0f85e 100644 --- a/schemas/rules.xsd +++ b/schemas/rules.xsd @@ -122,6 +122,13 @@ + + + + Fully bound description of a Conf entry. + + + diff --git a/schemas/types.xsd b/schemas/types.xsd index 0a55f6355..fbd55547d 100644 --- a/schemas/types.xsd +++ b/schemas/types.xsd @@ -105,6 +105,12 @@ + + + + + + @@ -535,4 +541,36 @@ + + + + + The Conf tag allows you to set configurations options client + machines (f.e. debconf). + + + + + + Name of the configuration setting. + + + + + + + The value of the configuration setting. If this is not specified, + the setting will be reset to it's default value. + + + + + + + Driver to use on the client to manage this configuration. + + + + + -- cgit v1.2.3-1-g7c22 From 6ab8aaff8adefe8fe4b588fac16d3f1c1e0b0715 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Thu, 10 Jun 2021 20:55:41 +0200 Subject: debconf: Add client tool --- src/lib/Bcfg2/Client/Tools/Debconf.py | 102 ++++++++++++++++++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 src/lib/Bcfg2/Client/Tools/Debconf.py diff --git a/src/lib/Bcfg2/Client/Tools/Debconf.py b/src/lib/Bcfg2/Client/Tools/Debconf.py new file mode 100644 index 000000000..147d58b4b --- /dev/null +++ b/src/lib/Bcfg2/Client/Tools/Debconf.py @@ -0,0 +1,102 @@ +"""Debconf Support for Bcfg2""" + +import subprocess +import Bcfg2.Options +import Bcfg2.Client.Tools + + +class Debconf(Bcfg2.Client.Tools.Tool): + """Debconf Support for Bcfg2.""" + name = 'Debconf' + __execs__ = ['/usr/bin/debconf-communicate'] + __handles__ = [('Conf', 'debconf')] + __req__ = {'Conf': ['name']} + + def __init__(self, config): + Bcfg2.Client.Tools.Tool.__init__(self, config) + + #: This is the referrence to the Popen object of the + #: running debconf-communicate process. If this is None, + #: no process is runnning. + self.debconf = None + + def _start_debconf(self): + if self.debconf is None: + self.debconf = subprocess.Popen( + ['/usr/bin/debconf-communicate'], + stdin=subprocess.PIPE, stdout=subprocess.PIPE) + + def _stop_debconf(self): + if self.debconf is not None: + self.debconf.stdin.close() + self.debconf.stdout.close() + self.debconf = None + + def _debconf_reply(self, msg): + self.logger.debug('Debconf: %s' % msg.strip()) + self.debconf.stdin.write(msg) + line = self.debconf.stdout.readline().rstrip('\n') + self.logger.debug('< %s' % line) + reply = line.split(' ', 1) + + result = None + if len(reply) > 1: + result = reply[1] + return (reply[0] == '0', result) + + def debconf_get(self, key): + (success, value) = self._debconf_reply('GET %s\n' % key) + if not success: + return (False, None) + + (_, seen) = self._debconf_reply('FGET %s seen\n' % key) + return (seen, value) + + def debconf_set(self, key, value): + (success, _) = self._debconf_reply('SET %s %s\n' % (key, value)) + if success: + self._debconf_reply('FSET %s seen true\n' % key) + + return success + + def debconf_reset(self, key): + (success, _) = self._debconf_reply('RESET %s\n' % key) + return success + + def VerifyConf(self, entry, _modlist): + """ Verify the given Debconf entry. """ + (seen, value) = self.debconf_get(entry.get('name')) + if 'value' not in entry.attrib: + if seen == 'true': + return False + else: + if value != entry.get('value'): + return False + return True + + def InstallConf(self, entry): + """ Install the given Debconf entry. """ + if 'value' not in entry.attrib: + return self.debconf_reset(entry.get('name')) + + return self.debconf_set(entry.get('name'), entry.get('value')) + + def Inventory(self, structures=None): + try: + self._start_debconf() + result = Bcfg2.Client.Tools.Tool.Inventory(self, structures) + finally: + self._stop_debconf() + + return result + Inventory.__doc__ = Bcfg2.Client.Tools.Tool.Inventory.__doc__ + + def Install(self, entries): + try: + self._start_debconf() + result = Bcfg2.Client.Tools.Tool.Install(self, entries) + finally: + self._stop_debconf() + + return result + Install.__doc__ = Bcfg2.Client.Tools.Tool.Install.__doc__ -- cgit v1.2.3-1-g7c22 From 161953cb28f356e1aa38f5cf67234f28a19ceb26 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Fri, 18 Jun 2021 17:07:40 +0200 Subject: debconf: Find extra entries Extra debconf entries, are entries that were seen but that are not specified in the configuration. --- schemas/types.xsd | 3 +-- src/lib/Bcfg2/Client/Tools/Debconf.py | 33 ++++++++++++++++++++++----------- 2 files changed, 23 insertions(+), 13 deletions(-) diff --git a/schemas/types.xsd b/schemas/types.xsd index fbd55547d..fc2e7bdc4 100644 --- a/schemas/types.xsd +++ b/schemas/types.xsd @@ -559,8 +559,7 @@ - The value of the configuration setting. If this is not specified, - the setting will be reset to it's default value. + The value of the configuration setting. diff --git a/src/lib/Bcfg2/Client/Tools/Debconf.py b/src/lib/Bcfg2/Client/Tools/Debconf.py index 147d58b4b..4e8797b1c 100644 --- a/src/lib/Bcfg2/Client/Tools/Debconf.py +++ b/src/lib/Bcfg2/Client/Tools/Debconf.py @@ -8,7 +8,7 @@ import Bcfg2.Client.Tools class Debconf(Bcfg2.Client.Tools.Tool): """Debconf Support for Bcfg2.""" name = 'Debconf' - __execs__ = ['/usr/bin/debconf-communicate'] + __execs__ = ['/usr/bin/debconf-communicate', '/usr/bin/debconf-show'] __handles__ = [('Conf', 'debconf')] __req__ = {'Conf': ['name']} @@ -65,20 +65,13 @@ class Debconf(Bcfg2.Client.Tools.Tool): def VerifyConf(self, entry, _modlist): """ Verify the given Debconf entry. """ - (seen, value) = self.debconf_get(entry.get('name')) - if 'value' not in entry.attrib: - if seen == 'true': - return False - else: - if value != entry.get('value'): - return False + (_, value) = self.debconf_get(entry.get('name')) + if value != entry.get('value'): + return False return True def InstallConf(self, entry): """ Install the given Debconf entry. """ - if 'value' not in entry.attrib: - return self.debconf_reset(entry.get('name')) - return self.debconf_set(entry.get('name'), entry.get('value')) def Inventory(self, structures=None): @@ -100,3 +93,21 @@ class Debconf(Bcfg2.Client.Tools.Tool): return result Install.__doc__ = Bcfg2.Client.Tools.Tool.Install.__doc__ + + def FindExtra(self): + specified = [entry.get('name') + for entry in self.getSupportedEntries()] + extra = set() + listowners = self.cmd.run(['/usr/bin/debconf-show', '--listowners']) + if listowners.success: + owners = listowners.stdout.splitlines() + + values = self.cmd.run(['/usr/bin/debconf-show'] + owners) + for line in values.stdout.splitlines(): + if len(line) > 2 and line[0] == '*': + (name, value) = line[2:].split(':', 2) + if name not in specified: + extra.add(name) + return [Bcfg2.Client.XML.Element('Conf', name=name, type='debconf') + for name in list(extra)] + FindExtra.__doc__ = Bcfg2.Client.Tools.Tool.FindExtra.__doc__ -- cgit v1.2.3-1-g7c22 From 528eae28bb320e4aa5747adcdd8953c308872ef4 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Sun, 16 Jan 2022 02:06:14 +0100 Subject: debconf: Only start communicate process on demand --- src/lib/Bcfg2/Client/Tools/Debconf.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/Debconf.py b/src/lib/Bcfg2/Client/Tools/Debconf.py index 4e8797b1c..76d745610 100644 --- a/src/lib/Bcfg2/Client/Tools/Debconf.py +++ b/src/lib/Bcfg2/Client/Tools/Debconf.py @@ -33,6 +33,9 @@ class Debconf(Bcfg2.Client.Tools.Tool): self.debconf = None def _debconf_reply(self, msg): + if self.debconf is None: + self._start_debconf() + self.logger.debug('Debconf: %s' % msg.strip()) self.debconf.stdin.write(msg) line = self.debconf.stdout.readline().rstrip('\n') @@ -76,7 +79,6 @@ class Debconf(Bcfg2.Client.Tools.Tool): def Inventory(self, structures=None): try: - self._start_debconf() result = Bcfg2.Client.Tools.Tool.Inventory(self, structures) finally: self._stop_debconf() @@ -86,7 +88,6 @@ class Debconf(Bcfg2.Client.Tools.Tool): def Install(self, entries): try: - self._start_debconf() result = Bcfg2.Client.Tools.Tool.Install(self, entries) finally: self._stop_debconf() -- cgit v1.2.3-1-g7c22 From e474ff29aae4990c77dd47500ede0ddee248b748 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Sun, 16 Jan 2022 02:34:33 +0100 Subject: debconf: Add ConfEntry to Reporting --- src/lib/Bcfg2/Reporting/Storage/DjangoORM.py | 9 +- .../Reporting/migrations/0009_add_conf_entry.py | 35 +++ src/lib/Bcfg2/Reporting/models.py | 32 +- .../south_migrations/0009_add_conf_entry.py | 321 +++++++++++++++++++++ .../Reporting/templates/config_items/item.html | 7 +- src/lib/Bcfg2/Server/Admin.py | 1 + 6 files changed, 400 insertions(+), 5 deletions(-) create mode 100644 src/lib/Bcfg2/Reporting/migrations/0009_add_conf_entry.py create mode 100644 src/lib/Bcfg2/Reporting/south_migrations/0009_add_conf_entry.py diff --git a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py index af1b0c341..493cbdfdf 100644 --- a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py +++ b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py @@ -30,7 +30,7 @@ def load_django_models(): FailureEntry, Performance, BaseEntry, ServiceEntry, ActionEntry, \ POSIXGroupEntry, POSIXUserEntry, SEBooleanEntry, SEFcontextEntry, \ SEInterfaceEntry, SELoginEntry, SEModuleEntry, SENodeEntry, \ - SEPermissiveEntry, SEPortEntry, SEUserEntry + SEPermissiveEntry, SEPortEntry, SEUserEntry, ConfEntry # pylint: enable=W0602 from Bcfg2.Reporting.models import \ @@ -39,7 +39,7 @@ def load_django_models(): FailureEntry, Performance, BaseEntry, ServiceEntry, ActionEntry, \ POSIXGroupEntry, POSIXUserEntry, SEBooleanEntry, SEFcontextEntry, \ SEInterfaceEntry, SELoginEntry, SEModuleEntry, SENodeEntry, \ - SEPermissiveEntry, SEPortEntry, SEUserEntry + SEPermissiveEntry, SEPortEntry, SEUserEntry, ConfEntry def get_all_field_names(model): @@ -127,6 +127,11 @@ class DjangoORM(StorageBase): defaults=dict(status='check', rc=-1), mapping=dict(output="rc")) + def _import_Conf(self, entry, state): + return self._import_default(entry, state, + defaults=dict(), + mapping=dict()) + def _import_Package(self, entry, state): name = entry.get('name') exists = entry.get('current_exists', default="true").lower() == "true" diff --git a/src/lib/Bcfg2/Reporting/migrations/0009_add_conf_entry.py b/src/lib/Bcfg2/Reporting/migrations/0009_add_conf_entry.py new file mode 100644 index 000000000..527a7bbe9 --- /dev/null +++ b/src/lib/Bcfg2/Reporting/migrations/0009_add_conf_entry.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('Reporting', '0008_add_ready_flag_interaction'), + ] + + operations = [ + migrations.CreateModel( + name='ConfEntry', + fields=[ + ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), + ('name', models.CharField(max_length=128, db_index=True)), + ('hash_key', models.BigIntegerField(editable=False, db_index=True)), + ('state', models.IntegerField(choices=[(0, b'Good'), (1, b'Bad'), (2, b'Modified'), (3, b'Extra')])), + ('exists', models.BooleanField(default=True)), + ('value', models.TextField(null=True)), + ('current_value', models.TextField(null=True)), + ], + options={ + 'ordering': ('state', 'name'), + 'abstract': False, + }, + ), + migrations.AddField( + model_name='interaction', + name='confs', + field=models.ManyToManyField(to='Reporting.ConfEntry'), + ), + ] diff --git a/src/lib/Bcfg2/Reporting/models.py b/src/lib/Bcfg2/Reporting/models.py index 6ba7d3765..fe465672d 100644 --- a/src/lib/Bcfg2/Reporting/models.py +++ b/src/lib/Bcfg2/Reporting/models.py @@ -153,6 +153,7 @@ class Interaction(models.Model): only_important = models.BooleanField(default=False) actions = models.ManyToManyField("ActionEntry") + confs = models.ManyToManyField("ConfEntry") packages = models.ManyToManyField("PackageEntry") paths = models.ManyToManyField("PathEntry") services = models.ManyToManyField("ServiceEntry") @@ -174,7 +175,7 @@ class Interaction(models.Model): 'seports', 'sefcontexts', 'senodes', 'selogins', 'seusers', 'seinterfaces', 'sepermissives', 'semodules', 'posixusers', - 'posixgroups') + 'posixgroups', 'confs') # Formerly InteractionMetadata profile = models.ForeignKey("Group", related_name="+", null=True) @@ -486,6 +487,33 @@ class ActionEntry(SuccessEntry): ENTRY_TYPE = r"Action" +class ConfEntry(SuccessEntry): + """ Conf entry """ + value = models.TextField(null=True) + current_value = models.TextField(null=True) + + ENTRY_TYPE = r"Conf" + + def conf_problem(self): + """Check for a conf problem.""" + if not self.current_value: + return True + return self.value != self.current_value + + def short_list(self): + """Return a list of problems""" + rv = super(ConfEntry, self).short_list() + if self.is_extra(): + return rv + if not self.conf_problem() or not self.exists: + return rv + if not self.current_value: + rv.append("Missing") + else: + rv.append("Wrong value") + return rv + + class SEBooleanEntry(SuccessEntry): """ SELinux boolean """ value = models.BooleanField(default=True) @@ -797,7 +825,7 @@ class ServiceEntry(SuccessEntry): return rv -ENTRY_TYPES = (ActionEntry, PackageEntry, PathEntry, ServiceEntry, +ENTRY_TYPES = (ActionEntry, ConfEntry, PackageEntry, PathEntry, ServiceEntry, SEBooleanEntry, SEPortEntry, SEFcontextEntry, SENodeEntry, SELoginEntry, SEUserEntry, SEInterfaceEntry, SEPermissiveEntry, SEModuleEntry) diff --git a/src/lib/Bcfg2/Reporting/south_migrations/0009_add_conf_entry.py b/src/lib/Bcfg2/Reporting/south_migrations/0009_add_conf_entry.py new file mode 100644 index 000000000..cbbad4d59 --- /dev/null +++ b/src/lib/Bcfg2/Reporting/south_migrations/0009_add_conf_entry.py @@ -0,0 +1,321 @@ +# -*- coding: utf-8 -*- +from south.utils import datetime_utils as datetime +from south.db import db +from south.v2 import SchemaMigration +from django.db import models + + +class Migration(SchemaMigration): + + def forwards(self, orm): + # Adding model 'ConfEntry' + db.create_table(u'Reporting_confentry', ( + (u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), + ('name', self.gf('django.db.models.fields.CharField')(max_length=128, db_index=True)), + ('hash_key', self.gf('django.db.models.fields.BigIntegerField')(db_index=True)), + ('state', self.gf('django.db.models.fields.IntegerField')()), + ('exists', self.gf('django.db.models.fields.BooleanField')(default=True)), + ('value', self.gf('django.db.models.fields.TextField')(null=True)), + ('current_value', self.gf('django.db.models.fields.TextField')(null=True)), + )) + db.send_create_signal(u'Reporting', ['ConfEntry']) + + # Adding M2M table for field confs on 'Interaction' + m2m_table_name = db.shorten_name(u'Reporting_interaction_confs') + db.create_table(m2m_table_name, ( + ('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)), + ('interaction', models.ForeignKey(orm[u'Reporting.interaction'], null=False)), + ('confentry', models.ForeignKey(orm[u'Reporting.confentry'], null=False)) + )) + db.create_unique(m2m_table_name, ['interaction_id', 'confentry_id']) + + + def backwards(self, orm): + # Deleting model 'ConfEntry' + db.delete_table(u'Reporting_confentry') + + # Removing M2M table for field confs on 'Interaction' + db.delete_table(db.shorten_name(u'Reporting_interaction_confs')) + + + models = { + u'Reporting.actionentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ActionEntry'}, + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'output': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'state': ('django.db.models.fields.IntegerField', [], {}), + 'status': ('django.db.models.fields.CharField', [], {'default': "'check'", 'max_length': '128'}) + }, + u'Reporting.bundle': { + 'Meta': {'ordering': "('name',)", 'object_name': 'Bundle'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}) + }, + u'Reporting.client': { + 'Meta': {'object_name': 'Client'}, + 'creation': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}), + 'current_interaction': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'parent_client'", 'null': 'True', 'to': u"orm['Reporting.Interaction']"}), + 'expiration': ('django.db.models.fields.DateTimeField', [], {'null': 'True', 'blank': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + u'Reporting.confentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ConfEntry'}, + 'current_value': ('django.db.models.fields.TextField', [], {'null': 'True'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.IntegerField', [], {}), + 'value': ('django.db.models.fields.TextField', [], {'null': 'True'}) + }, + u'Reporting.deviceentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'DeviceEntry', '_ormbases': [u'Reporting.PathEntry']}, + 'current_major': ('django.db.models.fields.IntegerField', [], {}), + 'current_minor': ('django.db.models.fields.IntegerField', [], {}), + 'device_type': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + u'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}), + 'target_major': ('django.db.models.fields.IntegerField', [], {}), + 'target_minor': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.failureentry': { + 'Meta': {'object_name': 'FailureEntry'}, + 'entry_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'message': ('django.db.models.fields.TextField', [], {}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + u'Reporting.fileacl': { + 'Meta': {'object_name': 'FileAcl'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}) + }, + u'Reporting.fileperms': { + 'Meta': {'unique_together': "(('owner', 'group', 'mode'),)", 'object_name': 'FilePerms'}, + 'group': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'mode': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'owner': ('django.db.models.fields.CharField', [], {'max_length': '128'}) + }, + u'Reporting.group': { + 'Meta': {'ordering': "('name',)", 'object_name': 'Group'}, + 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.Bundle']", 'symmetrical': 'False'}), + 'category': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), + 'comment': ('django.db.models.fields.TextField', [], {'blank': 'True'}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.Group']", 'symmetrical': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}), + 'profile': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'public': ('django.db.models.fields.BooleanField', [], {'default': 'False'}) + }, + u'Reporting.interaction': { + 'Meta': {'ordering': "['-timestamp']", 'unique_together': "(('client', 'timestamp'),)", 'object_name': 'Interaction'}, + 'actions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.ActionEntry']", 'symmetrical': 'False'}), + 'bad_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'bundles': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.Bundle']", 'symmetrical': 'False'}), + 'client': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'interactions'", 'to': u"orm['Reporting.Client']"}), + 'confs': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.ConfEntry']", 'symmetrical': 'False'}), + 'dry_run': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'extra_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'failures': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.FailureEntry']", 'symmetrical': 'False'}), + 'good_count': ('django.db.models.fields.IntegerField', [], {}), + 'groups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.Group']", 'symmetrical': 'False'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'modified_count': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'only_important': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'packages': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.PackageEntry']", 'symmetrical': 'False'}), + 'paths': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.PathEntry']", 'symmetrical': 'False'}), + 'posixgroups': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.POSIXGroupEntry']", 'symmetrical': 'False'}), + 'posixusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.POSIXUserEntry']", 'symmetrical': 'False'}), + 'profile': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'null': 'True', 'to': u"orm['Reporting.Group']"}), + 'ready': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'repo_rev_code': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'sebooleans': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.SEBooleanEntry']", 'symmetrical': 'False'}), + 'sefcontexts': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.SEFcontextEntry']", 'symmetrical': 'False'}), + 'seinterfaces': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.SEInterfaceEntry']", 'symmetrical': 'False'}), + 'selogins': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.SELoginEntry']", 'symmetrical': 'False'}), + 'semodules': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.SEModuleEntry']", 'symmetrical': 'False'}), + 'senodes': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.SENodeEntry']", 'symmetrical': 'False'}), + 'sepermissives': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.SEPermissiveEntry']", 'symmetrical': 'False'}), + 'seports': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.SEPortEntry']", 'symmetrical': 'False'}), + 'server': ('django.db.models.fields.CharField', [], {'max_length': '256'}), + 'services': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.ServiceEntry']", 'symmetrical': 'False'}), + 'seusers': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.SEUserEntry']", 'symmetrical': 'False'}), + 'state': ('django.db.models.fields.CharField', [], {'max_length': '32'}), + 'timestamp': ('django.db.models.fields.DateTimeField', [], {'db_index': 'True'}), + 'total_count': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.linkentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'LinkEntry', '_ormbases': [u'Reporting.PathEntry']}, + 'current_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}), + u'pathentry_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': u"orm['Reporting.PathEntry']", 'unique': 'True', 'primary_key': 'True'}), + 'target_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}) + }, + u'Reporting.packageentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PackageEntry'}, + 'current_version': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.IntegerField', [], {}), + 'target_version': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}), + 'verification_details': ('django.db.models.fields.TextField', [], {'default': "''"}) + }, + u'Reporting.pathentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'PathEntry'}, + 'acls': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['Reporting.FileAcl']", 'symmetrical': 'False'}), + 'current_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': u"orm['Reporting.FilePerms']"}), + 'detail_type': ('django.db.models.fields.IntegerField', [], {'default': '0'}), + 'details': ('django.db.models.fields.TextField', [], {'default': "''"}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'path_type': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'state': ('django.db.models.fields.IntegerField', [], {}), + 'target_perms': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'+'", 'to': u"orm['Reporting.FilePerms']"}) + }, + u'Reporting.performance': { + 'Meta': {'object_name': 'Performance'}, + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'interaction': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'performance_items'", 'to': u"orm['Reporting.Interaction']"}), + 'metric': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'value': ('django.db.models.fields.DecimalField', [], {'max_digits': '32', 'decimal_places': '16'}) + }, + u'Reporting.posixgroupentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'POSIXGroupEntry'}, + 'current_gid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'gid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.posixuserentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'POSIXUserEntry'}, + 'current_gecos': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'current_group': ('django.db.models.fields.CharField', [], {'max_length': '64', 'null': 'True'}), + 'current_home': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'current_shell': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'null': 'True'}), + 'current_uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'gecos': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + 'group': ('django.db.models.fields.CharField', [], {'max_length': '64'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + 'home': ('django.db.models.fields.CharField', [], {'max_length': '1024'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'shell': ('django.db.models.fields.CharField', [], {'default': "'/bin/bash'", 'max_length': '1024'}), + 'state': ('django.db.models.fields.IntegerField', [], {}), + 'uid': ('django.db.models.fields.IntegerField', [], {'null': 'True'}) + }, + u'Reporting.sebooleanentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEBooleanEntry'}, + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.IntegerField', [], {}), + 'value': ('django.db.models.fields.BooleanField', [], {'default': 'True'}) + }, + u'Reporting.sefcontextentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEFcontextEntry'}, + 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'filetype': ('django.db.models.fields.CharField', [], {'max_length': '16'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'state': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.seinterfaceentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEInterfaceEntry'}, + 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'state': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.seloginentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SELoginEntry'}, + 'current_selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'selinuxuser': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'state': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.semoduleentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEModuleEntry'}, + 'current_disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'disabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.senodeentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SENodeEntry'}, + 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'proto': ('django.db.models.fields.CharField', [], {'max_length': '4'}), + 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'state': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.sepermissiveentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPermissiveEntry'}, + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.seportentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEPortEntry'}, + 'current_selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'selinuxtype': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'state': ('django.db.models.fields.IntegerField', [], {}) + }, + u'Reporting.serviceentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'ServiceEntry'}, + 'current_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'state': ('django.db.models.fields.IntegerField', [], {}), + 'target_status': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '128'}) + }, + u'Reporting.seuserentry': { + 'Meta': {'ordering': "('state', 'name')", 'object_name': 'SEUserEntry'}, + 'current_prefix': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'current_roles': ('django.db.models.fields.CharField', [], {'max_length': '128', 'null': 'True'}), + 'exists': ('django.db.models.fields.BooleanField', [], {'default': 'True'}), + 'hash_key': ('django.db.models.fields.BigIntegerField', [], {'db_index': 'True'}), + u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), + 'name': ('django.db.models.fields.CharField', [], {'max_length': '128', 'db_index': 'True'}), + 'prefix': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'roles': ('django.db.models.fields.CharField', [], {'max_length': '128'}), + 'state': ('django.db.models.fields.IntegerField', [], {}) + } + } + + complete_apps = ['Reporting'] diff --git a/src/lib/Bcfg2/Reporting/templates/config_items/item.html b/src/lib/Bcfg2/Reporting/templates/config_items/item.html index 91c368bd7..9a103bc72 100644 --- a/src/lib/Bcfg2/Reporting/templates/config_items/item.html +++ b/src/lib/Bcfg2/Reporting/templates/config_items/item.html @@ -45,7 +45,7 @@ div.entry_list h3 { {% endif %} {# Really need a better test here #} -{% if item.mode_problem or item.status_problem or item.linkentry.link_problem or item.version_problem %} +{% if item.mode_problem or item.status_problem or item.linkentry.link_problem or item.version_problem or item.conf_problem %} @@ -86,6 +86,11 @@ div.entry_list h3 { {% endif %} + {% if item.conf_problem %} + + + + {% endif %}
Problem TypeExpectedFound
{{item.selinuxtype}} {{item.current_selinuxtype}}
Conf Value{{item.value|default:""}}{{item.current_value|default:""}}
{% endif %} diff --git a/src/lib/Bcfg2/Server/Admin.py b/src/lib/Bcfg2/Server/Admin.py index 77bca88eb..0accd0361 100644 --- a/src/lib/Bcfg2/Server/Admin.py +++ b/src/lib/Bcfg2/Server/Admin.py @@ -884,6 +884,7 @@ class _ReportsCmd(AdminCmd): # pylint: disable=W0223 Bcfg2.Reporting.models.Bundle, Bcfg2.Reporting.models.FailureEntry, Bcfg2.Reporting.models.ActionEntry, + Bcfg2.Reporting.models.ConfEntry, Bcfg2.Reporting.models.PathEntry, Bcfg2.Reporting.models.PackageEntry, Bcfg2.Reporting.models.PathEntry, -- cgit v1.2.3-1-g7c22 From aac1945e6a4a2a94736e2a2ff7a761121cb65a19 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Sun, 16 Jan 2022 02:35:00 +0100 Subject: debconf: Save current_value for reporting --- src/lib/Bcfg2/Client/Tools/Debconf.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/Debconf.py b/src/lib/Bcfg2/Client/Tools/Debconf.py index 76d745610..0d286028b 100644 --- a/src/lib/Bcfg2/Client/Tools/Debconf.py +++ b/src/lib/Bcfg2/Client/Tools/Debconf.py @@ -68,10 +68,9 @@ class Debconf(Bcfg2.Client.Tools.Tool): def VerifyConf(self, entry, _modlist): """ Verify the given Debconf entry. """ - (_, value) = self.debconf_get(entry.get('name')) - if value != entry.get('value'): - return False - return True + (_, current_value) = self.debconf_get(entry.get('name')) + entry.set('current_value', current_value) + return current_value == entry.get('value') def InstallConf(self, entry): """ Install the given Debconf entry. """ @@ -98,7 +97,7 @@ class Debconf(Bcfg2.Client.Tools.Tool): def FindExtra(self): specified = [entry.get('name') for entry in self.getSupportedEntries()] - extra = set() + extra = dict() listowners = self.cmd.run(['/usr/bin/debconf-show', '--listowners']) if listowners.success: owners = listowners.stdout.splitlines() @@ -106,9 +105,10 @@ class Debconf(Bcfg2.Client.Tools.Tool): values = self.cmd.run(['/usr/bin/debconf-show'] + owners) for line in values.stdout.splitlines(): if len(line) > 2 and line[0] == '*': - (name, value) = line[2:].split(':', 2) - if name not in specified: - extra.add(name) - return [Bcfg2.Client.XML.Element('Conf', name=name, type='debconf') - for name in list(extra)] + (name, current_value) = line[2:].split(':', 2) + if name not in specified and name not in extra: + extra[name] = Bcfg2.Client.XML.Element( + 'Conf', name=name, type='debconf', + current_value=current_value[1:]) + return extra.values() FindExtra.__doc__ = Bcfg2.Client.Tools.Tool.FindExtra.__doc__ -- cgit v1.2.3-1-g7c22 From 49cf4482ea952de6bcf3c023c017309cd7926c15 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Sat, 29 Jan 2022 19:59:03 +0100 Subject: debconf: Add ability to ignore conf settings --- schemas/types.xsd | 9 +++++++++ src/lib/Bcfg2/Client/Tools/Debconf.py | 3 +++ 2 files changed, 12 insertions(+) diff --git a/schemas/types.xsd b/schemas/types.xsd index fc2e7bdc4..dc57c5cc8 100644 --- a/schemas/types.xsd +++ b/schemas/types.xsd @@ -570,6 +570,15 @@
+ + + + If you set this to "true" the configuration setting will be ignored + and not updated. This is usefull to remove a setting from the list of + extra entries. + + + diff --git a/src/lib/Bcfg2/Client/Tools/Debconf.py b/src/lib/Bcfg2/Client/Tools/Debconf.py index 0d286028b..706f7a8dc 100644 --- a/src/lib/Bcfg2/Client/Tools/Debconf.py +++ b/src/lib/Bcfg2/Client/Tools/Debconf.py @@ -68,6 +68,9 @@ class Debconf(Bcfg2.Client.Tools.Tool): def VerifyConf(self, entry, _modlist): """ Verify the given Debconf entry. """ + if entry.get('ignore', 'false').lower() == 'true': + return True + (_, current_value) = self.debconf_get(entry.get('name')) entry.set('current_value', current_value) return current_value == entry.get('value') -- cgit v1.2.3-1-g7c22 From a4bd3e3b55cc842b50075e9cae912f7dc9e9cfa2 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Sun, 30 Jan 2022 05:00:21 +0100 Subject: debconf: Support removing of conf entries --- src/lib/Bcfg2/Client/Tools/Debconf.py | 12 +++++++++++- src/lib/Bcfg2/Client/__init__.py | 5 ++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/Debconf.py b/src/lib/Bcfg2/Client/Tools/Debconf.py index 706f7a8dc..2dc88e748 100644 --- a/src/lib/Bcfg2/Client/Tools/Debconf.py +++ b/src/lib/Bcfg2/Client/Tools/Debconf.py @@ -96,7 +96,17 @@ class Debconf(Bcfg2.Client.Tools.Tool): return result Install.__doc__ = Bcfg2.Client.Tools.Tool.Install.__doc__ - + + def Remove(self, entries): + try: + for entry in entries: + self.debconf_reset(entry.get('name')) + self.modified += entry + finally: + self._stop_debconf() + self.extra = self.FindExtra() + Remove.__doc__ = Bcfg2.Client.Tools.Tool.Remove.__doc__ + def FindExtra(self): specified = [entry.get('name') for entry in self.getSupportedEntries()] diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py index 157cc7f65..a7e0dade5 100644 --- a/src/lib/Bcfg2/Client/__init__.py +++ b/src/lib/Bcfg2/Client/__init__.py @@ -110,7 +110,7 @@ class Client(object): help='Only verify the given bundle(s)'), Bcfg2.Options.Option( '-r', '--remove', - choices=['all', 'services', 'packages', 'users'], + choices=['all', 'services', 'packages', 'users', 'conf'], help='Force removal of additional configuration items')), Bcfg2.Options.ExclusiveOptionGroup( Bcfg2.Options.PathOption( @@ -640,6 +640,9 @@ class Client(object): elif Bcfg2.Options.setup.remove == 'users': self.removal = [entry for entry in self.extra if entry.tag in ['POSIXUser', 'POSIXGroup']] + elif Bcfg2.Options.setup.remove == 'conf': + self.removal = [entry for entry in self.extra + if entry.tag == 'Conf'] candidates = [entry for entry in self.states if not self.states[entry]] -- cgit v1.2.3-1-g7c22 From c11f4f790d710721dcc99f87cfcbafb49e9a4715 Mon Sep 17 00:00:00 2001 From: Alexander Sulfrian Date: Sun, 30 Jan 2022 04:54:49 +0100 Subject: debconf: Verify seen value --- src/lib/Bcfg2/Client/Tools/Debconf.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/lib/Bcfg2/Client/Tools/Debconf.py b/src/lib/Bcfg2/Client/Tools/Debconf.py index 2dc88e748..784f7e9bc 100644 --- a/src/lib/Bcfg2/Client/Tools/Debconf.py +++ b/src/lib/Bcfg2/Client/Tools/Debconf.py @@ -53,7 +53,7 @@ class Debconf(Bcfg2.Client.Tools.Tool): return (False, None) (_, seen) = self._debconf_reply('FGET %s seen\n' % key) - return (seen, value) + return (seen == 'true', value) def debconf_set(self, key, value): (success, _) = self._debconf_reply('SET %s %s\n' % (key, value)) @@ -71,9 +71,12 @@ class Debconf(Bcfg2.Client.Tools.Tool): if entry.get('ignore', 'false').lower() == 'true': return True - (_, current_value) = self.debconf_get(entry.get('name')) + (seen, current_value) = self.debconf_get(entry.get('name')) + if not seen: + current_value = '%s (not seen)' % current_value entry.set('current_value', current_value) - return current_value == entry.get('value') + + return seen and current_value == entry.get('value') def InstallConf(self, entry): """ Install the given Debconf entry. """ -- cgit v1.2.3-1-g7c22