From e9e94e78bb52ca2ec36e3f3402dbde3f8289cdf3 Mon Sep 17 00:00:00 2001 From: Andrew Brestick Date: Thu, 31 Jul 2008 20:50:44 +0000 Subject: added support for alternate metadata plugins in bcfg2-admin git-svn-id: https://svn.mcs.anl.gov/repos/bcfg/trunk/bcfg2@4845 ce84e21b-d406-0410-9b95-82705330c041 --- src/lib/Server/Admin/Client.py | 76 ++++++++------------------------------ src/lib/Server/Admin/Viz.py | 21 +++++------ src/lib/Server/Admin/__init__.py | 31 +++++++++++++++- src/lib/Server/Plugin.py | 10 ++++- src/lib/Server/Plugins/BB.py | 58 ++++++++++++++++++++++++++++- src/lib/Server/Plugins/Metadata.py | 65 +++++++++++++++++++++++++++++++- 6 files changed, 183 insertions(+), 78 deletions(-) (limited to 'src/lib') diff --git a/src/lib/Server/Admin/Client.py b/src/lib/Server/Admin/Client.py index c51ac1598..1f784a4f2 100644 --- a/src/lib/Server/Admin/Client.py +++ b/src/lib/Server/Admin/Client.py @@ -1,25 +1,14 @@ -import lxml.etree -import fcntl import Bcfg2.Server.Admin +from Bcfg2.Server.Plugins.Metadata import MetadataConsistencyError -class Client(Bcfg2.Server.Admin.Mode): +class Client(Bcfg2.Server.Admin.MetadataCore): __shorthelp__ = 'bcfg2-admin client add attr1=val1 attr2=val2\nbcfg2-admin client del ' __longhelp__ = __shorthelp__ + '\n\tCreate or delete client entries' def __init__(self, configfile): - Bcfg2.Server.Admin.Mode.__init__(self, configfile) - try: - self.bcore = Bcfg2.Server.Core.Core(self.get_repo_path(), [], - [], [], 'foo', False, 'UTF-8') - except Bcfg2.Server.Core.CoreInitError, msg: - self.errExit("Core load failed because %s" % msg) - [self.bcore.fam.Service() for _ in range(5)] - while self.bcore.fam.Service(): - pass - self.tree = lxml.etree.parse(self.get_repo_path() + "/Metadata/clients.xml") - self.root = self.tree.getroot() + Bcfg2.Server.Admin.MetadataCore.__init__(self, configfile) def __call__(self, args): - Bcfg2.Server.Admin.Mode.__call__(self, args) + Bcfg2.Server.Admin.MetadataCore.__call__(self, args) if len(args) == 0: self.errExit("Client mode requires at least one argument: or ") if "-h" in args: @@ -30,55 +19,22 @@ class Client(Bcfg2.Server.Admin.Mode): attr_d = {} for i in args[2:]: attr, val = i.split('=', 1) - if attr not in ['profile', 'uuid', 'password', 'address', - 'secure', 'location']: + if attr not in ['profile', 'user', 'state', 'image', + 'action']: print "Attribute %s unknown" % attr raise SystemExit(1) attr_d[attr] = val - self.add_client(args[1], attr_d) + try: + self.metadata.add_client(args[1], attr_d) + except MetadataConsistencyError: + print "Error in adding client" + raise SystemExit(1) elif args[0] in ['delete', 'remove', 'del', 'rm']: - self.del_client(args[1]) + try: + self.metadata.remove_client(args[1]) + except MetadataConsistencyError: + print "Error in deleting client" + raise SystemExit(1) else: print "No command specified" raise SystemExit(1) - client_tree = open(self.get_repo_path() + "/Metadata/clients.xml","w") - fd = client_tree.fileno() - while True: - try: - fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) - except IOError: - continue - else: - break - self.tree.write(client_tree) - fcntl.lockf(fd, fcntl.LOCK_UN) - client_tree.close() - - def add_client(self, client, attrs): - '''add a new client''' - element = lxml.etree.Element("Client", name=client) - for key, val in attrs.iteritems(): - element.set(key, val) - node = self.search_client(client) - if node != None: - print "Client \"%s\" already exists" % (client) - raise SystemExit(1) - self.root.append(element) - - def del_client(self, client): - '''delete an existing client''' - node = self.search_client(client) - if node == None: - print "Client \"%s\" not found" % (client) - raise SystemExit(1) - self.root.remove(node) - - def search_client(self, client): - '''find a client''' - for node in self.root: - if node.attrib["name"] == client: - return node - for child in node: - if child.tag == "Alias" and child.attrib["name"] == client: - return node - return None diff --git a/src/lib/Server/Admin/Viz.py b/src/lib/Server/Admin/Viz.py index dfc8b3392..d2eeb3370 100644 --- a/src/lib/Server/Admin/Viz.py +++ b/src/lib/Server/Admin/Viz.py @@ -2,7 +2,7 @@ import getopt, popen2, lxml.etree import Bcfg2.Server.Admin -class Viz(Bcfg2.Server.Admin.Mode): +class Viz(Bcfg2.Server.Admin.MetadataCore): __shorthelp__ = '''bcfg2-admin viz [--includehosts] [--includebundles] [--includekey] [-o output.png] [--raw]''' __longhelp__ = __shorthelp__ + '\n\tProduce graphviz diagrams of metadata structures' @@ -11,10 +11,10 @@ class Viz(Bcfg2.Server.Admin.Mode): 'green1', 'blue1', 'yellow1', 'darkturquoise', 'gray66'] def __init__(self, cfile): - Bcfg2.Server.Admin.Mode.__init__(self, cfile) + Bcfg2.Server.Admin.MetadataCore.__init__(self, cfile) def __call__(self, args): - Bcfg2.Server.Admin.Mode.__call__(self, args) + Bcfg2.Server.Admin.MetadataCore.__call__(self, args) # First get options to the 'viz' subcommand try: opts, args = getopt.getopt(args, 'rhbko:', @@ -48,9 +48,7 @@ class Viz(Bcfg2.Server.Admin.Mode): def Visualize(self, repopath, raw=False, hosts=False, bundles=False, key=False, output=False): '''Build visualization of groups file''' - groupdata = lxml.etree.parse(repopath + '/Metadata/groups.xml') - groupdata.xinclude() - groups = groupdata.getroot() + groups = self.metadata.get_groups() if raw: cmd = "dd bs=4M" if output: @@ -78,13 +76,12 @@ class Viz(Bcfg2.Server.Admin.Mode): dotpipe.tochild.write('\trankdir="LR";\n') if hosts: - clients = lxml.etree.parse(repopath + \ - '/Metadata/clients.xml').getroot() - for client in clients.findall('Client'): - if instances.has_key(client.get('profile')): - instances[client.get('profile')].append(client.get('name')) + clients = self.metadata.clients + for client, profile in clients.iteritems(): + if instances.has_key(profile): + instances[profile].append(client) else: - instances[client.get('profile')] = [client.get('name')] + instances[profile] = [client] for profile, clist in instances.iteritems(): clist.sort() dotpipe.tochild.write( diff --git a/src/lib/Server/Admin/__init__.py b/src/lib/Server/Admin/__init__.py index cff9eec6a..a650f64e6 100644 --- a/src/lib/Server/Admin/__init__.py +++ b/src/lib/Server/Admin/__init__.py @@ -3,8 +3,8 @@ __revision__ = '$Revision$' __all__ = ['Mode', 'Client', 'Compare', 'Fingerprint', 'Init', 'Minestruct', 'Pull', 'Query', 'Tidy', 'Viz'] -import ConfigParser, lxml.etree, logging - +import ConfigParser, lxml.etree, logging, sys +import Bcfg2.Server.Core class ModeOperationError(Exception): pass @@ -46,3 +46,30 @@ class Mode(object): self.errExit("Could not find stats for client %s" % (client)) return hostent[0] +class MetadataCore(Mode): + '''Base class for admin-modes that handle metadata''' + def __init__(self, configfile): + Mode.__init__(self, configfile) + options = {'plugins': Bcfg2.Options.SERVER_PLUGINS, + 'structures': Bcfg2.Options.SERVER_STRUCTURES, + 'generators': Bcfg2.Options.SERVER_GENERATORS} + setup = Bcfg2.Options.OptionParser(options) + setup.parse(sys.argv[1:]) + plugins = [plugin for plugin in setup['plugins'] + if plugin in ('BB', 'Metadata')] + structures = [structure for structure in setup['structures'] + if structure in ('BB', 'Metadata')] + generators = [generator for generator in setup['generators'] + if generator in ('BB', 'Metadata')] + try: + self.bcore = Bcfg2.Server.Core.Core(self.get_repo_path(), plugins, + structures, generators, 'foo', False, 'UTF-8') + except Bcfg2.Server.Core.CoreInitError, msg: + self.errExit("Core load failed because %s" % msg) + [self.bcore.fam.Service() for _ in range(5)] + while self.bcore.fam.Service(): + pass + self.metadata = self.bcore.metadata + + def __call__(self, args): + Bcfg2.Server.Admin.Mode.__call__(self, args) diff --git a/src/lib/Server/Plugin.py b/src/lib/Server/Plugin.py index 0f9464a47..114e0b039 100644 --- a/src/lib/Server/Plugin.py +++ b/src/lib/Server/Plugin.py @@ -79,7 +79,15 @@ class StructurePlugin(Plugin): class MetadataPlugin(Plugin): '''Signal metadata capabilities for this plugin''' - pass + def add_client(self, client_name, attribs): + '''add client''' + pass + def remove_client(self, client_name): + '''remove client''' + pass + def get_groups(self): + '''get groups xml tree''' + pass class ProbingPlugin(Plugin): '''Signal probe capability for this plugin''' diff --git a/src/lib/Server/Plugins/BB.py b/src/lib/Server/Plugins/BB.py index af3f3d379..50badb7a3 100644 --- a/src/lib/Server/Plugins/BB.py +++ b/src/lib/Server/Plugins/BB.py @@ -2,8 +2,9 @@ import Bcfg2.Server.Plugin import lxml.etree -import sys, os +import os, fcntl from socket import gethostbyname +from Bcfg2.Server.Plugins.Metadata import MetadataConsistencyError # map of keywords to profiles # probably need a better way to do this @@ -44,6 +45,56 @@ class BB(Bcfg2.Server.Plugin.GeneratorPlugin, self.nodes = {} self.dhcpd_loaded = False self.need_update = False + + def get_groups(self): + '''get groups xml tree''' + groups_tree = lxml.etree.parse(self.data + "/groups.xml") + root = groups_tree.getroot() + return root + + def remove_client(self, client_name): + '''Remove client from bb.xml''' + bb_tree = lxml.etree.parse(self.data + "/bb.xml") + root = bb_tree.getroot() + if DOMAIN_SUFFIX in client_name: + client_name = client_name.split('.')[0] + if len(root.xpath(".//Node[@name='%s']" % client_name)) != 1: + self.logger.error("Client \"%s\" does not exist" % client_name) + raise MetadataConsistencyError + else: + root.remove(root.xpath(".//Node[@name='%s']" % client_name)[0]) + self.write_metadata(bb_tree) + + def add_client(self, client_name, attribs): + '''Add a client to bb.xml''' + bb_tree = lxml.etree.parse(self.data + "/bb.xml") + root = bb_tree.getroot() + if DOMAIN_SUFFIX in client_name: + client_name = client_name.split('.')[0] + if len(root.xpath(".//Node[@name='%s']" % client_name)) != 0: + self.logger.error("Client \"%s\" already exists" % client_name) + raise MetadataConsistencyError + else: + element = lxml.etree.Element("Client", name=client_name) + for key, val in attribs.iteritems(): + element.set(key, val) + root.append(element) + self.write_metadata(bb_tree) + + def write_metadata(self, tree): + '''write metadata back to bb.xml''' + data_file = open(self.data + "/bb.xml","w") + fd = data_file.fileno() + while True: + try: + fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + continue + else: + break + tree.write(data_file) + fcntl.lockf(fd, fcntl.LOCK_UN) + data_file.close() def gen_dhcpd(self, entry, metadata): '''Generate dhcpd.conf to serve to dhcp server''' @@ -149,7 +200,7 @@ class BB(Bcfg2.Server.Plugin.GeneratorPlugin, if node.attrib['name'] == metadata.hostname.split('.')[0]: node.attrib['action'] = action break - bb_tree.write("%s/%s" % (self.data, 'bb.xml')) + self.write_metadata(bb_tree) return bundles def HandleEvent(self, event=None): @@ -209,6 +260,9 @@ class BB(Bcfg2.Server.Plugin.GeneratorPlugin, self.nodes[host] = node_dict # update symlinks and /etc/dhcp3/dhcpd.conf if self.write_to_disk: + if not node_dict.has_key('mac'): + self.logger.error("no mac address for %s" % host) + continue mac = node_dict['mac'].replace(':','-').lower() linkname = "/tftpboot/pxelinux.cfg/01-%s" % (mac) try: diff --git a/src/lib/Server/Plugins/Metadata.py b/src/lib/Server/Plugins/Metadata.py index 345f14b54..d28d75530 100644 --- a/src/lib/Server/Plugins/Metadata.py +++ b/src/lib/Server/Plugins/Metadata.py @@ -121,7 +121,70 @@ class Metadata(Bcfg2.Server.Plugin.MetadataPlugin, self.extra = {'groups.xml':[], 'clients.xml':[]} self.password = core.password self.load_probedata() - + + def get_groups(self): + '''return groups xml tree''' + groups_tree = lxml.etree.parse(self.data + "/groups.xml") + root = groups_tree.getroot() + return root + + def search_client(self, client_name, tree): + '''find a client''' + for node in tree: + if node.attrib["name"] == client_name: + return node + for child in node: + if child.tag == "Alias" and child.attrib["name"] == client_name: + return node + return None + + def add_client(self, client_name, attribs): + '''add client to clients.xml''' + tree = lxml.etree.parse(self.data + "/clients.xml") + root = tree.getroot() + element = lxml.etree.Element("Client", name=client_name) + for key, val in attribs.iteritems(): + element.set(key, val) + node = self.search_client(client_name, root) + if node != None: + self.logger.error("Client \"%s\" already exists" % (client_name)) + raise MetadataConsistencyError + root.append(element) + client_tree = open(self.data + "/clients.xml","w") + fd = client_tree.fileno() + while True: + try: + fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + continue + else: + break + tree.write(client_tree) + fcntl.lockf(fd, fcntl.LOCK_UN) + client_tree.close() + + def remove_client(self, client_name): + '''Remove a client''' + tree = lxml.etree.parse(self.data + "/clients.xml") + root = tree.getroot() + node = self.search_client(client_name, root) + if node == None: + self.logger.error("Client \"%s\" not found" % (client_name)) + raise MetadataConsistencyError + root.remove(node) + client_tree = open(self.data + "/clients.xml","w") + fd = client_tree.fileno() + while True: + try: + fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError: + continue + else: + break + tree.write(client_tree) + fcntl.lockf(fd, fcntl.LOCK_UN) + client_tree.close() + def HandleEvent(self, event): '''Handle update events for data files''' filename = event.filename.split('/')[-1] -- cgit v1.2.3-1-g7c22