diff --git a/README.md b/README.md index 1e3ade7..330aef5 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,3 @@ -# Puppet ENC Using Mongodb +THIS PROJECT HAS BEEN MOVED TO https://github.com/drwahl/yape.git -## Documentation - -Please refer to the [Wiki](https://github.com/bcarpio/mongodb-enc/wiki). - - -## Project Status - -This project is still in **Alpha**. This means at any given time the code is under active development. There is still one big change coming where I change the mongodb schema. I currently don't like how I have the mongodb laid out. If you are using this code and would like to make feature requests please do so in the issue tracker. - -## Author Info - -Website: [Brian Carpio](http://www.briancarpio.com) - -Twitter: [Linsys](https://twitter.com/linsys) \ No newline at end of file +This originally started as a fork of bcarpio/mongodb-enc, however it has turned in to a complete re-write. The only thing that has been kept is the basic schema. Since the two code bases no longer share any code in common, I decided to break off the fork and create an entirely new project with a new name. All further development will be completed at https://github.com/drwahl/yape.git. diff --git a/conf/conf.ini b/conf/conf.ini index c0655bf..d8e4f74 100644 --- a/conf/conf.ini +++ b/conf/conf.ini @@ -1,4 +1,4 @@ [mongodb_info] -mongodb_servers = arch-mongod-s1-01, arch-mongod-s1-02, arch-mongod-s1-03, arch-mongod-s1-04 +mongodb_server = localhost mongodb_db_name = instances mongodb_collection_name = puppet_enc diff --git a/packaging/rpm/mongodb-enc.spec b/packaging/rpm/mongodb-enc.spec new file mode 100644 index 0000000..28a0164 --- /dev/null +++ b/packaging/rpm/mongodb-enc.spec @@ -0,0 +1,47 @@ +Name: mongodb-enc +Version: 0.1 +Release: 1%{dist} +Summary: MongoDB driven External Node Classifier (ENC) +License: GPLv3 +URL: https://github.com/drwahl/mongodb-enc +Group: System Environment/Base +Source0: %{name}-%{version}.tar.gz +BuildRoot: %{_tmppath}/%{name}-%{version}-root-%(%{__id_u} -n) +BuildArch: noarch + +Requires: python +Requires: pymongo +Requires: PyYAML +Requires: python-argparse + +%description +A set of scripts which can be used to leverage mongodb as an external node +classifier for puppet. + +%prep +%setup -q -n %{name} + +%install +rm -rf %{buildroot} + +%{__mkdir_p} %{buildroot}%{_bindir}/mongodb-enc +%{__mkdir_p} %{buildroot}%{_sysconfdir}/mongodb-enc +%{__mkdir_p} %{buildroot}%{_localstatedir}/log/mongodb-enc +cp -r ./scripts/* %{buildroot}%{_bindir}/mongodb-enc/ +cp -r ./conf/* %{buildroot}%{_sysconfdir}/mongodb-enc/ + +%files +%{_bindir}/mongodb-enc/* +%{_sysconfdir}/mongodb-enc/* + +%pre + +%post + +%clean +rm -rf %{buildroot} + +%changelog +* Thu Dec 6 2012 David Wahlstrom - 0.1-1 +- initial packaging of mongodb-enc + diff --git a/scripts/add_node.py b/scripts/add_node.py deleted file mode 100755 index d50c855..0000000 --- a/scripts/add_node.py +++ /dev/null @@ -1,121 +0,0 @@ -#!/usr/bin/python -# vim: set expandtab: -""" -********************************************************************** -GPL License -*********************************************************************** -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -***********************************************************************/ - -:author: Brian Carpio -:email: bcarpio@thetek.net -:web: http://www.briancarpio.com - -""" -import sys -import argparse -import config - -def main(): - """ This script adds nodes to the mongodb enc """ - - col = config.main() - cmd_parser = argparse.ArgumentParser(description='Add Nodes To Mongodb ENC') - cmd_parser.add_argument('-a', '--action', dest='puppet_action', choices=['append', 'new'], help='Append Or Recreate Default Node', required=True) - cmd_parser.add_argument('-n', '--node', dest='puppet_node', help='Puppet Node Hostname', required=True) - cmd_parser.add_argument('-c', '--class', dest='puppet_classes', help='Can specify multiple classes each with -c', action='append') - cmd_parser.add_argument('-p', '--param', dest='puppet_param', help='Can specify multiple parameters each with -p', action='append') - cmd_parser.add_argument('-i', '--inherit', dest='puppet_inherit', help='Define a node to inherit classes from', action='store') - cmd_parser.add_argument('-e', '--environment', dest='environment', help='Optional, defaults to "production"', default='production') - args = cmd_parser.parse_args() - - if args.puppet_node == args.puppet_inherit != 'default' : - print "ERROR: Node name and inherit name can not be the same" - sys.exit(1) - - if args.puppet_classes: - - c = {} - for pclass in args.puppet_classes: - c[pclass] = '' - - if args.puppet_param: - args.puppet_param = dict([arg.split('=') for arg in args.puppet_param]) - - if args.puppet_inherit: - ck = col.find_one({ "node" : args.puppet_inherit}) - if not ck: - print "ERROR: Inherit node does not exist, please add "+args.puppet_inherit+" and then retry" - sys.exit(1) - - if args.puppet_action == 'new': - if not args.puppet_inherit: - print "ERROR: You Need To Define PUPPET_INHERIT" - sys.exit(1) - check = col.find({ 'node' : args.puppet_node }, {'node': 1}) - for document in check: - node = document['node'] - if node == args.puppet_node: - print args.puppet_node+" Exists In Mongodb. Please Remove Node" - - - if args.puppet_classes: - d = { 'node' : args.puppet_node, 'enc' : { 'classes': c, 'environment' : args.environment }} - - else: - d = { 'node' : args.puppet_node, 'enc' : { 'environment' : args.environment }} - - if args.puppet_param: - d['enc']['parameters'] = args.puppet_param - - if args.puppet_inherit: - d['inherit'] = args.puppet_inherit - - - col.ensure_index('node', unique=True) - col.insert(d) - - if args.puppet_action == 'append': - - node = col.find_one({ 'node' : args.puppet_node}) - if node == None: - print "ERROR: Not Node In Mongo ENC. Please Use -a new" - sys.exit(1) - - if args.puppet_classes: - - if 'classes' in node['enc']: - node['enc']['classes'].update(c) - else: - node['enc']['classes'] = c - c = node['enc']['classes'] - col.update({ 'node' : args.puppet_node}, { '$set': { 'enc.classes' : c }}) - - if args.puppet_param: - - if 'parameters' in node['enc']: - node['enc']['parameters'].update(args.puppet_param) - else: - node['enc']['parameters'] = args.puppet_param - p = node['enc']['parameters'] - col.update({ 'node' : args.puppet_node}, { '$set': {'enc.parameters' : p}}) - - if args.puppet_inherit: - node['enc']['inherit'] = args.puppet_inherit - col.update({ 'node' : args.puppet_node}, { '$set' : {'inherit' : args.puppet_inherit}}) - - -if __name__ == "__main__": - main() diff --git a/scripts/config.py b/scripts/config.py deleted file mode 100644 index fb2fcf7..0000000 --- a/scripts/config.py +++ /dev/null @@ -1,45 +0,0 @@ -#!/usr/bin/python -# vim: set expandtab: -""" -********************************************************************** -GPL License -*********************************************************************** -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -***********************************************************************/ - -:author: Brian Carpio -:email: bcarpio@thetek.net -:web: http://www.briancarpio.com - -""" -from pymongo import Connection -import os -from ConfigParser import SafeConfigParser - -def main(): - """ This script adds nodes to the mongodb enc """ - - parser = SafeConfigParser() - config = os.path.join(os.path.dirname(__file__),"../conf/conf.ini") - parser.read(config) - database = parser.get('mongodb_info', 'mongodb_db_name') - collection = parser.get('mongodb_info', 'mongodb_collection_name') - host = parser.get('mongodb_info', 'mongodb_servers') - con = Connection(host) - col = con[database][collection] - return col - -if __name__ == "__main__": - main() diff --git a/scripts/mongodb_enc.py b/scripts/mongodb_enc.py new file mode 100755 index 0000000..e54472b --- /dev/null +++ b/scripts/mongodb_enc.py @@ -0,0 +1,215 @@ +#!/usr/bin/python +# vim: set expandtab: +""" +********************************************************************** +GPL License +*********************************************************************** +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . + +***********************************************************************/ + +:author: David Wahlstrom +:email: david.wahlstrom@gmail.com + +""" + + +class Node(object): + """Class to facilitate adding/modifying a node""" + + def __init__(self, nodename, nodeclass=None, classparams=None, nodeparam=None, puppet_inherit=None, environment=None): + """Initialize variables for use""" + + #create an initial connection to the mongodb + self.mongo_collection = self.configure() + + #verify the inherit node exists + if puppet_inherit: + if self.verifynode(puppet_inherit) is False: + print "ERROR: Inherit node does not exist, please add %s and then retry" % puppet_inherit + else: + self.puppet_inherit = puppet_inherit + self.node = nodename + if nodeclass: + self.nodeclass = nodeclass + else: + self.nodeclass = '' + self.classparams = classparams + self.nodeparam = nodeparam + if puppet_inherit: + self.puppet_inherit = puppet_inherit + else: + self.puppet_inherit = 'none' + if environment: + self.environment = environment + else: + self.environment = '' + + def configure(self): + """Read configuration file and intialize connection to the mongodb instance""" + + from pymongo import Connection + import os + from ConfigParser import SafeConfigParser + + parser = SafeConfigParser() + if os.path.isfile('/etc/mongodb_enc/conf.ini'): + config = '/etc/mongodb_enc/conf.ini' + else: + config = os.path.join(os.path.dirname(__file__), "../conf/conf.ini") + parser.read(config) + database = parser.get('mongodb_info', 'mongodb_db_name') + collection = parser.get('mongodb_info', 'mongodb_collection_name') + host = parser.get('mongodb_info', 'mongodb_server') + con = Connection(host) + col = con[database][collection] + return col + + def verifynode(self, vernode=None): + """Verify that the node exists in the DB. Returns True if it does exist, Return False if it does not""" + + if not vernode: + testnode = self.node + else: + testnode = vernode + + docnode = self.mongo_collection.find_one({'node': testnode}) + if docnode is None: + return False + else: + if docnode['node'] == testnode: + return True + else: + return False + + def parse_node_classification(self, puppet_class=None, class_params=None, parameters=None, environment=None, inherit=None): + """Parse puppet_class, class_params, parameters, and environment, and return a dict with the result""" + + puppet_enc = { + 'classes': '', + 'environment': '', + 'parameters': '', + } + + if not puppet_class: + puppet_class = self.nodeclass + puppet_enc['classes'] = puppet_class + + if not class_params: + class_params = self.classparams + + if parameters: + puppet_enc['parameters'] = parameters + else: + if self.nodeparam: + puppet_enc['parameters'] = self.nodeparam + else: + puppet_enc['parameters'] = '' + + if environment: + puppet_enc['environment'] = environment + else: + puppet_enc['environment'] = self.environment + + if not puppet_enc['classes']: + puppet_enc['classes'] = [] + + if not puppet_enc['parameters']: + puppet_enc['parameters'] = {} + + if not puppet_enc['environment']: + del puppet_enc['environment'] + + paramclass = {} + try: + paramclass = self.mongo_collection.find_one({'node': self.node})['enc']['classes'] + except: + paramclass = {} + + if not paramclass: + paramclass = {} + + #Pull parameters for each class + if puppet_class: + paramkeyvalue = {} + paramvalue = [] + if not class_params: + paramkeyvalue = '' + else: + for param in class_params.split(','): + paramkey = '' + paramkey = param.split('=')[0] + paramvalue.append(param.split('=')[1]) + paramkeyvalue[paramkey] = paramvalue + + paramclass[puppet_class] = paramkeyvalue + + #Since we stored them as lists above, reduce single item lists to strings + for puppetclass in paramclass: + for param in paramclass[puppetclass]: + if type(paramclass[puppetclass][param]) is type(list()): + if len(paramclass[puppetclass][param]) == 1: + paramclass[puppetclass][param] = paramclass[puppetclass][param][0] + + puppet_enc['classes'] = paramclass + + return puppet_enc + + def update(self, enc, inherit=None): + """Add/modify "enc" properties on "node" """ + + if inherit: + tmp_inherit = inherit + else: + tmp_inherit = self.puppet_inherit + + if not self.verifynode('none'): + self.mongo_collection.update({'node': 'none'}, {"$set": {'enc': {'classes': []}, 'inherit': ''}}, True) + + self.mongo_collection.update({'node': self.node}, {"$set": {'enc': enc, 'inherit': tmp_inherit}}, True) + + def remove(self, rmnode=None): + """Remove node from mongodb""" + + if rmnode: + nodename = rmnode + else: + nodename = self.node + + nodes_with_inheritance = self.mongo_collection.find({'inherit': nodename}) + for inheritnode in nodes_with_inheritance: + print "%s inherits the node you are trying to remove." % inheritnode['node'] + + self.mongo_collection.remove({'node': nodename}) + +if __name__ == "__main__": + + import argparse + + cmd_parser = argparse.ArgumentParser(description='Add/remove/modify nodes in MongoDB ENC.') + cmd_parser.add_argument('-n', '--node', dest='puppet_node', help='Puppet node hostname.', required=True) + cmd_parser.add_argument('-r', '--remove', dest='remove_attr', help='Remove supplied attributes from host.', action='store_true', default=False) + cmd_parser.add_argument('-c', '--class', dest='puppet_class', help='Apply (or remove with -r) class on node (-n). Parameters can be passed to the class with -m.', action='store', default=None) + cmd_parser.add_argument('-m', '--classparameters', dest='class_params', help='Apply (or remove with -r) class parameters to class (-c) on node (-n).', action='store', default=None) + cmd_parser.add_argument('-p', '--param', dest='puppet_param', help='Apply (or remove with -r) parameters (global variables) to node (-n).', action='store', default=None) + cmd_parser.add_argument('-i', '--inherit', dest='puppet_inherit', help='Apply (or remove with -r) inherit node. Only a single node can be set to inherit from.', action='store', default=None) + cmd_parser.add_argument('-e', '--environment', dest='environment', help='Apply (or remove with -r) puppet agent environment.', default=None) + args = cmd_parser.parse_args() + + node = Node(args.puppet_node, args.puppet_class, args.class_params, args.puppet_param, args.puppet_inherit, args.environment) + + if args.remove_attr is True: + node.remove(node.node) + else: + node.update(node.parse_node_classification()) diff --git a/scripts/mongodb_node_classifier.py b/scripts/mongodb_node_classifier.py index 7c0c4e4..5bc196d 100755 --- a/scripts/mongodb_node_classifier.py +++ b/scripts/mongodb_node_classifier.py @@ -19,56 +19,85 @@ ***********************************************************************/ -:author: Brian Carpio -:email: bcarpio@thetek.net -:web: http://www.briancarpio.com +:author: David Wahlstrom +:email: david.wahlstrom@gmail.com """ import yaml import sys -import config -def main(): - """ This script is called by puppet """ +def configure(): + """Read configuration file and intialize connection to the mongodb instance""" + + from pymongo import Connection + import os + from ConfigParser import SafeConfigParser + + parser = SafeConfigParser() + if os.path.isfile('/etc/mongodb_enc/conf.ini'): + config = '/etc/mongodb_enc/conf.ini' + else: + config = os.path.join(os.path.dirname(__file__), "../conf/conf.ini") + parser.read(config) + database = parser.get('mongodb_info', 'mongodb_db_name') + collection = parser.get('mongodb_info', 'mongodb_collection_name') + host = parser.get('mongodb_info', 'mongodb_server') + con = Connection(host) + col = con[database][collection] + return col + +def classify(cnode): + """Classify the requested node, including inheritances""" + + col = configure() + try: + node_classes = col.find_one({'node': cnode})['enc']['classes'] + except TypeError: + node_classes = {} + + try: + # Grab the info from the inheritance node + inode = col.find_one({'node': cnode}) + if not inode['inherit']: + raise KeyError + inode_classes = classify(inode['inherit']) + if not col.find_one({"node" : cnode}): + print "ERROR: Inheritance node " + cnode + " not found in ENC" + sys.exit(1) + node_classes = col.find_one({"node": cnode})['enc']['classes'] + # Grab the requested node's classes + tmp_class_store = node_classes + # Apply the inheritance node classes + node_classes = dict(inode_classes) + # Apply the requested node's classes and overrides + node_classes.update(tmp_class_store) + except KeyError: + pass + except TypeError: + pass + + return node_classes + +def main(node): + """This script is called by puppet""" + if (len(sys.argv) < 2): - print "ERROR: Please Supply A Hostname or FQDN" + print "ERROR: Please supply a hostname or FQDN" sys.exit(1) - col = config.main() - - # Probably want to remove this. This is because I don't use FQDNs in my current puppet manifest. - # also made this easier for me to test. - node = sys.argv[1] - #node = node.split('.')[0] + col = configure() # Find the node given at a command line argument d = col.find_one({"node": node}) if d == None: - print "ERROR: Node "+node+" Not Found In ENC" + print "ERROR: Node %s not found in ENC" % node sys.exit(1) - # Check if the node requiers inheritance - n = col.find_one({"node": node}) - if 'inherit' in n: - i = True - while i == True: - inode = n['inherit'] - if not col.find_one({"node" : inode}): - print "ERROR: Inheritance Node "+inode+" Not Found In ENC" - sys.exit(1) - idict = col.find_one({"node": inode}) - if 'classes' in idict['enc']: - iclass = idict['enc']['classes'] - if 'classes' in n['enc']: - d['enc']['classes'].update(iclass) - else: - d['enc']['classes'] = iclass - n = col.find_one({"node": inode}) - if 'inherit' not in n: - i = False + # Classify node + d['enc']['classes'] = classify(node) print yaml.safe_dump(d['enc'], default_flow_style=False) if __name__ == "__main__": - main() + main(sys.argv[1]) diff --git a/scripts/remove_node.py b/scripts/remove_node.py deleted file mode 100755 index d0383fc..0000000 --- a/scripts/remove_node.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/python -# vim: set expandtab: -""" -********************************************************************** -GPL License -*********************************************************************** -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -***********************************************************************/ - -:author: Brian Carpio -:email: bcarpio@thetek.net -:web: http://www.briancarpio.com - -""" -import os -import argparse -import config - -def main(): - - """ This script removes nodes from mongodb """ - col = config.main() - - cmd_parser = argparse.ArgumentParser(description='Remove Nodes To Mongodb ENC') - cmd_parser.add_argument('-n', '--node', dest='puppet_node', help='Puppet Node Hostname', required=True) - args = cmd_parser.parse_args() - - isinode = col.find_one({ "inherit" : args.puppet_node }) - if isinode: - isinode = col.find({ "inherit" : args.puppet_node }) - for node in isinode: - print "ERROR: "+args.puppet_node+" is inherited by "+node['node'] - else: - col.remove({ 'node' : args.puppet_node}) - -if __name__ == "__main__": - main() diff --git a/scripts/setup.py b/scripts/setup.py deleted file mode 100755 index 1a9c0d0..0000000 --- a/scripts/setup.py +++ /dev/null @@ -1,68 +0,0 @@ -#!/usr/bin/python -# vim: set expandtab: -""" -********************************************************************** -GPL License -*********************************************************************** -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . - -***********************************************************************/ - -:author: Brian Carpio -:email: bcarpio@thetek.net -:web: http://www.briancarpio.com - -""" -import sys -import argparse -import config - - -def main(): - """ This script creates the default node definition """ - - col = config.main() - - cmd_parser = argparse.ArgumentParser(description='Add Default Node To Mongodb ENC') - cmd_parser.add_argument('-a', '--action', dest='puppet_action', choices=['append', 'new'], help='Append Or Recreate Default Node', required=True) - cmd_parser.add_argument('-c', '--class', dest='puppet_classes', help='Can specify multiple classes each with -c', action='append', required=True) - args = cmd_parser.parse_args() - - - c = {} - col.ensure_index('node', unique=True) - - for pclass in args.puppet_classes: - c[pclass] = '' - - if args.puppet_action == 'append': - d = { 'node' : 'default', 'enc' : { 'classes': c }} - check = col.find_one({ 'node' : 'default' }, {'node': 1}) - if not check: - print "Default Node Doesn't Exist, Please Add It First" - sys.exit(1) - ec = col.find_one({ 'node' : 'default'}) - ec['enc']['classes'].update(c) - col.remove({ 'node' : 'default'}) - col.insert(ec) - - if args.puppet_action == 'new': - d = { 'node' : 'default', 'enc' : { 'classes': c }} - check = col.find_one({ 'node' : 'default' }, {'node': 1}) - if check: - col.remove({ 'node' : 'default'}) - col.insert(d) - -if __name__ == "__main__": - main()