#!/usr/bin/env python # -*- coding: utf-8 -*- """import puppet data to foreman puppet""" __author__ = "Joerg Steffens" __copyright__ = "Copyright 2015, dass IT GmbH" __license__ = "GPL" __version__ = "0.1" __email__ = "joerg.steffens@dass-it.de" import argparse from glob import glob import logging import os #from os import listdir #from os.path import isfile, join from pprint import pprint, pformat import yaml # TODO: def construct_ruby_object(loader, suffix, node): return loader.construct_yaml_map(node) def construct_ruby_sym(loader, node): return loader.construct_yaml_str(node) class ForemanInfo: """ get information from Foreman server """ def __init__(self): pass class HostMigrateException(Exception): pass class Host: # set map to None, if class should ge removed classMap = { 'debeka-module-netzwerk': 'debeka_module_netzwerk', 'debeka-hardware-support': 'debeka_hardware_support', 'debeka-systemzugang': 'debeka_systemzugang', 'debeka_virenscanner': 'debeka_virenscanner', 'debeka_omw': 'debeka_omw', 'debeka_smt': 'debeka_smt', 'debeka_datensicherung': 'debeka_datensicherung', 'ntp': 'ntp', 'ssh': 'ssh', } src = { 'hiera': None, 'facts': None, } hammer = { 'options': { 'domain': "--domain server.debeka.de", 'comment': '--comment "import test"', 'architecture': '--architecture "x86_64"', 'operatingsystem': '--operatingsystem "SLES 11 SP3"', # TODO 'root-password': '--root-password "linuxlinux"', 'medium': '--medium smt.debeka-SLES-Pool-x86_64', 'partition-table': '--partition-table debeka-autoyast-sda-lvm', 'build': '--build false', 'interfaces': [], }, } def __init__(self, host, destprefix=""): self.logger=logging.getLogger(__name__) self.host=host self.destprefix=destprefix self.logger.info( "* host: " + host ) self.hammer['options']['name']='--name ' + destprefix + host def __convert_classes(self): classes=self.src['hiera']['classes'] self.logger.debug(classes) self.hammer['puppet-classes'] = [] for i in classes: if not self.classMap.has_key(i): self.logger.error("unknown class " + i) return False elif self.classMap[i] != None: self.hammer['puppet-classes'].append(self.classMap[i]) # TODO: convert class to puppet-class-id self.logger.debug(self.hammer) # remove all converted data self.src['hiera'].pop('classes') def __convert_netzkarte_alias(self, main, alias, values): self.logger.debug( "alias: " + main + alias + pformat( values ) ) #--interface "identifier=test3,attached_to=eth0,virtual=true,managed=true,subnet_id=2,ip=192.168.27.3" interface="identifier=%s,attached_to=%s" % ( main + "_" + alias, main) for key in values: if key == "ipAddr": interface += ",ip=%s" % (values[key]) elif key == "prefixlen": # ignore, should come from network pass else: self.logger.error("failed: net interface %s, alias %s: unhandeld key %s" % (main, alias, key)) return False self.hammer['options']['interfaces'].append( '--interface="' + interface + ',managed=true,virtual=true"' ) def __convert_netzkarte(self): try: netzkarte=self.src['hiera']['debeka-module-netzwerk::netzkarte'] except KeyError: # no netzkarte defined. Can this be ignored? Ignore host? raise HostMigrateException("no debeka-module-netzwerk::netzkarte defined") primary = None for name in netzkarte: interface="identifier=%s" % (name) if self.src['facts']['values'].has_key('macaddress_' + name): interface += ",mac=%s" % (self.src['facts']['values']['macaddress_' + name]) for key in netzkarte[name]: if key == "ipAddr": interface += ",ip=%s" % (netzkarte[name][key]) elif key == "prefixlen": # ignore, should come from network pass elif key == "aliasse": for alias in netzkarte[name][key]: self.__convert_netzkarte_alias(name, alias, netzkarte[name][key][alias]) pass else: self.logger.error("failed: unhandeld key " + key) return False # TODO: add subnet_id interface += ",managed=true" if name == "eth0": interface += ",provision=true,primary=true" primary=name self.logger.debug(interface) self.hammer['options']['interfaces'].append( '--interface="' + interface + '"' ) # one primary interface must be defined if primary == None: raise HostMigrateException("no primary interface defined") def __set_environment(self): try: environment=self.src['hiera']['umgebung'] except KeyError: # not defined # required, so raise raise HostMigrateException( "no environment (umgebung) defined" ) #return False self.hammer['options']['environment'] = '--environment ' + environment ## TODO: should it be like this? self.hammer['options']['hostgroup'] = '--hostgroup ' + environment # remove all converted data self.src['hiera'].pop('umgebung') def __read_hiera_data(self): filename="./data/hiera/" + self.host + ".yaml" try: with open( filename , 'r') as hierafile: raw = hierafile.read() # fix syntax error in hiera files # replace # netmask: - # device: - # by # netmask: "-" # device: "-" data=raw.replace( ": -", ": '-'" ); except IOError: self.logger.error( "failed to read hiera data for host " + self.host + " from file " + filename) return False try: self.src['hiera']=yaml.load(data) except yaml.scanner.ScannerError as e: self.logger.error(e) return False self.logger.debug( "hiera:\n" + pformat( self.src['hiera'])) return True def __read_facts(self): filename = "./data/facts/" + self.host + ".server.debeka.de.yaml" try: stream = open(filename, "r") except IOError: self.logger.error( "failed to read facts for host " + self.host + " from file " + filename) return False yaml.add_multi_constructor(u"!ruby/object:", construct_ruby_object) yaml.add_constructor(u"!ruby/sym", construct_ruby_sym) self.src['facts']=yaml.load(stream) self.logger.debug( "facts:" + pformat(self.src['facts'])) return True def get_hammer_options(self): if not (self.__read_hiera_data() and self.__read_facts()): self.logger.error( "failed: aborted host " + self.host ) return False self.__convert_classes() self.__convert_netzkarte() self.__set_environment() self.logger.info("hammer:") self.logger.info(pformat(self.hammer)) if __name__ == '__main__': logging.basicConfig(format='%(message)s') logger = logging.getLogger(__name__) logger.setLevel(logging.INFO) destprefix="xxxtest-" parser = argparse.ArgumentParser(description='Migrate Debeka Puppet hosts to Foreman Puppet.' ) parser.add_argument( '--debug', action='store_true', help="enable debugging output" ) parser.add_argument( '--destprefix', dest="prefix", default=destprefix, help="prefix to be added to converted hosts, used for testing (default: '" + destprefix + "')" ) parser.add_argument( '--host', help="name of the host that should get converted" ) parser.add_argument( '--dir', help="base directory, that contains hiera and facts subdirectories" ) args = parser.parse_args() if args.debug: logger.setLevel(logging.DEBUG) logger.debug( "start" ) if args.host: host=Host(args.host, args.prefix) host.get_hammer_options() elif args.dir: for i in glob( args.dir + "/hiera/*.yaml" ): #onlyfiles = [ f for f in listdir(mypath) if isfile(join(mypath,f)) ] hostname = os.path.splitext(os.path.basename(i))[0] host=Host(hostname, args.prefix) host.get_hammer_options() else: logger.error( "either specify host or dir" )