summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--doc/help/troubleshooting.txt4
-rw-r--r--doc/server/plugins/generators/rules.txt2
-rw-r--r--man/bcfg2-info.84
-rw-r--r--osx/Makefile6
-rw-r--r--redhat/systemd/bcfg2-server.service13
-rw-r--r--redhat/systemd/bcfg2.service13
-rw-r--r--schemas/bundle.xsd213
-rw-r--r--schemas/pkgtype.xsd1
-rw-r--r--src/lib/Server/Admin/Compare.py159
-rw-r--r--src/lib/Server/Admin/Viz.py28
-rw-r--r--src/lib/Server/Plugin.py2
-rw-r--r--src/lib/Server/Plugins/Metadata.py41
-rwxr-xr-xsrc/sbin/bcfg2-info7
-rwxr-xr-xtools/export2.py18
14 files changed, 296 insertions, 215 deletions
diff --git a/doc/help/troubleshooting.txt b/doc/help/troubleshooting.txt
index 1cd61a8f3..cb68a02c5 100644
--- a/doc/help/troubleshooting.txt
+++ b/doc/help/troubleshooting.txt
@@ -200,8 +200,8 @@ Server Errors
| Packages: No matching | Server | None of the sources | [s12]_ |
| sources for client | | defined in the | |
| <clientname>; improper group | | Package plugin's | |
-| memberships? | | ``config.xml``apply | |
-| | | to the client. | |
+| memberships? | | ``config.xml`` | |
+| | | apply to the client | |
+------------------------------+----------+---------------------+--------------+
diff --git a/doc/server/plugins/generators/rules.txt b/doc/server/plugins/generators/rules.txt
index a2953ad08..05baa2dbb 100644
--- a/doc/server/plugins/generators/rules.txt
+++ b/doc/server/plugins/generators/rules.txt
@@ -97,8 +97,6 @@ The Package Tag may have the following attributes:
| verify | verify='false' - do not do package | String |
| | verification | |
+------------+----------------------------------------------+--------+
-| reloc | RPM relocation path. | String |
-+------------+----------------------------------------------+--------+
| multiarch | Comma separated list of the architectures of | String |
| | this package that should be installed. | |
+------------+----------------------------------------------+--------+
diff --git a/man/bcfg2-info.8 b/man/bcfg2-info.8
index a97f60c40..fe20d5d6c 100644
--- a/man/bcfg2-info.8
+++ b/man/bcfg2-info.8
@@ -83,10 +83,6 @@ Shell out to native python interpreter.
.RS
Display filesystem events as they are processed.
.RE
-.B generators
-.RS
-List current versions of generators.
-.RE
.B groups
.RS
List groups
diff --git a/osx/Makefile b/osx/Makefile
index 075628668..8d16b42ae 100644
--- a/osx/Makefile
+++ b/osx/Makefile
@@ -1,6 +1,6 @@
-PYVERSION := $(shell /usr/bin/python -c "import sys; print sys.version[0:3]")
-PYMAJORVERSION := $(shell /usr/bin/python -c "import sys; print sys.version[0:1]")
-PYMINORVERSION := $(shell /usr/bin/python -c "import sys; print sys.version[2:3]")
+PYVERSION := $(shell /usr/bin/python -c "import sys; print '%s.%s' % (sys.version_info[0], sys.version_info[1])"python -c "import sys; print sys.version[0:3]")
+PYMAJORVERSION := $(shell /usr/bin/python -c "import sys; print sys.version_info[0]")
+PYMINORVERSION := $(shell /usr/bin/python -c "import sys; print sys.version_info[1]")
PREFLIGHT = preflight
POSTFLIGHT = postflight
PKGROOT = bcfg2pkg
diff --git a/redhat/systemd/bcfg2-server.service b/redhat/systemd/bcfg2-server.service
new file mode 100644
index 000000000..641b02ec6
--- /dev/null
+++ b/redhat/systemd/bcfg2-server.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Bcfg2 configuration daemon
+After=syslog.target network.target
+
+[Service]
+Type=forking
+StandardOutput=syslog
+StandardError=syslog
+EnvironmentFile=-/etc/sysconfig/bcfg2
+ExecStart=/usr/bin/bcfg2-server $OPTIONS
+
+[Install]
+WantedBy=multi-user.target
diff --git a/redhat/systemd/bcfg2.service b/redhat/systemd/bcfg2.service
new file mode 100644
index 000000000..a0c3afd81
--- /dev/null
+++ b/redhat/systemd/bcfg2.service
@@ -0,0 +1,13 @@
+[Unit]
+Description=Bcfg2 configuration client
+After=syslog.target network.target
+
+[Service]
+Type=forking
+StandardOutput=syslog
+StandardError=syslog
+EnvironmentFile=-/etc/sysconfig/bcfg2
+ExecStart=/usr/bin/bcfg2 $OPTIONS
+
+[Install]
+WantedBy=multi-user.target
diff --git a/schemas/bundle.xsd b/schemas/bundle.xsd
index c0a7e08ac..2dd77e9af 100644
--- a/schemas/bundle.xsd
+++ b/schemas/bundle.xsd
@@ -110,6 +110,14 @@
</xsd:documentation>
</xsd:annotation>
</xsd:element>
+ <xsd:element name='Bundle' type='BundleType'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Nesting Bundle tags is allowed in order to support
+ XInclude within Bundles.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
<xsd:element ref="py:def"/>
<xsd:element ref="py:match"/>
<xsd:element ref="py:choose"/>
@@ -135,7 +143,113 @@
<xsd:attributeGroup ref="py:genshiAttrs"/>
</xsd:complexType>
- <xsd:element name='Bundle'>
+ <xsd:complexType name='BundleType'>
+ <xsd:choice minOccurs='0' maxOccurs='unbounded'>
+ <xsd:element name='Package' type='StructureEntry'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract implementation of a Package entry. The full
+ specification will be included in Rules.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='Path' type='PathEntry'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract implementation of a Path entry. The entry will
+ either be handled by Cfg, TGenshi, or another
+ DirectoryBacked plugin; or handled by Rules, in which case
+ the full specification of this entry will be included in
+ Rules.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='Service' type='StructureEntry'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract implementation of a Service entry. The full
+ specification will be included in Rules.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='Action' type='StructureEntry'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Abstract implementation of an Action entry. The full
+ specification will be included in Rules.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='BoundPackage' type='PackageType'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Fully bound description of a software package to be managed.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='BoundPath' type='BoundPathEntry'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Fully bound description of a filesystem path to be handled
+ by the POSIX driver.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='BoundService' type='ServiceType'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Fully bound description of a system service to be managed.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='BoundAction' type='ActionType'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Fully bound description of a command to be run.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='Group' type='GroupType'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Elements within Group tags only apply to clients that are
+ members of that group
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='Client' type='GroupType'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Elements within Client tags only apply to the named client
+ (or vice-versa; see #element_negate below)
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element name='Bundle' type='BundleType'>
+ <xsd:annotation>
+ <xsd:documentation>
+ Nesting Bundle tags is allowed in order to support
+ XInclude within Bundles.
+ </xsd:documentation>
+ </xsd:annotation>
+ </xsd:element>
+ <xsd:element ref="py:def"/>
+ <xsd:element ref="py:match"/>
+ <xsd:element ref="py:choose"/>
+ <xsd:element ref="py:for"/>
+ <xsd:element ref="py:if"/>
+ <xsd:element ref="py:with"/>
+ <xsd:element ref="py:replace"/>
+ </xsd:choice>
+ <xsd:attribute type='xsd:string' name='description' />
+ <xsd:attribute type='xsd:string' name='name'/>
+ <xsd:attribute type='xsd:string' name='version'/>
+ <xsd:attribute type='xsd:string' name='origin'/>
+ <xsd:attribute type='xsd:string' name='revision'/>
+ <xsd:attributeGroup ref="py:genshiAttrs"/>
+ </xsd:complexType>
+
+ <xsd:element name='Bundle' type='BundleType'>
<xsd:annotation>
<xsd:documentation>
A bundle describes a group of inter-dependent configuration
@@ -150,102 +264,5 @@
that a given client will receive.
</xsd:documentation>
</xsd:annotation>
- <xsd:complexType>
- <xsd:choice minOccurs='0' maxOccurs='unbounded'>
- <xsd:element name='Package' type='StructureEntry'>
- <xsd:annotation>
- <xsd:documentation>
- Abstract implementation of a Package entry. The full
- specification will be included in Rules.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element name='Path' type='PathEntry'>
- <xsd:annotation>
- <xsd:documentation>
- Abstract implementation of a Path entry. The entry will
- either be handled by Cfg, TGenshi, or another
- DirectoryBacked plugin; or handled by Rules, in which case
- the full specification of this entry will be included in
- Rules.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element name='Service' type='StructureEntry'>
- <xsd:annotation>
- <xsd:documentation>
- Abstract implementation of a Service entry. The full
- specification will be included in Rules.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element name='Action' type='StructureEntry'>
- <xsd:annotation>
- <xsd:documentation>
- Abstract implementation of an Action entry. The full
- specification will be included in Rules.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element name='BoundPackage' type='PackageType'>
- <xsd:annotation>
- <xsd:documentation>
- Fully bound description of a software package to be managed.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element name='BoundPath' type='BoundPathEntry'>
- <xsd:annotation>
- <xsd:documentation>
- Fully bound description of a filesystem path to be handled
- by the POSIX driver.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element name='BoundService' type='ServiceType'>
- <xsd:annotation>
- <xsd:documentation>
- Fully bound description of a system service to be managed.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element name='BoundAction' type='ActionType'>
- <xsd:annotation>
- <xsd:documentation>
- Fully bound description of a command to be run.
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element name='Group' type='GroupType'>
- <xsd:annotation>
- <xsd:documentation>
- Elements within Group tags only apply to clients that are
- members of that group
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element name='Client' type='GroupType'>
- <xsd:annotation>
- <xsd:documentation>
- Elements within Client tags only apply to the named client
- (or vice-versa; see #element_negate below)
- </xsd:documentation>
- </xsd:annotation>
- </xsd:element>
- <xsd:element ref="py:def"/>
- <xsd:element ref="py:match"/>
- <xsd:element ref="py:choose"/>
- <xsd:element ref="py:for"/>
- <xsd:element ref="py:if"/>
- <xsd:element ref="py:with"/>
- <xsd:element ref="py:replace"/>
- </xsd:choice>
- <xsd:attribute type='xsd:string' name='description' />
- <xsd:attribute type='xsd:string' name='name'/>
- <xsd:attribute type='xsd:string' name='version'/>
- <xsd:attribute type='xsd:string' name='origin'/>
- <xsd:attribute type='xsd:string' name='revision'/>
- <xsd:attributeGroup ref="py:genshiAttrs"/>
- </xsd:complexType>
</xsd:element>
</xsd:schema>
diff --git a/schemas/pkgtype.xsd b/schemas/pkgtype.xsd
index ad63cd9d2..70a466448 100644
--- a/schemas/pkgtype.xsd
+++ b/schemas/pkgtype.xsd
@@ -43,7 +43,6 @@
<xsd:attribute type='xsd:string' name='file'/>
<xsd:attribute type='xsd:string' name='verify'/>
<xsd:attribute type='xsd:string' name='simplefile'/>
- <xsd:attribute type='xsd:string' name='reloc'/>
<xsd:attribute type='xsd:string' name='multiarch'/>
<xsd:attribute type='xsd:string' name='srcs'/>
<xsd:attribute type='xsd:string' name='type'/>
diff --git a/src/lib/Server/Admin/Compare.py b/src/lib/Server/Admin/Compare.py
index 4c751b55a..82d0d690c 100644
--- a/src/lib/Server/Admin/Compare.py
+++ b/src/lib/Server/Admin/Compare.py
@@ -14,62 +14,64 @@ class Compare(Bcfg2.Server.Admin.Mode):
def __init__(self, configfile):
Bcfg2.Server.Admin.Mode.__init__(self, configfile)
- self.important = {'Package': ['name', 'version'],
- 'Service': ['name', 'status'],
- 'Directory': ['name', 'owner', 'group', 'perms'],
- 'SymLink': ['name', 'to'],
- 'ConfigFile': ['name', 'owner', 'group', 'perms'],
- 'Permissions': ['name', 'perms'],
- 'PostInstall': ['name']}
+ self.important = {'Path': ['name', 'type', 'owner', 'group', 'perms',
+ 'important', 'paranoid', 'sensitive',
+ 'dev_type', 'major', 'minor', 'prune',
+ 'encoding', 'empty', 'to', 'recursive',
+ 'vcstype', 'sourceurl', 'revision'],
+ 'Package': ['name', 'type', 'version', 'simplefile',
+ 'verify'],
+ 'Service': ['name', 'type', 'status', 'mode',
+ 'target', 'sequence', 'parameters'],
+ 'Action': ['name', 'timing', 'when', 'status',
+ 'command'],
+ 'PostInstall': ['name']
+ }
def compareStructures(self, new, old):
+ if new.tag == 'Independent':
+ bundle = 'Base'
+ else:
+ bundle = new.get('name')
+
+ identical = True
+
for child in new.getchildren():
- equiv = old.xpath('%s[@name="%s"]' %
- (child.tag, child.get('name')))
- if child.tag in self.important:
- print("tag type %s not handled" % (child.tag))
+ if child.tag not in self.important:
+ print(" %s in (new) bundle %s:\n tag type not handled!" %
+ (child.tag, bundle))
continue
+ equiv = old.xpath('%s[@name="%s"]' %
+ (child.tag, child.get('name')))
if len(equiv) == 0:
- print("didn't find matching %s %s" %
- (child.tag, child.get('name')))
+ print(" %s %s in bundle %s:\n only in new configuration" %
+ (child.tag, child.get('name'), bundle))
+ identical = False
continue
- elif len(equiv) >= 1:
- if child.tag == 'ConfigFile':
- if child.text != equiv[0].text:
- print(" %s %s contents differ" \
- % (child.tag, child.get('name')))
- continue
- noattrmatch = [field for field in self.important[child.tag] if \
- child.get(field) != equiv[0].get(field)]
- if not noattrmatch:
- new.remove(child)
- old.remove(equiv[0])
- else:
- print(" %s %s attributes %s do not match" % \
- (child.tag, child.get('name'), noattrmatch))
- if len(old.getchildren()) == 0 and len(new.getchildren()) == 0:
- return True
- if new.tag == 'Independent':
- name = 'Base'
- else:
- name = new.get('name')
- both = []
- oldl = ["%s %s" % (entry.tag, entry.get('name')) for entry in old]
- newl = ["%s %s" % (entry.tag, entry.get('name')) for entry in new]
- for entry in newl:
- if entry in oldl:
- both.append(entry)
- newl.remove(entry)
- oldl.remove(entry)
- for entry in both:
- print(" %s differs (in bundle %s)" % (entry, name))
- for entry in oldl:
- print(" %s only in old configuration (in bundle %s)" % (entry,
- name))
- for entry in newl:
- print(" %s only in new configuration (in bundle %s)" % (entry,
- name))
- return False
+ diff = []
+ if child.tag == 'Path' and child.get('type') == 'file' and \
+ child.text != equiv[0].text:
+ diff.append('contents')
+ attrdiff = [field for field in self.important[child.tag] if \
+ child.get(field) != equiv[0].get(field)]
+ if attrdiff:
+ diff.append('attributes (%s)' % ', '.join(attrdiff))
+ if diff:
+ print(" %s %s in bundle %s:\n %s differ" % (child.tag, \
+ child.get('name'), bundle, ' and '.join(diff)))
+ identical = False
+
+ for child in old.getchildren():
+ if child.tag not in self.important:
+ print(" %s in (old) bundle %s:\n tag type not handled!" %
+ (child.tag, bundle))
+ elif len(new.xpath('%s[@name="%s"]' %
+ (child.tag, child.get('name')))) == 0:
+ print(" %s %s in bundle %s:\n only in old configuration" %
+ (child.tag, child.get('name'), bundle))
+ identical = False
+
+ return identical
def compareSpecifications(self, path1, path2):
try:
@@ -89,30 +91,30 @@ class Compare(Bcfg2.Server.Admin.Mode):
if bundle.get('name')[-4:] == '.xml':
bundle.set('name', bundle.get('name')[:-4])
- rcs = []
+ identical = True
+
+ for bundle in old.findall('./Bundle'):
+ if len(new.xpath('Bundle[@name="%s"]' % (bundle.get('name')))) == 0:
+ print(" Bundle %s only in old configuration" %
+ bundle.get('name'))
+ identical = False
for bundle in new.findall('./Bundle'):
equiv = old.xpath('Bundle[@name="%s"]' % (bundle.get('name')))
if len(equiv) == 0:
- print("couldnt find matching bundle for %s" % bundle.get('name'))
- continue
- if len(equiv) == 1:
- if self.compareStructures(bundle, equiv[0]):
- new.remove(bundle)
- old.remove(equiv[0])
- rcs.append(True)
- else:
- rcs.append(False)
- else:
- print("Unmatched bundle %s" % (bundle.get('name')))
- rcs.append(False)
- i1 = new.find('./Independent')
- i2 = old.find('./Independent')
- if self.compareStructures(i1, i2):
- new.remove(i1)
- old.remove(i2)
- else:
- rcs.append(False)
- return False not in rcs
+ print(" Bundle %s only in new configuration" %
+ bundle.get('name'))
+ identical = False
+ elif not self.compareStructures(bundle, equiv[0]):
+ identical = False
+
+ i1 = lxml.etree.Element('Independent')
+ i2 = lxml.etree.Element('Independent')
+ i1.extend(new.findall('./Independent/*'))
+ i2.extend(old.findall('./Independent/*'))
+ if not self.compareStructures(i1, i2):
+ identical = False
+
+ return identical
def __call__(self, args):
Bcfg2.Server.Admin.Mode.__call__(self, args)
@@ -124,19 +126,26 @@ class Compare(Bcfg2.Server.Admin.Mode):
args.remove('-r')
(oldd, newd) = args
(old, new) = [os.listdir(spot) for spot in args]
+ old_extra = []
for item in old:
- print("Entry:", item)
+ if item not in new:
+ old_extra.append(item)
+ continue
+ print("File: %s" % item)
state = self.__call__([oldd + '/' + item, newd + '/' + item])
new.remove(item)
if state:
- print("Entry:", item, "good")
+ print("File %s is good" % item)
else:
- print("Entry:", item, "bad")
+ print("File %s is bad" % item)
if new:
- print("new has extra entries", new)
+ print("%s has extra files: %s" % (newd, ', '.join(new)))
+ if old_extra:
+ print("%s has extra files: %s" % (oldd, ', '.join(old_extra)))
return
try:
(old, new) = args
+ return self.compareSpecifications(new, old)
except IndexError:
print(self.__call__.__doc__)
raise SystemExit(1)
diff --git a/src/lib/Server/Admin/Viz.py b/src/lib/Server/Admin/Viz.py
index f39e6d7a8..bd7c6dd05 100644
--- a/src/lib/Server/Admin/Viz.py
+++ b/src/lib/Server/Admin/Viz.py
@@ -1,5 +1,6 @@
import getopt
from subprocess import Popen, PIPE
+import sys
import Bcfg2.Server.Admin
@@ -8,18 +9,22 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
__shorthelp__ = "Produce graphviz diagrams of metadata structures"
__longhelp__ = (__shorthelp__ + "\n\nbcfg2-admin viz [--includehosts] "
"[--includebundles] [--includekey] "
+ "[--only-client clientname] "
"[-o output.png] [--raw]\n")
__usage__ = ("bcfg2-admin viz [options]\n\n"
- " %-25s%s\n"
- " %-25s%s\n"
- " %-25s%s\n"
- " %-25s%s\n" %
+ " %-32s%s\n"
+ " %-32s%s\n"
+ " %-32s%s\n"
+ " %-32s%s\n"
+ " %-32s%s\n" %
("-H, --includehosts",
"include hosts in the viz output",
"-b, --includebundles",
"include bundles in the viz output",
"-k, --includekey",
"show a key for different digraph shapes",
+ "-c, --only-client <clientname>",
+ "show only the groups, bundles for the named client",
"-o, --outfile <file>",
"write viz output to an output file"))
@@ -42,18 +47,21 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
Bcfg2.Server.Admin.MetadataCore.__call__(self, args)
# First get options to the 'viz' subcommand
try:
- opts, args = getopt.getopt(args, 'Hbko:',
+ opts, args = getopt.getopt(args, 'Hbkc:o:',
['includehosts', 'includebundles',
- 'includekey', 'outfile='])
+ 'includekey', 'only-client=', 'outfile='])
except getopt.GetoptError:
msg = sys.exc_info()[1]
print(msg)
+ print(self.__longhelp__)
+ raise SystemExit(1)
#FIXME: is this for --raw?
#rset = False
hset = False
bset = False
kset = False
+ only_client = None
outputfile = False
for opt, arg in opts:
if opt in ("-H", "--includehosts"):
@@ -62,16 +70,18 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
bset = True
elif opt in ("-k", "--includekey"):
kset = True
+ elif opt in ("-c", "--only-client"):
+ only_client = arg
elif opt in ("-o", "--outfile"):
outputfile = arg
data = self.Visualize(self.get_repo_path(), hset, bset,
- kset, outputfile)
+ kset, only_client, outputfile)
print(data)
raise SystemExit(0)
def Visualize(self, repopath, hosts=False,
- bundles=False, key=False, output=False):
+ bundles=False, key=False, only_client=None, output=False):
"""Build visualization of groups file."""
if output:
format = output.split('.')[-1]
@@ -90,7 +100,7 @@ class Viz(Bcfg2.Server.Admin.MetadataCore):
raise SystemExit(1)
dotpipe.stdin.write('\trankdir="LR";\n')
dotpipe.stdin.write(self.metadata.viz(hosts, bundles,
- key, self.colors))
+ key, only_client, self.colors))
if key:
dotpipe.stdin.write("\tsubgraph cluster_key {\n")
dotpipe.stdin.write('''\tstyle="filled";\n''')
diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py
index 3841e637d..a05c537e5 100644
--- a/src/lib/Server/Plugin.py
+++ b/src/lib/Server/Plugin.py
@@ -927,7 +927,7 @@ class GroupSpool(Plugin, Generator):
if not posixpath.isdir(epath):
# do not pass through directory events
self.entries[ident].handle_event(event)
- if action == 'changed':
+ if action == 'changed' and ident in self.entries:
self.entries[ident].handle_event(event)
elif action == 'deleted':
fbase = self.handles[event.requestID] + event.filename
diff --git a/src/lib/Server/Plugins/Metadata.py b/src/lib/Server/Plugins/Metadata.py
index 6570f2912..7fc34f178 100644
--- a/src/lib/Server/Plugins/Metadata.py
+++ b/src/lib/Server/Plugins/Metadata.py
@@ -782,8 +782,20 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
xdict['xquery'][0].set('auth', 'cert')
self.clients_xml.write_xml(xdict['filename'], xdict['xmltree'])
- def viz(self, hosts, bundles, key, colors):
+ def viz(self, hosts, bundles, key, only_client, colors):
"""Admin mode viz support."""
+ if only_client:
+ clientmeta = self.core.build_metadata(only_client)
+
+ def include_client(client):
+ return not only_client or client != only_client
+
+ def include_bundle(bundle):
+ return not only_client or bundle in clientmeta.bundles
+
+ def include_group(group):
+ return not only_client or group in clientmeta.groups
+
groups_tree = lxml.etree.parse(self.data + "/groups.xml")
try:
groups_tree.xinclude()
@@ -791,7 +803,6 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
self.logger.error("Failed to process XInclude for file %s" % dest)
groups = groups_tree.getroot()
categories = {'default': 'grey83'}
- instances = {}
viz_str = ""
egroups = groups.findall("Group") + groups.findall('.//Groups/Group')
for group in egroups:
@@ -801,8 +812,11 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
if None in categories:
del categories[None]
if hosts:
+ instances = {}
clients = self.clients
for client, profile in list(clients.items()):
+ if include_client(client):
+ continue
if profile in instances:
instances[profile].append(client)
else:
@@ -817,7 +831,8 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
bundles = []
[bundles.append(bund.get('name')) \
for bund in groups.findall('.//Bundle') \
- if bund.get('name') not in bundles]
+ if bund.get('name') not in bundles \
+ and include_bundle(bund.get('name'))]
bundles.sort()
for bundle in bundles:
viz_str += '''\t"bundle-%s" [ label="%s", shape="septagon"];\n''' \
@@ -829,20 +844,22 @@ class Metadata(Bcfg2.Server.Plugin.Plugin,
else:
style = "filled"
gseen.append(group.get('name'))
- viz_str += '\t"group-%s" [label="%s", style="%s", fillcolor=%s];\n' % \
- (group.get('name'), group.get('name'), style, group.get('color'))
- if bundles:
- for bundle in group.findall('Bundle'):
- viz_str += '\t"group-%s" -> "bundle-%s";\n' % \
- (group.get('name'), bundle.get('name'))
+ if include_group(group.get('name')):
+ viz_str += '\t"group-%s" [label="%s", style="%s", fillcolor=%s];\n' % \
+ (group.get('name'), group.get('name'), style, group.get('color'))
+ if bundles:
+ for bundle in group.findall('Bundle'):
+ viz_str += '\t"group-%s" -> "bundle-%s";\n' % \
+ (group.get('name'), bundle.get('name'))
gfmt = '\t"group-%s" [label="%s", style="filled", fillcolor="grey83"];\n'
for group in egroups:
for parent in group.findall('Group'):
- if parent.get('name') not in gseen:
+ if parent.get('name') not in gseen and include_group(parent.get('name')):
viz_str += gfmt % (parent.get('name'), parent.get('name'))
gseen.append(parent.get("name"))
- viz_str += '\t"group-%s" -> "group-%s" ;\n' % \
- (group.get('name'), parent.get('name'))
+ if include_group(group.get('name')):
+ viz_str += '\t"group-%s" -> "group-%s" ;\n' % \
+ (group.get('name'), parent.get('name'))
if key:
for category in categories:
viz_str += '''\t"''' + category + '''" [label="''' + category + \
diff --git a/src/sbin/bcfg2-info b/src/sbin/bcfg2-info
index 07953ae69..951a5df58 100755
--- a/src/sbin/bcfg2-info
+++ b/src/sbin/bcfg2-info
@@ -37,7 +37,6 @@ clients - Print out client/profile information
config - Print out the configuration of the Bcfg2 server
debug - Shell out to native python interpreter
event_debug - Display filesystem events as they are processed
-generators - List current versions of generators
groups - List groups
help - Print this list of available commands
mappings <type*> <name*> - Print generator mappings for optional type and name
@@ -311,12 +310,6 @@ class infoCore(cmd.Cmd, Bcfg2.Server.Core.Core):
]
printTabular(output)
-
- def do_generators(self, _):
- """Print out generator info."""
- for generator in self.generators:
- print(generator.__version__)
-
def do_showentries(self, args):
"""Show abstract configuration entries for a given host."""
arglen = len(args.split())
diff --git a/tools/export2.py b/tools/export2.py
index 0b8cc159d..567d4b227 100755
--- a/tools/export2.py
+++ b/tools/export2.py
@@ -72,10 +72,17 @@ def main(argv=None):
help = 'run in debun mode',
default = False,
dest = 'debug')
+ p.add_option('--paranoid', '-P',
+ action = 'store_true',
+ help = 'run in paranoid mode, make changes but do not commit to repository',
+ default = False,
+ dest = 'paranoid')
options, arguments = p.parse_args()
if options.debug:
print options
+ print "What should debug mode do?"
+ quit()
# py3k compatibility
try:
@@ -180,6 +187,8 @@ def main(argv=None):
#FIXME: do this using python-dulwich
commando = {}
+ commando["vcs_diff"] = "git diff"
+
commando["vcs_commit"] = "git commit -asm 'Version bump to %s'" % version
# NOTE: This will use the default email address key. If you want to sign the tag
@@ -196,13 +205,20 @@ def main(argv=None):
commando["scp_archive"] = "scp %s* terra.mcs.anl.gov:/mcs/ftp/pub/bcfg/" % tarname
# Execute the commands
- commando_orders = ["vcs_commit","vcs_tag","create_archive","gpg_encrypt","scp_archive"]
+ if options.paranoid:
+ commando_orders = ["vcs_diff"]
+ else:
+ commando_orders = ["vcs_commit","vcs_tag","create_archive","gpg_encrypt","scp_archive"]
+
if options.dryrun:
for cmd in commando_orders:
print "*** dry-run: %s" % commando[cmd]
else:
for cmd in commando_orders:
output = run(commando[cmd])[0].strip()
+ if options.verbose:
+ print output
+ print "Ran '%s' with above output." % cmd
if __name__ == '__main__':
sys.exit(main())