summaryrefslogtreecommitdiffstats
path: root/src/lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib')
-rw-r--r--src/lib/Bcfg2/Client/Frame.py31
-rw-r--r--src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py1
-rw-r--r--src/lib/Bcfg2/Client/Tools/SELinux.py2
-rw-r--r--src/lib/Bcfg2/Client/Tools/YUM.py15
-rw-r--r--src/lib/Bcfg2/Client/Tools/__init__.py104
-rw-r--r--src/lib/Bcfg2/Client/__init__.py28
-rw-r--r--src/lib/Bcfg2/Compat.py7
-rw-r--r--src/lib/Bcfg2/Reporting/Storage/DjangoORM.py6
-rw-r--r--src/lib/Bcfg2/Reporting/models.py8
-rw-r--r--src/lib/Bcfg2/Reporting/templates/base.html2
-rw-r--r--src/lib/Bcfg2/Reporting/templates/clients/index.html5
-rw-r--r--src/lib/Bcfg2/Reporting/views.py7
-rw-r--r--src/lib/Bcfg2/Server/Core.py40
-rwxr-xr-xsrc/lib/Bcfg2/Server/Encryption.py6
-rw-r--r--src/lib/Bcfg2/Server/Lint/Comments.py2
-rwxr-xr-xsrc/lib/Bcfg2/Server/Lint/Genshi.py48
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py1
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py9
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Defaults.py29
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Git.py48
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Collection.py2
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Packages/Yum.py93
-rw-r--r--src/lib/Bcfg2/Server/Plugins/SSLCA.py5
-rw-r--r--src/lib/Bcfg2/Server/Plugins/TemplateHelper.py18
-rw-r--r--src/lib/Bcfg2/Server/Reports/updatefix.py5
-rw-r--r--src/lib/Bcfg2/settings.py2
-rw-r--r--src/lib/Bcfg2/version.py2
29 files changed, 325 insertions, 212 deletions
diff --git a/src/lib/Bcfg2/Client/Frame.py b/src/lib/Bcfg2/Client/Frame.py
index f230aacb7..baf8a14f2 100644
--- a/src/lib/Bcfg2/Client/Frame.py
+++ b/src/lib/Bcfg2/Client/Frame.py
@@ -1,14 +1,12 @@
""" Frame is the Client Framework that verifies and installs entries,
and generates statistics. """
-import os
-import sys
import time
-import select
import fnmatch
import logging
import Bcfg2.Client.Tools
-from Bcfg2.Compat import input, any, all # pylint: disable=W0622
+from Bcfg2.Client import prompt
+from Bcfg2.Compat import any, all # pylint: disable=W0622
def cmpent(ent1, ent2):
@@ -154,7 +152,7 @@ class Frame(object):
for entry in multi:
self.logger.debug(entry)
- def promptFilter(self, prompt, entries):
+ def promptFilter(self, msg, entries):
"""Filter a supplied list based on user input."""
ret = []
entries.sort(cmpent)
@@ -165,20 +163,9 @@ class Frame(object):
if 'qtext' in entry.attrib:
iprompt = entry.get('qtext')
else:
- iprompt = prompt % (entry.tag, entry.get('name'))
- # flush input buffer
- while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0:
- os.read(sys.stdin.fileno(), 4096)
- try:
- ans = input(iprompt.encode(sys.stdout.encoding, 'replace'))
- if ans in ['y', 'Y']:
- ret.append(entry)
- except EOFError:
- # python 2.4.3 on CentOS doesn't like ^C for some reason
- break
- except:
- print("Error while reading input")
- continue
+ iprompt = msg % (entry.tag, entry.get('name'))
+ if prompt(iprompt):
+ ret.append(entry)
return ret
def __getattr__(self, name):
@@ -281,7 +268,7 @@ class Frame(object):
def Decide(self): # pylint: disable=R0912
"""Set self.whitelist based on user interaction."""
- prompt = "Install %s: %s? (y/N): "
+ iprompt = "Install %s: %s? (y/N): "
rprompt = "Remove %s: %s? (y/N): "
if self.setup['remove']:
if self.setup['remove'] == 'all':
@@ -353,7 +340,7 @@ class Frame(object):
(bmodified or a.get('when') == 'always'))]
# now we process all "always actions"
if self.setup['interactive']:
- self.promptFilter(prompt, actions)
+ self.promptFilter(iprompt, actions)
self.DispatchInstallCalls(actions)
if bundle.tag != 'Bundle':
@@ -379,7 +366,7 @@ class Frame(object):
if b.get("name")))
if self.setup['interactive']:
- self.whitelist = self.promptFilter(prompt, self.whitelist)
+ self.whitelist = self.promptFilter(iprompt, self.whitelist)
self.removal = self.promptFilter(rprompt, self.removal)
for entry in candidates:
diff --git a/src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py b/src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py
index 896ca5f49..64a0b1e15 100644
--- a/src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py
+++ b/src/lib/Bcfg2/Client/Tools/POSIX/Hardlink.py
@@ -12,5 +12,4 @@ class POSIXHardlink(POSIXLinkTool):
return os.path.samefile(entry.get('name'), entry.get('to'))
def _link(self, entry):
- ## TODO: set permissions
return os.link(entry.get('to'), entry.get('name'))
diff --git a/src/lib/Bcfg2/Client/Tools/SELinux.py b/src/lib/Bcfg2/Client/Tools/SELinux.py
index 08d943251..451495be2 100644
--- a/src/lib/Bcfg2/Client/Tools/SELinux.py
+++ b/src/lib/Bcfg2/Client/Tools/SELinux.py
@@ -360,7 +360,7 @@ class SELinuxEntryHandler(object):
""" find extra entries of this entry type """
specified = [self._key(e)
for e in self.tool.getSupportedEntries()
- if e.get("type") == self.etype]
+ if e.tag == "SE%s" % self.etype.title()]
try:
records = self.custom_records
except ValueError:
diff --git a/src/lib/Bcfg2/Client/Tools/YUM.py b/src/lib/Bcfg2/Client/Tools/YUM.py
index a0dfe6dd9..4539a6a36 100644
--- a/src/lib/Bcfg2/Client/Tools/YUM.py
+++ b/src/lib/Bcfg2/Client/Tools/YUM.py
@@ -603,17 +603,18 @@ class YUM(Bcfg2.Client.Tools.PkgTool):
if stat['verify'] != {}:
stat['verify_fail'] = True
package_fail = True
- self.logger.debug("It is suggested that you either manage "
- "these files, revert the changes, or ignore "
- "false failures:")
- self.logger.debug(" Verify Problems:")
+ self.logger.info("It is suggested that you either manage "
+ "these files, revert the changes, or ignore "
+ "false failures:")
+ self.logger.info(" Verify Problems: %s" %
+ stat['pkg'].get('name'))
for fname, probs in list(stat['verify'].items()):
if len(probs) > 1:
- self.logger.debug(" %s" % fname)
+ self.logger.info(" %s" % fname)
for prob in probs:
- self.logger.debug(" %s" % prob)
+ self.logger.info(" %s" % prob[1])
else:
- self.logger.debug(" %s: %s" % (fname, probs[0]))
+ self.logger.info(" %s: %s" % (fname, probs[0]))
if len(all_pkg_objs) > 0:
# Is this an install only package? We just look at the first one
diff --git a/src/lib/Bcfg2/Client/Tools/__init__.py b/src/lib/Bcfg2/Client/Tools/__init__.py
index cd86a2a4b..41759655d 100644
--- a/src/lib/Bcfg2/Client/Tools/__init__.py
+++ b/src/lib/Bcfg2/Client/Tools/__init__.py
@@ -3,10 +3,10 @@
import os
import sys
import stat
-import select
+import Bcfg2.Client
import Bcfg2.Client.XML
from Bcfg2.Utils import Executor, ClassName
-from Bcfg2.Compat import input, walk_packages # pylint: disable=W0622
+from Bcfg2.Compat import walk_packages # pylint: disable=W0622
__all__ = [m[1] for m in walk_packages(path=__path__)]
@@ -113,25 +113,34 @@ class Tool(object):
#: A list of all entries handled by this tool
self.handled = []
- for struct in config:
+ self._analyze_config()
+ self._check_execs()
+
+ def _analyze_config(self):
+ """ Analyze the config at tool initialization-time for
+ important and handled entries """
+ for struct in self.config:
for entry in struct:
if (entry.tag == 'Path' and
entry.get('important', 'false').lower() == 'true'):
self.__important__.append(entry.get('name'))
- if self.handlesEntry(entry):
- self.handled.append(entry)
+ self.handled = self.getSupportedEntries()
+
+ def _check_execs(self):
+ """ Check all executables used by this tool to ensure that
+ they exist and are executable """
for filename in self.__execs__:
try:
mode = stat.S_IMODE(os.stat(filename)[stat.ST_MODE])
- if mode & stat.S_IEXEC != stat.S_IEXEC:
- raise ToolInstantiationError("%s: %s not executable" %
- (self.name, filename))
except OSError:
raise ToolInstantiationError(sys.exc_info()[1])
except:
raise ToolInstantiationError("%s: Failed to stat %s" %
- (self.name, filename),
- exc_info=1)
+ (self.name, filename))
+ if not mode & stat.S_IEXEC:
+ raise ToolInstantiationError("%s: %s not executable" %
+ (self.name, filename))
+
def BundleUpdated(self, bundle, states): # pylint: disable=W0613
""" Callback that is invoked when a bundle has been updated.
@@ -185,11 +194,13 @@ class Tool(object):
if self.canVerify(entry):
try:
func = getattr(self, "Verify%s" % entry.tag)
- states[entry] = func(entry, mods)
except AttributeError:
self.logger.error("%s: Cannot verify %s entries" %
(self.name, entry.tag))
- except:
+ continue
+ try:
+ states[entry] = func(entry, mods)
+ except: # pylint: disable=W0702
self.logger.error("%s: Unexpected failure verifying %s"
% (self.name,
self.primarykey(entry)),
@@ -213,14 +224,16 @@ class Tool(object):
:returns: None """
for entry in entries:
try:
- func = getattr(self, "Install%s" % (entry.tag))
- states[entry] = func(entry)
- if states[entry]:
- self.modified.append(entry)
+ func = getattr(self, "Install%s" % entry.tag)
except AttributeError:
self.logger.error("%s: Cannot install %s entries" %
(self.name, entry.tag))
- except:
+ continue
+ try:
+ states[entry] = func(entry)
+ if states[entry]:
+ self.modified.append(entry)
+ except: # pylint: disable=W0702
self.logger.error("%s: Unexpected failure installing %s" %
(self.name, self.primarykey(entry)),
exc_info=1)
@@ -409,6 +422,19 @@ class PkgTool(Tool):
"""
raise NotImplementedError
+ def _get_package_command(self, packages):
+ """ Get the command to install the given list of packages.
+
+ :param packages: The Package entries to install
+ :type packages: list of lxml.etree._Element
+ :returns: string - the command to run
+ """
+ pkgargs = " ".join(self.pkgtool[1][0] %
+ tuple(pkg.get(field)
+ for field in self.pkgtool[1][1])
+ for pkg in packages)
+ return self.pkgtool[0] % pkgargs
+
def Install(self, packages, states):
""" Run a one-pass install where all required packages are
installed with a single command, followed by single package
@@ -422,11 +448,6 @@ class PkgTool(Tool):
self.logger.info("Trying single pass package install for pkgtype %s" %
self.pkgtype)
- data = [tuple([pkg.get(field) for field in self.pkgtool[1][1]])
- for pkg in packages]
- pkgargs = " ".join([self.pkgtool[1][0] % datum for datum in data])
-
- self.logger.debug("Installing packages: %s" % pkgargs)
if self.cmd.run(self.pkgtool[0] % pkgargs):
self.logger.info("Single Pass Succeded")
# set all package states to true and flush workqueues
@@ -436,7 +457,7 @@ class PkgTool(Tool):
and entry.get('type') == self.pkgtype
and entry.get('name') in pkgnames):
self.logger.debug('Setting state to true for pkg %s' %
- (entry.get('name')))
+ entry.get('name'))
states[entry] = True
self.RefreshPackages()
else:
@@ -452,18 +473,13 @@ class PkgTool(Tool):
else:
self.logger.info("Installing pkg %s version %s" %
(pkg.get('name'), pkg.get('version')))
- if self.cmd.run(
- self.pkgtool[0] %
- (self.pkgtool[1][0] %
- tuple([pkg.get(field)
- for field in self.pkgtool[1][1]]))):
+ if self.cmd.run(self._get_package_command([pkg])):
states[pkg] = True
else:
self.logger.error("Failed to install package %s" %
- (pkg.get('name')))
+ pkg.get('name'))
self.RefreshPackages()
- for entry in [ent for ent in packages if states[ent]]:
- self.modified.append(entry)
+ self.modified.extend(entry for entry in packages if states[entry])
def RefreshPackages(self):
""" Refresh the internal representation of the package
@@ -557,11 +573,13 @@ class SvcTool(Tool):
if self.setup['servicemode'] == 'disabled':
return
- for entry in [ent for ent in bundle if self.handlesEntry(ent)]:
- restart = entry.get("restart", "true")
- if (restart.lower() == "false" or
- (restart.lower() == "interactive" and
- not self.setup['interactive'])):
+ for entry in bundle:
+ if not self.handlesEntry(entry):
+ continue
+
+ restart = entry.get("restart", "true").lower()
+ if (restart == "false" or
+ (restart == "interactive" and not self.setup['interactive'])):
continue
success = False
@@ -570,14 +588,8 @@ class SvcTool(Tool):
success = self.stop_service(entry)
elif entry.get('name') not in self.restarted:
if self.setup['interactive']:
- prompt = ('Restart service %s?: (y/N): ' %
- entry.get('name'))
- # flush input buffer
- while len(select.select([sys.stdin.fileno()], [], [],
- 0.0)[0]) > 0:
- os.read(sys.stdin.fileno(), 4096)
- ans = input(prompt)
- if ans not in ['y', 'Y']:
+ if not Bcfg2.Client.prompt('Restart service %s? (y/N) '
+ % entry.get('name')):
continue
success = self.restart_service(entry)
if success:
@@ -593,8 +605,8 @@ class SvcTool(Tool):
install_entries = []
for entry in entries:
if entry.get('install', 'true').lower() == 'false':
- self.logger.info("Service %s installation is false. Skipping "
- "installation." % (entry.get('name')))
+ self.logger.info("Installation is false for %s:%s, skipping" %
+ (entry.tag, entry.get('name')))
else:
install_entries.append(entry)
return Tool.Install(self, install_entries, states)
diff --git a/src/lib/Bcfg2/Client/__init__.py b/src/lib/Bcfg2/Client/__init__.py
index c03021f14..dd5ae1e83 100644
--- a/src/lib/Bcfg2/Client/__init__.py
+++ b/src/lib/Bcfg2/Client/__init__.py
@@ -1,3 +1,31 @@
"""This contains all Bcfg2 Client modules"""
__all__ = ["Frame", "Tools", "XML", "Client"]
+
+import os
+import sys
+import select
+from Bcfg2.Compat import input # pylint: disable=W0622
+
+
+def prompt(msg):
+ """ Helper to give a yes/no prompt to the user. Flushes input
+ buffers, handles exceptions, etc. Returns True if the user
+ answers in the affirmative, False otherwise.
+
+ :param msg: The message to show to the user. The message is not
+ altered in any way for display; i.e., it should
+ contain "[y/N]" if desired, etc.
+ :type msg: string
+ :returns: bool - True if yes, False if no """
+ while len(select.select([sys.stdin.fileno()], [], [], 0.0)[0]) > 0:
+ os.read(sys.stdin.fileno(), 4096)
+ try:
+ ans = input(msg.encode(sys.stdout.encoding, 'replace'))
+ return ans in ['y', 'Y']
+ except EOFError:
+ # python 2.4.3 on CentOS doesn't like ^C for some reason
+ return False
+ except:
+ print("Error while reading input: %s" % sys.exc_info()[1])
+ return False
diff --git a/src/lib/Bcfg2/Compat.py b/src/lib/Bcfg2/Compat.py
index b0f0ef5cf..beb534791 100644
--- a/src/lib/Bcfg2/Compat.py
+++ b/src/lib/Bcfg2/Compat.py
@@ -260,3 +260,10 @@ def oct_mode(mode):
:type mode: int
:returns: string """
return oct(mode).replace('o', '')
+
+
+try:
+ long = long
+except NameError:
+ # longs are just ints in py3k
+ long = int
diff --git a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
index fb7af7465..bca4a9c1e 100644
--- a/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
+++ b/src/lib/Bcfg2/Reporting/Storage/DjangoORM.py
@@ -224,7 +224,11 @@ class DjangoORM(StorageBase):
inter.extra_count = counter_fields[TYPE_EXTRA]
inter.save()
for entry_type in updates.keys():
- getattr(inter, entry_type).add(*updates[entry_type])
+ # batch this for sqlite
+ i = 0
+ while(i < len(updates[entry_type])):
+ getattr(inter, entry_type).add(*updates[entry_type][i:i+100])
+ i += 100
# performance metrics
for times in stats.findall('OpStamps'):
diff --git a/src/lib/Bcfg2/Reporting/models.py b/src/lib/Bcfg2/Reporting/models.py
index c7850f4af..ab2dc8418 100644
--- a/src/lib/Bcfg2/Reporting/models.py
+++ b/src/lib/Bcfg2/Reporting/models.py
@@ -392,7 +392,13 @@ class BaseEntry(models.Model):
@classmethod
def prune_orphans(cls):
'''Remove unused entries'''
- cls.objects.filter(interaction__isnull=True).delete()
+ # yeat another sqlite hack
+ cls_orphans = [x['id'] \
+ for x in cls.objects.filter(interaction__isnull=True).values("id")]
+ i = 0
+ while i < len(cls_orphans):
+ cls.objects.filter(id__in=cls_orphans[i:i+100]).delete()
+ i += 100
class SuccessEntry(BaseEntry):
diff --git a/src/lib/Bcfg2/Reporting/templates/base.html b/src/lib/Bcfg2/Reporting/templates/base.html
index 1ec6b8c24..533dcc79e 100644
--- a/src/lib/Bcfg2/Reporting/templates/base.html
+++ b/src/lib/Bcfg2/Reporting/templates/base.html
@@ -88,7 +88,7 @@
<div style='clear:both'></div>
</div><!-- document -->
<div id="footer">
- <span>Bcfg2 Version 1.3.0rc1</span>
+ <span>Bcfg2 Version 1.3.0rc2</span>
</div>
<div id="calendar_div" style='position:absolute; visibility:hidden; background-color:white; layer-background-color:white;'></div>
diff --git a/src/lib/Bcfg2/Reporting/templates/clients/index.html b/src/lib/Bcfg2/Reporting/templates/clients/index.html
index 45ba20b86..d9c415c20 100644
--- a/src/lib/Bcfg2/Reporting/templates/clients/index.html
+++ b/src/lib/Bcfg2/Reporting/templates/clients/index.html
@@ -30,6 +30,9 @@
{% endif %}
{% endfor %}
</table>
-{% else %}<p>No client records are available.</p>
+{% else %}
+ <div class='client_list_box'>
+ <p>No client records are available.</p>
+ </div>
{% endif %}
{% endblock %}
diff --git a/src/lib/Bcfg2/Reporting/views.py b/src/lib/Bcfg2/Reporting/views.py
index 8ab3f8e59..0341a18af 100644
--- a/src/lib/Bcfg2/Reporting/views.py
+++ b/src/lib/Bcfg2/Reporting/views.py
@@ -213,8 +213,11 @@ def entry_status(request, entry_type, pk, timestamp=None, **kwargs):
# There is no good way to do this...
items = []
- for it in cls.objects.filter(interaction__in=current_clients, name=item.name).distinct("id").select_related():
- items.append((it, it.interaction_set.filter(pk__in=current_clients).order_by('client__name').select_related('client')))
+ seen = []
+ for it in cls.objects.filter(interaction__in=current_clients, name=item.name).select_related():
+ if it.pk not in seen:
+ items.append((it, it.interaction_set.filter(pk__in=current_clients).order_by('client__name').select_related('client')))
+ seen.append(it.pk)
return render_to_response('config_items/entry_status.html',
{'entry': item,
diff --git a/src/lib/Bcfg2/Server/Core.py b/src/lib/Bcfg2/Server/Core.py
index f93ca0a7b..1b0ecadf5 100644
--- a/src/lib/Bcfg2/Server/Core.py
+++ b/src/lib/Bcfg2/Server/Core.py
@@ -332,9 +332,23 @@ class BaseCore(object):
self.fam.handle_event_set(self.lock)
except:
continue
- # VCS plugin periodic updates
- for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.Version):
- self.revision = plugin.get_revision()
+ self._update_vcs_revision()
+
+ @track_statistics()
+ def _update_vcs_revision(self):
+ """ Update the revision of the current configuration on-disk
+ from the VCS plugin """
+ for plugin in self.plugins_by_type(Bcfg2.Server.Plugin.Version):
+ try:
+ newrev = plugin.get_revision()
+ if newrev != self.revision:
+ self.logger.debug("Updated to revision %s" % newrev)
+ self.revision = newrev
+ break
+ except:
+ self.logger.warning("Error getting revision from %s: %s" %
+ (plugin.name, sys.exc_info()[1]))
+ self.revision = '-1'
def init_plugin(self, plugin):
""" Import and instantiate a single plugin. The plugin is
@@ -360,8 +374,8 @@ class BaseCore(object):
try:
plug = getattr(mod, plugin.split('.')[-1])
except AttributeError:
- self.logger.error("Failed to load plugin %s (AttributeError)" %
- plugin)
+ self.logger.error("Failed to load plugin %s: %s" %
+ (plugin, sys.exc_info()[1]))
return
# Blacklist conflicting plugins
cplugs = [conflict for conflict in plug.conflicts
@@ -536,18 +550,16 @@ class BaseCore(object):
continue
try:
self.Bind(entry, metadata)
- except PluginExecutionError:
- exc = sys.exc_info()[1]
- if 'failure' not in entry.attrib:
- entry.set('failure', 'bind error: %s' % exc)
- self.logger.error("Failed to bind entry %s:%s: %s" %
- (entry.tag, entry.get('name'), exc))
- except Exception:
+ except:
exc = sys.exc_info()[1]
if 'failure' not in entry.attrib:
entry.set('failure', 'bind error: %s' % exc)
- self.logger.error("Unexpected failure in BindStructure: %s %s"
- % (entry.tag, entry.get('name')), exc_info=1)
+ if isinstance(exc, PluginExecutionError):
+ msg = "Failed to bind entry"
+ else:
+ msg = "Unexpected failure binding entry"
+ self.logger.error("%s %s:%s: %s" %
+ (msg, entry.tag, entry.get('name'), exc))
def Bind(self, entry, metadata):
""" Bind a single entry using the appropriate generator.
diff --git a/src/lib/Bcfg2/Server/Encryption.py b/src/lib/Bcfg2/Server/Encryption.py
index c931ed2a7..b46337eb0 100755
--- a/src/lib/Bcfg2/Server/Encryption.py
+++ b/src/lib/Bcfg2/Server/Encryption.py
@@ -30,6 +30,9 @@ CFG_SECTION = "encryption"
#: The config option used to store the algorithm
CFG_ALGORITHM = "algorithm"
+#: The config option used to store the decryption strictness
+CFG_DECRYPT = "decrypt"
+
#: Default cipher algorithm. To get a full list of valid algorithms,
#: you can run::
#:
@@ -40,6 +43,7 @@ ALGORITHM = Bcfg2.Options.get_option_parser().cfp.get( # pylint: disable=E1103
CFG_ALGORITHM,
default="aes_256_cbc").lower().replace("-", "_")
+
Rand.rand_seed(os.urandom(1024))
@@ -165,7 +169,7 @@ def get_passphrases():
if setup.cfp.has_section(CFG_SECTION):
return dict([(o, setup.cfp.get(CFG_SECTION, o))
for o in setup.cfp.options(CFG_SECTION)
- if o != CFG_ALGORITHM])
+ if o not in [CFG_ALGORITHM, CFG_DECRYPT]])
else:
return dict()
diff --git a/src/lib/Bcfg2/Server/Lint/Comments.py b/src/lib/Bcfg2/Server/Lint/Comments.py
index ecea1ad1b..8bfb76461 100644
--- a/src/lib/Bcfg2/Server/Lint/Comments.py
+++ b/src/lib/Bcfg2/Server/Lint/Comments.py
@@ -130,7 +130,7 @@ class Comments(Bcfg2.Server.Lint.ServerPlugin):
rtype)
def check_plaintext(self, filename, data, rtype):
- """ check generic plaintex files for required headers """
+ """ check generic plaintext files for required headers """
self.check_lines(filename, data.splitlines(), rtype)
def check_lines(self, filename, lines, rtype):
diff --git a/src/lib/Bcfg2/Server/Lint/Genshi.py b/src/lib/Bcfg2/Server/Lint/Genshi.py
index 437e69d82..ed0d9930f 100755
--- a/src/lib/Bcfg2/Server/Lint/Genshi.py
+++ b/src/lib/Bcfg2/Server/Lint/Genshi.py
@@ -1,8 +1,11 @@
""" Check Genshi templates for syntax errors """
import sys
-import genshi.template
import Bcfg2.Server.Lint
+from genshi.template import TemplateLoader, NewTextTemplate, MarkupTemplate, \
+ TemplateSyntaxError
+from Bcfg2.Server.Plugins.Bundler import BundleTemplateFile
+from Bcfg2.Server.Plugins.Cfg.CfgGenshiGenerator import CfgGenshiGenerator
class Genshi(Bcfg2.Server.Lint.ServerPlugin):
@@ -10,29 +13,40 @@ class Genshi(Bcfg2.Server.Lint.ServerPlugin):
def Run(self):
""" run plugin """
- loader = genshi.template.TemplateLoader()
if 'Cfg' in self.core.plugins:
- self.check_files(self.core.plugins['Cfg'].entries,
- loader=loader)
+ self.check_cfg()
+ if 'Bundler' in self.core.plugins:
+ self.check_bundler()
@classmethod
def Errors(cls):
return {"genshi-syntax-error": "error"}
- def check_files(self, entries, loader=None):
- """ Check genshi templates in a list of entries for syntax
- errors """
- if loader is None:
- loader = genshi.template.TemplateLoader()
-
- for eset in entries.values():
- for fname, sdata in list(eset.entries.items()):
- if (self.HandlesFile(fname) and
- (fname.endswith(".genshi") or fname.endswith(".newtxt"))):
+ def check_cfg(self):
+ """ Check genshi templates in Cfg for syntax errors """
+ for entryset in self.core.plugins['Cfg'].entries.values():
+ for entry in entryset.entries.values():
+ if (self.HandlesFile(entry.name) and
+ isinstance(entry, CfgGenshiGenerator) and
+ not entry.template):
try:
- loader.load(sdata.name,
- cls=genshi.template.NewTextTemplate)
- except genshi.template.TemplateSyntaxError:
+ entry.loader.load(entry.name,
+ cls=NewTextTemplate)
+ except TemplateSyntaxError:
err = sys.exc_info()[1]
self.LintError("genshi-syntax-error",
"Genshi syntax error: %s" % err)
+
+ def check_bundler(self):
+ """ Check templates in Bundler for syntax errors """
+ loader = TemplateLoader()
+
+ for entry in self.core.plugins['Bundler'].entries.values():
+ if (self.HandlesFile(entry.name) and
+ isinstance(entry, BundleTemplateFile)):
+ try:
+ loader.load(entry.name, cls=MarkupTemplate)
+ except TemplateSyntaxError:
+ err = sys.exc_info()[1]
+ self.LintError("genshi-syntax-error",
+ "Genshi syntax error: %s" % err)
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
index b702ac899..313e53ee9 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgExternalCommandVerifier.py
@@ -23,10 +23,15 @@ class CfgExternalCommandVerifier(CfgVerifier):
def verify_entry(self, entry, metadata, data):
try:
proc = Popen(self.cmd, stdin=PIPE, stdout=PIPE, stderr=PIPE)
- err = proc.communicate(input=data)[1]
+ out, err = proc.communicate(input=data)
rv = proc.wait()
if rv != 0:
- raise CfgVerificationError(err)
+ # pylint: disable=E1103
+ raise CfgVerificationError(err.strip() or out.strip() or
+ "Non-zero return value %s" % rv)
+ # pylint: enable=E1103
+ except CfgVerificationError:
+ raise
except:
err = sys.exc_info()[1]
raise CfgVerificationError("Error running external command "
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
index c11939bd5..5f10879be 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/CfgGenshiGenerator.py
@@ -166,6 +166,7 @@ class CfgGenshiGenerator(CfgGenerator):
raise
def handle_event(self, event):
+ CfgGenerator.handle_event(self, event)
try:
self.template = self.loader.load(self.name, cls=NewTextTemplate,
encoding=self.encoding)
diff --git a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
index 81adea42f..2301de725 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cfg/__init__.py
@@ -553,11 +553,10 @@ class CfgEntrySet(Bcfg2.Server.Plugin.EntrySet,
try:
self._validate_data(entry, metadata, data)
except CfgVerificationError:
- msg = "Data for %s for %s failed to verify: %s" % \
- (entry.get('name'), metadata.hostname,
- sys.exc_info()[1])
- self.logger.error(msg)
- raise PluginExecutionError(msg)
+ raise PluginExecutionError("Failed to verify %s for %s: %s" %
+ (entry.get('name'),
+ metadata.hostname,
+ sys.exc_info()[1]))
if entry.get('encoding') == 'base64':
data = b64encode(data)
diff --git a/src/lib/Bcfg2/Server/Plugins/Defaults.py b/src/lib/Bcfg2/Server/Plugins/Defaults.py
index f4d86a64f..04c14aa96 100644
--- a/src/lib/Bcfg2/Server/Plugins/Defaults.py
+++ b/src/lib/Bcfg2/Server/Plugins/Defaults.py
@@ -5,7 +5,7 @@ import Bcfg2.Server.Plugins.Rules
class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
- Bcfg2.Server.Plugin.StructureValidator):
+ Bcfg2.Server.Plugin.GoalValidator):
"""Set default attributes on bound entries"""
__author__ = 'bcfg-dev@mcs.anl.gov'
@@ -22,27 +22,18 @@ class Defaults(Bcfg2.Server.Plugins.Rules.Rules,
def HandleEvent(self, event):
Bcfg2.Server.Plugin.XMLDirectoryBacked.HandleEvent(self, event)
- def validate_structures(self, metadata, structures):
+ def validate_goals(self, metadata, config):
""" Apply defaults """
- for struct in structures:
+ for struct in config.getchildren():
for entry in struct.getchildren():
- if entry.tag.startswith("Bound"):
- is_bound = True
- entry.tag = entry.tag[5:]
- else:
- is_bound = False
try:
- try:
- self.BindEntry(entry, metadata)
- except Bcfg2.Server.Plugin.PluginExecutionError:
- # either no matching defaults (which is okay),
- # or multiple matching defaults (which is not
- # okay, but is logged). either way, we don't
- # care about the error.
- pass
- finally:
- if is_bound:
- entry.tag = "Bound" + entry.tag
+ self.BindEntry(entry, metadata)
+ except Bcfg2.Server.Plugin.PluginExecutionError:
+ # either no matching defaults (which is okay),
+ # or multiple matching defaults (which is not
+ # okay, but is logged). either way, we don't
+ # care about the error.
+ pass
@property
def _regex_enabled(self):
diff --git a/src/lib/Bcfg2/Server/Plugins/Git.py b/src/lib/Bcfg2/Server/Plugins/Git.py
index 8cc63a46f..c8362db41 100644
--- a/src/lib/Bcfg2/Server/Plugins/Git.py
+++ b/src/lib/Bcfg2/Server/Plugins/Git.py
@@ -2,7 +2,7 @@
git. """
import sys
-import Bcfg2.Server.Plugin
+from Bcfg2.Server.Plugin import Version, PluginExecutionError
from subprocess import Popen, PIPE
try:
@@ -12,16 +12,16 @@ except ImportError:
HAS_GITPYTHON = False
-class Git(Bcfg2.Server.Plugin.Version):
+class Git(Version):
""" The Git plugin provides a revision interface for Bcfg2 repos
using git. """
__author__ = 'bcfg-dev@mcs.anl.gov'
__vcs_metadata_path__ = ".git"
if HAS_GITPYTHON:
- __rmi__ = Bcfg2.Server.Plugin.Version.__rmi__ + ['Update']
+ __rmi__ = Version.__rmi__ + ['Update']
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
+ Version.__init__(self, core, datastore)
if HAS_GITPYTHON:
self.repo = git.Repo(self.vcs_root)
else:
@@ -31,6 +31,11 @@ class Git(Bcfg2.Server.Plugin.Version):
self.logger.debug("Initialized git plugin with git directory %s" %
self.vcs_path)
+ def _log_git_cmd(self, output):
+ """ Send output from a GitPython command to the debug log """
+ for line in output.strip().splitlines():
+ self.debug_log("Git: %s" % line)
+
def get_revision(self):
"""Read git revision information for the Bcfg2 repository."""
try:
@@ -46,11 +51,9 @@ class Git(Bcfg2.Server.Plugin.Version):
raise Exception(err)
return rv
except:
- err = sys.exc_info()[1]
- msg = "Git: Error getting revision from %s: %s" % (self.vcs_root,
- err)
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ raise PluginExecutionError("Git: Error getting revision from %s: "
+ "%s" % (self.vcs_root,
+ sys.exc_info()[1]))
def Update(self, ref=None):
""" Git.Update() => True|False
@@ -60,20 +63,25 @@ class Git(Bcfg2.Server.Plugin.Version):
self.debug_log("Git: Performing garbage collection on repo at %s" %
self.vcs_root)
try:
- self.repo.git.gc('--auto')
+ self._log_git_cmd(self.repo.git.gc('--auto'))
except git.GitCommandError:
self.logger.warning("Git: Failed to perform garbage collection: %s"
% sys.exc_info()[1])
+ self.debug_log("Git: Fetching all refs for repo at %s" % self.vcs_root)
+ try:
+ self._log_git_cmd(self.repo.git.fetch('--all'))
+ except git.GitCommandError:
+ self.logger.warning("Git: Failed to fetch refs: %s" %
+ sys.exc_info()[1])
+
if ref:
self.debug_log("Git: Checking out %s" % ref)
try:
- self.repo.git.checkout('-f', ref)
+ self._log_git_cmd(self.repo.git.checkout('-f', ref))
except git.GitCommandError:
- err = sys.exc_info()[1]
- msg = "Git: Failed to checkout %s: %s" % (ref, err)
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ raise PluginExecutionError("Git: Failed to checkout %s: %s" %
+ (ref, sys.exc_info()[1]))
# determine if we should try to pull to get the latest commit
# on this head
@@ -87,12 +95,10 @@ class Git(Bcfg2.Server.Plugin.Version):
self.debug_log("Git: %s is a tracking branch, pulling from %s" %
(self.repo.head.ref.name, tracking))
try:
- self.repo.git.pull("--rebase")
- except: # pylint: disable=W0702
- err = sys.exc_info()[1]
- msg = "Git: Failed to pull from upstream: %s" % err
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ self._log_git_cmd(self.repo.git.pull("--rebase"))
+ except git.GitCommandError:
+ raise PluginExecutionError("Git: Failed to pull from "
+ "upstream: %s" % sys.exc_info()[1])
self.logger.info("Git: Repo at %s updated to %s" %
(self.vcs_root, self.get_revision()))
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index e568c5c65..b053e65d3 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -1452,7 +1452,7 @@ class MetadataLint(Bcfg2.Server.Lint.ServerPlugin):
defaults = []
for grp in self.metadata.groups_xml.xdata.xpath("//Groups/Group") + \
self.metadata.groups_xml.xdata.xpath("//Groups/Group//Group"):
- if grp.get("default"):
+ if grp.get("default", "false").lower() == "true":
defaults.append(self.RenderXML(grp))
if len(defaults) > 1:
self.LintError("multiple-default-groups",
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
index 3ad64b242..59eefe143 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Collection.py
@@ -533,7 +533,7 @@ class Collection(list, Bcfg2.Server.Plugin.Debuggable):
# should be resolved
current = pkgs.pop()
self.debug_log("Packages: handling package requirement %s" %
- current)
+ (current,))
packages.add(current)
deps = self.get_deps(current)
newdeps = set(deps).difference(examined)
diff --git a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
index 4057ed230..775caaa08 100644
--- a/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
+++ b/src/lib/Bcfg2/Server/Plugins/Packages/Yum.py
@@ -684,6 +684,21 @@ class YumCollection(Collection):
return self.call_helper("get_groups", inputdata=gdicts)
+ def _element_to_pkg(self, el, name):
+ """ Convert a Package or Instance element to a package tuple """
+ rv = (name, el.get("arch"), el.get("epoch"),
+ el.get("version"), el.get("release"))
+ if rv[3] in ['any', 'auto']:
+ rv = (rv[0], rv[1], rv[2], None, None)
+ # if a package requires no specific version, we just use
+ # the name, not the tuple. this limits the amount of JSON
+ # encoding/decoding that has to be done to pass the
+ # package list to bcfg2-yum-helper.
+ if rv[1:] == (None, None, None, None):
+ return name
+ else:
+ return rv
+
def packages_from_entry(self, entry):
""" When using the Python yum libraries, convert a Package
entry to a list of package tuples. See :ref:`yum-pkg-objects`
@@ -693,32 +708,42 @@ class YumCollection(Collection):
:type entry: lxml.etree._Element
:returns: list of tuples
"""
+ if not self.use_yum:
+ return Collection.packages_from_entry(self, entry)
+
rv = set()
name = entry.get("name")
- def _tag_to_pkg(tag):
- """ Convert a Package or Instance tag to a package tuple """
- rv = (name, tag.get("arch"), tag.get("epoch"),
- tag.get("version"), tag.get("release"))
- if rv[3] in ['any', 'auto']:
- rv = (rv[0], rv[1], rv[2], None, None)
- # if a package requires no specific version, we just use
- # the name, not the tuple. this limits the amount of JSON
- # encoding/decoding that has to be done to pass the
- # package list to bcfg2-yum-helper.
- if rv[1:] == (None, None, None, None):
- return name
- else:
- return rv
-
for inst in entry.getchildren():
if inst.tag != "Instance":
continue
- rv.add(_tag_to_pkg(inst))
+ rv.add(self._element_to_pkg(inst, name))
if not rv:
- rv.add(_tag_to_pkg(entry))
+ rv.add(self._element_to_pkg(entry, name))
return list(rv)
+ def _get_entry_attrs(self, pkgtup):
+ """ Given a package tuple, return a dict of attributes
+ suitable for applying to either a Package or an Instance
+ tag """
+ attrs = dict(version=self.setup.cfp.get("packages", "version",
+ default="auto"))
+ if attrs['version'] == 'any' or not isinstance(pkgtup, tuple):
+ return attrs
+
+ try:
+ if pkgtup[1]:
+ attrs['arch'] = pkgtup[1]
+ if pkgtup[2]:
+ attrs['epoch'] = pkgtup[2]
+ if pkgtup[3]:
+ attrs['version'] = pkgtup[3]
+ if pkgtup[4]:
+ attrs['release'] = pkgtup[4]
+ except IndexError:
+ self.logger.warning("Malformed package tuple: %s" % pkgtup)
+ return attrs
+
def packages_to_entry(self, pkglist, entry):
""" When using the Python yum libraries, convert a list of
package tuples to a Package entry. See :ref:`yum-pkg-objects`
@@ -738,28 +763,8 @@ class YumCollection(Collection):
:type entry: lxml.etree._Element
:returns: None
"""
- def _get_entry_attrs(pkgtup):
- """ Given a package tuple, return a dict of attributes
- suitable for applying to either a Package or an Instance
- tag """
- attrs = dict(version=self.setup.cfp.get("packages",
- "version",
- default="auto"))
- if attrs['version'] == 'any' or not isinstance(pkgtup, tuple):
- return attrs
-
- try:
- if pkgtup[1]:
- attrs['arch'] = pkgtup[1]
- if pkgtup[2]:
- attrs['epoch'] = pkgtup[2]
- if pkgtup[3]:
- attrs['version'] = pkgtup[3]
- if pkgtup[4]:
- attrs['release'] = pkgtup[4]
- except IndexError:
- self.logger.warning("Malformed package tuple: %s" % pkgtup)
- return attrs
+ if not self.use_yum:
+ return Collection.packages_to_entry(self, pkglist, entry)
packages = dict()
for pkg in pkglist:
@@ -776,9 +781,9 @@ class YumCollection(Collection):
**pkgattrs)
for inst in instances:
lxml.etree.SubElement(pkg_el, "Instance",
- _get_entry_attrs(inst))
+ self._get_entry_attrs(inst))
else:
- attrs = _get_entry_attrs(instances[0])
+ attrs = self._get_entry_attrs(instances[0])
attrs.update(pkgattrs)
lxml.etree.SubElement(entry, 'BoundPackage', **attrs)
@@ -803,7 +808,11 @@ class YumCollection(Collection):
initial_names.append(pkg)
new = []
for pkg in complete:
- if pkg[0] not in initial_names:
+ if isinstance(pkg, tuple):
+ name = pkg[0]
+ else:
+ name = pkg
+ if name not in initial_names:
new.append(pkg)
return new
diff --git a/src/lib/Bcfg2/Server/Plugins/SSLCA.py b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
index cc1a2ceac..ab2f80552 100644
--- a/src/lib/Bcfg2/Server/Plugins/SSLCA.py
+++ b/src/lib/Bcfg2/Server/Plugins/SSLCA.py
@@ -180,7 +180,7 @@ class SSLCAEntrySet(Bcfg2.Server.Plugin.EntrySet):
self.logger.error("SSLCA: Failed to unlink temporary files: %s"
% sys.exc_info()[1])
if cert_spec['append_chain'] and 'chaincert' in ca:
- cert += open(self.parent.get_ca(ca)['chaincert']).read()
+ cert += open(ca['chaincert']).read()
open(os.path.join(self.path, filename), 'w').write(cert)
return cert
@@ -363,7 +363,8 @@ class SSLCA(Bcfg2.Server.Plugin.GroupSpool):
""" The SSLCA generator handles the creation and management of ssl
certificates and their keys. """
__author__ = 'g.hagger@gmail.com'
- es_cls = lambda self, *args: SSLCAEntrySet(*args, parent=self)
+ # python 2.5 doesn't support mixing *magic and keyword arguments
+ es_cls = lambda self, *args: SSLCAEntrySet(*args, **dict(parent=self))
es_child_cls = SSLCADataFile
def get_ca(self, name):
diff --git a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
index e3f8ed749..ad3eb65bc 100644
--- a/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
+++ b/src/lib/Bcfg2/Server/Plugins/TemplateHelper.py
@@ -12,12 +12,25 @@ LOGGER = logging.getLogger(__name__)
MODULE_RE = re.compile(r'(?P<filename>(?P<module>[^\/]+)\.py)$')
+def safe_module_name(module):
+ """ Munge the name of a TemplateHelper module to avoid collisions
+ with other Python modules. E.g., if someone has a helper named
+ 'ldap.py', it should not be added to ``sys.modules`` as ``ldap``,
+ but rather as something more obscure. """
+ return '__TemplateHelper_%s' % module
+
+
class HelperModule(object):
""" Representation of a TemplateHelper module """
def __init__(self, name):
self.name = name
+
+ #: The name of the module as used by get_additional_data().
+ #: the name of the file with .py stripped off.
self._module_name = MODULE_RE.search(self.name).group('module')
+
+ #: The attributes exported by this module
self._attrs = []
def HandleEvent(self, event=None):
@@ -31,7 +44,8 @@ class HelperModule(object):
return
try:
- module = imp.load_source(self._module_name, self.name)
+ module = imp.load_source(safe_module_name(self._module_name),
+ self.name)
except: # pylint: disable=W0702
err = sys.exc_info()[1]
LOGGER.error("TemplateHelper: Failed to import %s: %s" %
@@ -97,7 +111,7 @@ class TemplateHelperLint(Bcfg2.Server.Lint.ServerPlugin):
module_name = MODULE_RE.search(helper).group(1)
try:
- module = imp.load_source(module_name, helper)
+ module = imp.load_source(safe_module_name(module_name), helper)
except: # pylint: disable=W0702
err = sys.exc_info()[1]
self.LintError("templatehelper-import-error",
diff --git a/src/lib/Bcfg2/Server/Reports/updatefix.py b/src/lib/Bcfg2/Server/Reports/updatefix.py
index b377806ab..cb131c29d 100644
--- a/src/lib/Bcfg2/Server/Reports/updatefix.py
+++ b/src/lib/Bcfg2/Server/Reports/updatefix.py
@@ -80,6 +80,9 @@ def _populate_interaction_entry_counts():
cursor.close()
+def update_noop():
+ return True
+
# be sure to test your upgrade query before reflecting the change in the models
# the list of function and sql command to do should go here
_fixes = [_merge_database_table_entries,
@@ -103,6 +106,8 @@ _fixes = [_merge_database_table_entries,
_interactions_constraint_or_idx,
'alter table reports_reason add is_binary bool NOT NULL default False;',
'alter table reports_reason add is_sensitive bool NOT NULL default False;',
+ update_noop, #_remove_table_column('reports_interaction', 'client_version'),
+ "alter table reports_reason add unpruned varchar(1280) not null default 'N/A';",
]
# this will calculate the last possible version of the database
diff --git a/src/lib/Bcfg2/settings.py b/src/lib/Bcfg2/settings.py
index da6e2036e..d819ee534 100644
--- a/src/lib/Bcfg2/settings.py
+++ b/src/lib/Bcfg2/settings.py
@@ -136,6 +136,8 @@ if HAS_SOUTH:
'south',
'Bcfg2.Reporting',
)
+if 'BCFG2_LEGACY_MODELS' in os.environ:
+ INSTALLED_APPS += ('Bcfg2.Server.Reports.reports',)
# Imported from Bcfg2.Server.Reports
MEDIA_ROOT = ''
diff --git a/src/lib/Bcfg2/version.py b/src/lib/Bcfg2/version.py
index 86803afcb..8223d7543 100644
--- a/src/lib/Bcfg2/version.py
+++ b/src/lib/Bcfg2/version.py
@@ -2,7 +2,7 @@
import re
-__version__ = "1.3.0rc1"
+__version__ = "1.3.0rc2"
class Bcfg2VersionInfo(tuple):