summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/server/plugins/version/git.txt36
-rw-r--r--doc/server/plugins/version/svn.txt30
-rw-r--r--doc/server/plugins/version/svn2.txt38
-rw-r--r--src/lib/Bcfg2/Options.py8
-rw-r--r--src/lib/Bcfg2/Server/Plugin/interfaces.py27
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Bzr.py10
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Cvs.py15
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Darcs.py12
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Fossil.py10
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Git.py143
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Hg.py13
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Svn.py133
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Svn2.py116
-rw-r--r--testsuite/pylintrc.conf2
-rw-r--r--tools/manpagegen/bcfg2.conf.5.ronn6
15 files changed, 332 insertions, 267 deletions
diff --git a/doc/server/plugins/version/git.txt b/doc/server/plugins/version/git.txt
index ea2f087ba..d4b9187fa 100644
--- a/doc/server/plugins/version/git.txt
+++ b/doc/server/plugins/version/git.txt
@@ -6,26 +6,32 @@
Git
===
-Why use the Git plugin
-======================
-
-The Git plugin is useful if you would like to track changes to your bcfg2
-repository using a `Git <http://git-scm.com/>`_ backend. Currently,
-It enables you to get revision information out of your repository for
+The Git plugin is useful if you would like to track changes to your
+bcfg2 repository using a `Git <http://git-scm.com/>`_ backend. It
+enables you to get revision information out of your repository for
reporting purposes. Once the plugin is enabled, every time a client
checks in, it will include the current repository revision in the
reports/statistics.
-Future plans are to commit changes to the repo which are made by the
-server (adding clients, ssh keys, etc).
+If the ``dulwich`` library is installed, the Git plugin will use
+that. If ``dulwich`` is not installed, but ``GitPython`` is, that
+will be used instead. If neither is installed, then calls will be
+made to the git command.
+
+If you plan to edit your git repository in-place on the Bcfg2 server
+(which is probably not recommended), then you may want to avoid using
+``dulwich``; it's sufficiently low-level that it may not present a
+user-friendly git repository at all times.
+
+Additionally, the Git plugin exposes one XML-RPC method calls,
+``Git.Update``, which updates the working copy to the latest version
+in the remote origin.
-How to enable the Git plugin
-============================
+Enabling the Git plugin
+=======================
-The Git plugin uses `Dulwich <http://samba.org/~jelmer/dulwich/>`_ to
-interface with git repositories. Therefore, you will need to install
-Dulwich on the Bcfg2 server first. Once installed, simply add Git to
-your plugins line in ``/etc/bcfg2.conf``::
+To enable the Git plugin, simply add it to your plugins line in
+``/etc/bcfg2.conf``::
[server]
- plugins = Base,Bundler,Cfg,...,Git
+ plugins = Bundler,Cfg,Metadata,...,Git
diff --git a/doc/server/plugins/version/svn.txt b/doc/server/plugins/version/svn.txt
index a23d4d35b..7166ee89d 100644
--- a/doc/server/plugins/version/svn.txt
+++ b/doc/server/plugins/version/svn.txt
@@ -7,22 +7,28 @@ Svn
===
The Svn plugin is useful if you would like to track changes to your
-bcfg2 repository using a `Subversion <http://subversion.tigris.org/>`_
-backend. It deprecates the previous Subversion integration mentioned here
-at ftp://ftp.mcs.anl.gov/pub/bcfg/papers/directing-change-with-bcfg2.pdf.
+Bcfg2 repository using a `Subversion <http://subversion.tigris.org/>`_
+backend.
-Currently, It enables you to get revision information out of your
-repository for reporting purposes. Once the plugin is enabled, every time
-a client checks in, it will include the current repository revision in
-the reports/statistics.
+As with the other Version plugins, the Svn plugin enables you to get
+revision information out of your repository for reporting
+purposes. Once the plugin is enabled, every time a client checks in,
+it will include the current repository revision in the
+reports/statistics.
-Future plans are to commit changes to the repo which are made by the
-server (adding clients, ssh keys, etc).
+Additionally, if the ``pysvn`` library is installed, the Svn plugin
+exposes two XML-RPC method calls:
-How to enable the Svn plugin
-============================
+* ``Svn.Update`` updates the working copy to the latest version in the
+ repository.
+* ``Svn.Commit`` commits any changes to the working copy back to the
+ repository. In order for this to work, the user Bcfg2 runs as must
+ be able to commit to the repository non-interactively.
+
+Enabling the Svn plugin
+=======================
Simply add Svn to your plugins line in ``/etc/bcfg2.conf``::
[server]
- plugins = Base,Bundler,Cfg,..,Svn
+ plugins = Bundler,Cfg,Metadata,..,Svn
diff --git a/doc/server/plugins/version/svn2.txt b/doc/server/plugins/version/svn2.txt
deleted file mode 100644
index 021ae4fd2..000000000
--- a/doc/server/plugins/version/svn2.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-.. -*- mode: rst -*-
-
-.. _server-plugins-version-svn2:
-
-====
-Svn2
-====
-
-The Svn2 plugin is useful if you would like to track changes to your
-bcfg2 repository using a `Subversion <http://subversion.tigris.org/>`_
-backend. It is a rewrite of the Svn plugin using the
-`pysvn <http://pysvn.tigris.org/>`_ api.
-
-Currently, it enables you to get revision information out of your
-repository for reporting purposes. Once the plugin is enabled, every time
-a client checks in, it will include the current repository revision in
-the reports/statistics. If any changes are made from the bcfg2-server,
-the plugin will commit them back into the repositroy. For example,
-``/usr/sbin/bcfg2-admin pull client Path /some/file`` will update the
-servers repository and commit the changes into svn.
-
-How to enable the Svn2 plugin
-=============================
-
-Simply add Svn2 to your plugins line in ``/etc/bcfg2.conf``::
-
- [server]
- plugins = Base,Bundler,Cfg,..,Svn2
-
-Auto Commit feautre
-===================
-
-Svn2 adds the ability to commit changes back into the repository. In
-order for this feature to work the repository should already be under version
-control and the user the bcfg2-server runs as must be able to commit without
-any interaction. For example, a file base repository url
-(``file:///path/to/repo``).
-
diff --git a/src/lib/Bcfg2/Options.py b/src/lib/Bcfg2/Options.py
index 83234a792..f3765a5ec 100644
--- a/src/lib/Bcfg2/Options.py
+++ b/src/lib/Bcfg2/Options.py
@@ -572,6 +572,11 @@ SERVER_DAEMON_GROUP = \
default=0,
cf=('server', 'group'),
cook=get_gid)
+SERVER_VCS_ROOT = \
+ Option('Server VCS repository root',
+ default=None,
+ odesc='<VCS repository root>',
+ cf=('server', 'vcs_root'))
# database options
DB_ENGINE = \
@@ -1078,7 +1083,8 @@ SERVER_COMMON_OPTIONS = dict(repo=SERVER_REPOSITORY,
ca=SERVER_CA,
protocol=SERVER_PROTOCOL,
web_configfile=WEB_CFILE,
- backend=SERVER_BACKEND)
+ backend=SERVER_BACKEND,
+ vcs_root=SERVER_VCS_ROOT)
CRYPT_OPTIONS = dict(encrypt=ENCRYPT,
decrypt=DECRYPT,
diff --git a/src/lib/Bcfg2/Server/Plugin/interfaces.py b/src/lib/Bcfg2/Server/Plugin/interfaces.py
index 894165858..cba3e8145 100644
--- a/src/lib/Bcfg2/Server/Plugin/interfaces.py
+++ b/src/lib/Bcfg2/Server/Plugin/interfaces.py
@@ -506,7 +506,7 @@ class GoalValidator(object):
raise NotImplementedError
-class Version(object):
+class Version(Plugin):
""" Version plugins interact with various version control systems. """
#: The path to the VCS metadata file or directory, relative to the
@@ -514,26 +514,23 @@ class Version(object):
#: be ".svn"
__vcs_metadata_path__ = None
- def __init__(self, datastore):
- """
- :param datastore: The path to the Bcfg2 repository on the
- filesystem
- :type datastore: string
- :raises: :class:`Bcfg2.Server.Plugin.exceptions.PluginInitError`
-
- .. autoattribute:: __vcs_metadata_path__
- """
+ def __init__(self, core, datastore):
+ Plugin.__init__(self, core, datastore)
- self.datastore = datastore
if self.__vcs_metadata_path__:
- self.vcs_path = os.path.join(datastore, self.__vcs_metadata_path__)
-
- if os.path.exists(self.vcs_path):
- self.get_revision()
+ if core.setup['vcs_root']:
+ self.vcs_root = core.setup['vcs_root']
else:
+ self.vcs_root = datastore
+ self.vcs_path = os.path.join(self.vcs_root,
+ self.__vcs_metadata_path__)
+
+ if not os.path.exists(self.vcs_path):
raise PluginInitError("%s is not present" % self.vcs_path)
else:
self.vcs_path = None
+ __init__.__doc__ = Plugin.__init__.__doc__ + """
+.. autoattribute:: __vcs_metadata_path__ """
def get_revision(self):
""" Return the current revision of the Bcfg2 specification.
diff --git a/src/lib/Bcfg2/Server/Plugins/Bzr.py b/src/lib/Bcfg2/Server/Plugins/Bzr.py
index a8b4113ff..e0cbdf72a 100644
--- a/src/lib/Bcfg2/Server/Plugins/Bzr.py
+++ b/src/lib/Bcfg2/Server/Plugins/Bzr.py
@@ -6,23 +6,21 @@ from bzrlib.workingtree import WorkingTree
from bzrlib import errors
-class Bzr(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Version):
+class Bzr(Bcfg2.Server.Plugin.Version):
""" The Bzr plugin provides a revision interface for Bcfg2 repos
using bazaar. """
__author__ = 'bcfg-dev@mcs.anl.gov'
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Version.__init__(self, datastore)
+ Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
self.logger.debug("Initialized Bazaar plugin with directory %s at "
- "revision = %s" % (self.datastore,
+ "revision = %s" % (self.vcs_root,
self.get_revision()))
def get_revision(self):
"""Read Bazaar revision information for the Bcfg2 repository."""
try:
- working_tree = WorkingTree.open(self.datastore)
+ working_tree = WorkingTree.open(self.vcs_root)
revision = str(working_tree.branch.revno())
if (working_tree.has_changes(working_tree.basis_tree()) or
working_tree.unknowns()):
diff --git a/src/lib/Bcfg2/Server/Plugins/Cvs.py b/src/lib/Bcfg2/Server/Plugins/Cvs.py
index a36a116f5..ba1559a1a 100644
--- a/src/lib/Bcfg2/Server/Plugins/Cvs.py
+++ b/src/lib/Bcfg2/Server/Plugins/Cvs.py
@@ -5,17 +5,15 @@ from subprocess import Popen, PIPE
import Bcfg2.Server.Plugin
-class Cvs(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Version):
+class Cvs(Bcfg2.Server.Plugin.Version):
""" The Cvs plugin provides a revision interface for Bcfg2 repos
using cvs."""
__author__ = 'bcfg-dev@mcs.anl.gov'
__vcs_metadata_path__ = "CVSROOT"
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Version.__init__(self, datastore)
- self.logger.debug("Initialized cvs plugin with cvs directory %s" %
+ Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
+ self.logger.debug("Initialized cvs plugin with CVS directory %s" %
self.vcs_path)
def get_revision(self):
@@ -23,11 +21,12 @@ class Cvs(Bcfg2.Server.Plugin.Plugin,
try:
data = Popen("env LC_ALL=C cvs log",
shell=True,
- cwd=self.datastore,
+ cwd=self.vcs_root,
stdout=PIPE).stdout.readlines()
return data[3].strip('\n')
except IndexError:
- msg = "Failed to read cvs log"
+ msg = "Failed to read CVS log"
self.logger.error(msg)
- self.logger.error('Ran command "cvs log %s"' % self.datastore)
+ self.logger.error('Ran command "cvs log" from directory %s' %
+ self.vcs_root)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
diff --git a/src/lib/Bcfg2/Server/Plugins/Darcs.py b/src/lib/Bcfg2/Server/Plugins/Darcs.py
index 9ec8e2df3..0033e00f3 100644
--- a/src/lib/Bcfg2/Server/Plugins/Darcs.py
+++ b/src/lib/Bcfg2/Server/Plugins/Darcs.py
@@ -5,16 +5,14 @@ from subprocess import Popen, PIPE
import Bcfg2.Server.Plugin
-class Darcs(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Version):
+class Darcs(Bcfg2.Server.Plugin.Version):
""" Darcs is a version plugin for dealing with Bcfg2 repos stored
in the Darcs VCS. """
__author__ = 'bcfg-dev@mcs.anl.gov'
__vcs_metadata_path__ = "_darcs"
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Version.__init__(self, datastore)
+ Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
self.logger.debug("Initialized Darcs plugin with darcs directory %s" %
self.vcs_path)
@@ -23,13 +21,13 @@ class Darcs(Bcfg2.Server.Plugin.Plugin,
try:
data = Popen("env LC_ALL=C darcs changes",
shell=True,
- cwd=self.datastore,
+ cwd=self.vcs_root,
stdout=PIPE).stdout.readlines()
revision = data[0].strip('\n')
except:
msg = "Failed to read darcs repository"
self.logger.error(msg)
- self.logger.error('Ran command "darcs changes" from directory "%s"'
- % self.datastore)
+ self.logger.error('Ran command "darcs changes" from directory %s' %
+ self.vcs_root)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
return revision
diff --git a/src/lib/Bcfg2/Server/Plugins/Fossil.py b/src/lib/Bcfg2/Server/Plugins/Fossil.py
index 85d0f38f5..f6735df12 100644
--- a/src/lib/Bcfg2/Server/Plugins/Fossil.py
+++ b/src/lib/Bcfg2/Server/Plugins/Fossil.py
@@ -5,16 +5,14 @@ from subprocess import Popen, PIPE
import Bcfg2.Server.Plugin
-class Fossil(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Version):
+class Fossil(Bcfg2.Server.Plugin.Version):
""" The Fossil plugin provides a revision interface for Bcfg2
repos using fossil. """
__author__ = 'bcfg-dev@mcs.anl.gov'
__vcs_metadata_path__ = "_FOSSIL_"
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Version.__init__(self, datastore)
+ Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
self.logger.debug("Initialized Fossil plugin with fossil directory %s"
% self.vcs_path)
@@ -23,7 +21,7 @@ class Fossil(Bcfg2.Server.Plugin.Plugin,
try:
data = Popen("env LC_ALL=C fossil info",
shell=True,
- cwd=self.datastore,
+ cwd=self.vcs_root,
stdout=PIPE).stdout.readlines()
revline = [line.split(': ')[1].strip() for line in data if \
line.split(': ')[0].strip() == 'checkout'][-1]
@@ -32,5 +30,5 @@ class Fossil(Bcfg2.Server.Plugin.Plugin,
msg = "Failed to read fossil info"
self.logger.error(msg)
self.logger.error('Ran command "fossil info" from directory "%s"' %
- self.datastore)
+ self.vcs_root)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
diff --git a/src/lib/Bcfg2/Server/Plugins/Git.py b/src/lib/Bcfg2/Server/Plugins/Git.py
index 177770a12..b7269af48 100644
--- a/src/lib/Bcfg2/Server/Plugins/Git.py
+++ b/src/lib/Bcfg2/Server/Plugins/Git.py
@@ -1,38 +1,153 @@
""" The Git plugin provides a revision interface for Bcfg2 repos using
git. """
+import os
+import sys
+import Bcfg2.Server.Plugin
+
+
+class GitAPIBase(object):
+ """ Base class for the various Git APIs (dulwich, GitPython,
+ subprocesses) """
+ def __init__(self, path):
+ self.path = path
+
+ def revision(self):
+ """ Get the current revision of the git repo as a string """
+ raise NotImplementedError
+
+ def pull(self):
+ """ Pull the latest version of the upstream git repo and
+ rebase against it. """
+ raise NotImplementedError
+
+
try:
+ from dulwich.client import get_transport_and_path
from dulwich.repo import Repo
+ from dulwich.file import GitFile, ensure_dir_exists
+
+ class GitAPI(GitAPIBase):
+ """ API for :class:`Git` using :mod:`dulwich` """
+ def __init__(self, path):
+ GitAPIBase.__init__(self, path)
+ self.repo = Repo(self.path)
+ self.client, self.origin_path = get_transport_and_path(
+ self.repo.get_config().get(("remote", "origin"),
+ "url"))
+
+ def revision(self):
+ return self.repo.head()
+
+ def pull(self):
+ try:
+ remote_refs = self.client.fetch(
+ self.origin_path, self.repo,
+ determine_wants=self.repo.object_store.determine_wants_all)
+ except KeyError:
+ etype, err = sys.exc_info()[:2]
+ # try to work around bug
+ # https://bugs.launchpad.net/dulwich/+bug/1025886
+ try:
+ # pylint: disable=W0212
+ self.client._fetch_capabilities.remove('thin-pack')
+ # pylint: enable=W0212
+ except KeyError:
+ raise etype(err)
+ remote_refs = self.client.fetch(
+ self.origin_path, self.repo,
+ determine_wants=self.repo.object_store.determine_wants_all)
+
+ tree_id = self.repo[remote_refs['HEAD']].tree
+ # iterate over tree content, giving path and blob sha.
+ for entry in self.repo.object_store.iter_tree_contents(tree_id):
+ entry_in_path = entry.in_path(self.repo.path)
+ ensure_dir_exists(os.path.split(entry_in_path.path)[0])
+ GitFile(entry_in_path.path,
+ 'wb').write(self.repo[entry.sha].data)
+
except ImportError:
- # fallback to shell commands when dulwich unavailable
- from subprocess import Popen, PIPE
+ try:
+ import git
-import Bcfg2.Server.Plugin
+ class GitAPI(GitAPIBase):
+ """ API for :class:`Git` using :mod:`git` (GitPython) """
+ def __init__(self, path):
+ GitAPIBase.__init__(self, path)
+ self.repo = git.Repo(path)
+ def revision(self):
+ return self.repo.head.commit.hexsha
-class Git(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Version):
+ def pull(self):
+ self.repo.git.pull("--rebase")
+
+ except ImportError:
+ from subprocess import Popen, PIPE
+
+ try:
+ Popen(["git"], stdout=PIPE, stderr=PIPE).wait()
+
+ class GitAPI(GitAPIBase):
+ """ API for :class:`Git` using subprocess to run git
+ commands """
+ def revision(self):
+ proc = Popen(["git", "--work-tree", self.path,
+ "rev-parse", "HEAD"], stdout=PIPE,
+ stderr=PIPE)
+ rv, err = proc.communicate()
+ if proc.wait():
+ raise Exception("Git: Error getting revision from %s: "
+ "%s" % (self.path, err))
+ return rv.strip() # pylint: disable=E1103
+
+ def pull(self):
+ proc = Popen(["git", "--work-tree", self.path,
+ "pull", "--rebase"], stdout=PIPE,
+ stderr=PIPE)
+ err = proc.communicate()[1].strip()
+ if proc.wait():
+ raise Exception("Git: Error pulling: %s" % err)
+
+ except OSError:
+ raise ImportError("Could not import dulwich or GitPython "
+ "libraries, and no 'git' command found in PATH")
+
+
+class Git(Bcfg2.Server.Plugin.Version):
""" The Git plugin provides a revision interface for Bcfg2 repos
using git. """
__author__ = 'bcfg-dev@mcs.anl.gov'
__vcs_metadata_path__ = ".git"
+ __rmi__ = Bcfg2.Server.Plugin.Version.__rmi__ + ['Update']
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Version.__init__(self, datastore)
+ Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
+ self.repo = GitAPI(self.vcs_root)
self.logger.debug("Initialized git plugin with git directory %s" %
self.vcs_path)
def get_revision(self):
"""Read git revision information for the Bcfg2 repository."""
try:
- return Repo(self.datastore).head()
- except NameError:
- return Popen("env LC_ALL=C git log -1 --pretty=format:%H",
- shell=True,
- cwd=self.datastore,
- stdout=PIPE).stdout.readline()
+ return self.repo.revision()
except:
- msg = "Failed to read git repository"
+ err = sys.exc_info()[1]
+ msg = "Failed to read git repository: %s" % err
+ self.logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+
+ def Update(self):
+ """ Git.Update() => True|False
+ Update the working copy against the upstream repository
+ """
+ try:
+ self.repo.pull()
+ self.logger.info("Git repo at %s updated to %s" %
+ (self.vcs_root, self.get_revision()))
+ return True
+ except: # pylint: disable=W0702
+ err = sys.exc_info()[1]
+ msg = "Failed to pull from git repository: %s" % err
self.logger.error(msg)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
diff --git a/src/lib/Bcfg2/Server/Plugins/Hg.py b/src/lib/Bcfg2/Server/Plugins/Hg.py
index b5dec2e3f..3fd3918bd 100644
--- a/src/lib/Bcfg2/Server/Plugins/Hg.py
+++ b/src/lib/Bcfg2/Server/Plugins/Hg.py
@@ -1,32 +1,31 @@
""" The Hg plugin provides a revision interface for Bcfg2 repos using
mercurial. """
+import sys
from mercurial import ui, hg
import Bcfg2.Server.Plugin
-class Hg(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Version):
+class Hg(Bcfg2.Server.Plugin.Version):
""" The Hg plugin provides a revision interface for Bcfg2 repos
using mercurial. """
-
__author__ = 'bcfg-dev@mcs.anl.gov'
__vcs_metadata_path__ = ".hg"
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Version.__init__(self, datastore)
+ Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
self.logger.debug("Initialized hg plugin with hg directory %s" %
self.vcs_path)
def get_revision(self):
"""Read hg revision information for the Bcfg2 repository."""
try:
- repo_path = self.datastore + "/"
+ repo_path = self.vcs_root + "/"
repo = hg.repository(ui.ui(), repo_path)
tip = repo.changelog.tip()
return repo.changelog.rev(tip)
except:
- msg = "Failed to read hg repository"
+ err = sys.exc_info()[1]
+ msg = "Failed to read hg repository: %s" % err
self.logger.error(msg)
raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
diff --git a/src/lib/Bcfg2/Server/Plugins/Svn.py b/src/lib/Bcfg2/Server/Plugins/Svn.py
index 5bc9d6bbd..fda6b57b5 100644
--- a/src/lib/Bcfg2/Server/Plugins/Svn.py
+++ b/src/lib/Bcfg2/Server/Plugins/Svn.py
@@ -1,34 +1,125 @@
""" The Svn plugin provides a revision interface for Bcfg2 repos using
-svn. """
+Subversion. If PySvn libraries are installed, then it exposes two
+additional XML-RPC methods for committing data to the repository and
+updating the repository. """
-import pipes
-from subprocess import Popen, PIPE
+import sys
import Bcfg2.Server.Plugin
+try:
+ import pysvn
+ HAS_SVN = True
+except ImportError:
+ import pipes
+ from subprocess import Popen, PIPE
+ HAS_SVN = False
-class Svn(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Version):
- """ The Svn plugin provides a revision interface for Bcfg2 repos
- using svn. """
+class Svn(Bcfg2.Server.Plugin.Version):
+ """Svn is a version plugin for dealing with Bcfg2 repos."""
__author__ = 'bcfg-dev@mcs.anl.gov'
- __vcs_metadata_path__ = ".svn"
+ if HAS_SVN:
+ __rmi__ = Bcfg2.Server.Plugin.Version.__rmi__ + ['Update', 'Commit']
+ else:
+ __vcs_metadata_path__ = ".svn"
def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Version.__init__(self, datastore)
- self.logger.debug("Initialized svn plugin with svn directory %s" %
+ Bcfg2.Server.Plugin.Version.__init__(self, core, datastore)
+
+ self.revision = None
+ self.svn_root = None
+ if not HAS_SVN:
+ self.logger.debug("Svn: PySvn not found, using CLI interface to "
+ "SVN")
+ self.client = None
+ else:
+ self.client = pysvn.Client()
+
+ self.logger.debug("Initialized svn plugin with SVN directory %s" %
self.vcs_path)
def get_revision(self):
"""Read svn revision information for the Bcfg2 repository."""
+ msg = None
+ if HAS_SVN:
+ try:
+ info = self.client.info(self.vcs_root)
+ self.revision = info.revision
+ self.svn_root = info.url
+ return str(self.revision.number)
+ except pysvn.ClientError: # pylint: disable=E1101
+ msg = "Svn: Failed to get revision: %s" % sys.exc_info()[1]
+ else:
+ try:
+ data = Popen(("env LC_ALL=C svn info %s" %
+ pipes.quote(self.vcs_root)), shell=True,
+ stdout=PIPE).communicate()[0].split('\n')
+ return [line.split(': ')[1] for line in data \
+ if line[:9] == 'Revision:'][-1]
+ except IndexError:
+ msg = "Failed to read svn info"
+ self.logger.error('Ran command "svn info %s"' % self.vcs_root)
+ self.revision = None
+ self.logger.error(msg)
+ raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+
+ def Update(self):
+ '''Svn.Update() => True|False\nUpdate svn working copy\n'''
try:
- data = Popen(("env LC_ALL=C svn info %s" %
- pipes.quote(self.datastore)), shell=True,
- stdout=PIPE).communicate()[0].split('\n')
- return [line.split(': ')[1] for line in data \
- if line[:9] == 'Revision:'][-1]
- except IndexError:
- msg = "Failed to read svn info"
- self.logger.error(msg)
- self.logger.error('Ran command "svn info %s"' % self.datastore)
- raise Bcfg2.Server.Plugin.PluginExecutionError(msg)
+ old_revision = self.revision.number
+ self.revision = self.client.update(self.vcs_root, recurse=True)[0]
+ except pysvn.ClientError: # pylint: disable=E1101
+ err = sys.exc_info()[1]
+ # try to be smart about the error we got back
+ details = None
+ if "callback_ssl_server_trust_prompt" in str(err):
+ details = "SVN server certificate is not trusted"
+ elif "callback_get_login" in str(err):
+ details = "SVN credentials not cached"
+
+ if details is None:
+ self.logger.error("Svn: Failed to update server repository",
+ exc_info=1)
+ else:
+ self.logger.error("Svn: Failed to update server repository: "
+ "%s" % details)
+ return False
+
+ if old_revision == self.revision.number:
+ self.logger.debug("repository is current")
+ else:
+ self.logger.info("Updated %s from revision %s to %s" % \
+ (self.vcs_root, old_revision, self.revision.number))
+ return True
+
+ def Commit(self):
+ """Svn.Commit() => True|False\nCommit svn repository\n"""
+ # First try to update
+ if not self.Update():
+ self.logger.error("Failed to update svn repository, refusing to "
+ "commit changes")
+ return False
+
+ try:
+ self.revision = self.client.checkin([self.vcs_root],
+ 'Svn: autocommit',
+ recurse=True)
+ self.revision = self.client.update(self.vcs_root, recurse=True)[0]
+ self.logger.info("Svn: Commited changes. At %s" %
+ self.revision.number)
+ return True
+ except pysvn.ClientError: # pylint: disable=E1101
+ err = sys.exc_info()[1]
+ # try to be smart about the error we got back
+ details = None
+ if "callback_ssl_server_trust_prompt" in str(err):
+ details = "SVN server certificate is not trusted"
+ elif "callback_get_login" in str(err):
+ details = "SVN credentials not cached"
+
+ if details is None:
+ self.logger.error("Svn: Failed to commit changes",
+ exc_info=1)
+ else:
+ self.logger.error("Svn: Failed to commit changes: %s" %
+ details)
+ return False
diff --git a/src/lib/Bcfg2/Server/Plugins/Svn2.py b/src/lib/Bcfg2/Server/Plugins/Svn2.py
deleted file mode 100644
index 19093eb6a..000000000
--- a/src/lib/Bcfg2/Server/Plugins/Svn2.py
+++ /dev/null
@@ -1,116 +0,0 @@
-""" The Svn2 plugin provides a revision interface for Bcfg2 repos using
-Subversion. It uses a pure python backend and exposes two additional
-XML-RPC methods. """
-
-import sys
-import Bcfg2.Server.Plugin
-try:
- import pysvn
- HAS_SVN = False
-except ImportError:
- HAS_SVN = True
-
-
-class Svn2(Bcfg2.Server.Plugin.Plugin,
- Bcfg2.Server.Plugin.Version):
- """Svn is a version plugin for dealing with Bcfg2 repos."""
- __author__ = 'bcfg-dev@mcs.anl.gov'
-
- conflicts = ['Svn']
- __rmi__ = Bcfg2.Server.Plugin.Plugin.__rmi__ + ['Update', 'Commit']
-
- def __init__(self, core, datastore):
- Bcfg2.Server.Plugin.Plugin.__init__(self, core, datastore)
- Bcfg2.Server.Plugin.Version.__init__(self, datastore)
-
- if HAS_SVN:
- msg = "Missing PySvn"
- self.logger.error("Svn2: " + msg)
- raise Bcfg2.Server.Plugin.PluginInitError(msg)
-
- self.client = pysvn.Client()
-
- self.svn_root = None
- self.revision = None
-
- # Read revision from bcfg2 repo
- revision = self.get_revision()
- if not self.revision:
- raise Bcfg2.Server.Plugin.PluginInitError("Failed to get revision")
-
- self.logger.debug("Initialized svn plugin with svn root %s at "
- "revision %s" % (self.svn_root, revision))
-
- def get_revision(self):
- """Read svn revision information for the Bcfg2 repository."""
- try:
- info = self.client.info(self.datastore)
- self.revision = info.revision
- self.svn_root = info.url
- return str(self.revision.number)
- except pysvn.ClientError: # pylint: disable=E1101
- self.logger.error("Svn2: Failed to get revision", exc_info=1)
- self.revision = None
- return str(-1)
-
- def Update(self):
- '''Svn2.Update() => True|False\nUpdate svn working copy\n'''
- try:
- old_revision = self.revision.number
- self.revision = self.client.update(self.datastore, recurse=True)[0]
- except pysvn.ClientError: # pylint: disable=E1101
- err = sys.exc_info()[1]
- # try to be smart about the error we got back
- details = None
- if "callback_ssl_server_trust_prompt" in str(err):
- details = "SVN server certificate is not trusted"
- elif "callback_get_login" in str(err):
- details = "SVN credentials not cached"
-
- if details is None:
- self.logger.error("Svn2: Failed to update server repository",
- exc_info=1)
- else:
- self.logger.error("Svn2: Failed to update server repository: "
- "%s" % details)
- return False
-
- if old_revision == self.revision.number:
- self.logger.debug("repository is current")
- else:
- self.logger.info("Updated %s from revision %s to %s" % \
- (self.datastore, old_revision, self.revision.number))
- return True
-
- def Commit(self):
- """Svn2.Commit() => True|False\nCommit svn repository\n"""
- # First try to update
- if not self.Update():
- self.logger.error("Failed to update svn repository, refusing to "
- "commit changes")
- return False
-
- try:
- self.revision = self.client.checkin([self.datastore],
- 'Svn2: autocommit',
- recurse=True)
- self.revision = self.client.update(self.datastore, recurse=True)[0]
- self.logger.info("Svn2: Commited changes. At %s" %
- self.revision.number)
- return True
- except pysvn.ClientError: # pylint: disable=E1101
- err = sys.exc_info()[1]
- # try to be smart about the error we got back
- details = None
- if "callback_ssl_server_trust_prompt" in str(err):
- details = "SVN server certificate is not trusted"
- elif "callback_get_login" in str(err):
- details = "SVN credentials not cached"
-
- if details is None:
- self.logger.error("Svn2: Failed to commit changes",
- exc_info=1)
- else:
- self.logger.error("Svn2: Failed to commit changes: %s" %
- details)
- return False
diff --git a/testsuite/pylintrc.conf b/testsuite/pylintrc.conf
index 829feaa26..63c2873ee 100644
--- a/testsuite/pylintrc.conf
+++ b/testsuite/pylintrc.conf
@@ -147,7 +147,7 @@ ignore-mixin-members=yes
# List of classes names for which member attributes should not be checked
# (useful for classes with attributes dynamically set).
-ignored-classes=ForeignKey,Interaction
+ignored-classes=ForeignKey,Interaction,git.cmd.Git
# When zope mode is activated, add a predefined set of Zope acquired attributes
# to generated-members.
diff --git a/tools/manpagegen/bcfg2.conf.5.ronn b/tools/manpagegen/bcfg2.conf.5.ronn
index ca216a433..f0359d0d3 100644
--- a/tools/manpagegen/bcfg2.conf.5.ronn
+++ b/tools/manpagegen/bcfg2.conf.5.ronn
@@ -122,6 +122,12 @@ specified in the `[server]` section of the configuration file.
* `group`:
The group name or GID to run the daemon as. Default is `0`
+ * `vcs_root`:
+ Specifies the path to the root of the VCS working copy that holds
+ your Bcfg2 specification, if it is different from `repository`.
+ E.g., if the VCS repository does not hold the bcfg2 data at the
+ top level, you may need to set this option.
+
### Account Plugin
The account plugin manages authentication data, including the following.