[1194] | 1 | #!/usr/bin/env python
|
---|
| 2 | # -*- coding: utf-8 -*-
|
---|
| 3 |
|
---|
| 4 | """import puppet data to foreman puppet"""
|
---|
| 5 |
|
---|
| 6 | __author__ = "Joerg Steffens"
|
---|
| 7 | __copyright__ = "Copyright 2015, dass IT GmbH"
|
---|
| 8 | __license__ = "GPL"
|
---|
| 9 | __version__ = "0.1"
|
---|
| 10 | __email__ = "joerg.steffens@dass-it.de"
|
---|
| 11 |
|
---|
| 12 | import argparse
|
---|
| 13 | from glob import glob
|
---|
| 14 | import logging
|
---|
| 15 | import os
|
---|
| 16 | #from os import listdir
|
---|
| 17 | #from os.path import isfile, join
|
---|
| 18 | from pprint import pprint, pformat
|
---|
| 19 | import yaml
|
---|
| 20 |
|
---|
| 21 |
|
---|
| 22 | # TODO:
|
---|
| 23 |
|
---|
| 24 |
|
---|
| 25 | def construct_ruby_object(loader, suffix, node):
|
---|
| 26 | return loader.construct_yaml_map(node)
|
---|
| 27 |
|
---|
| 28 | def construct_ruby_sym(loader, node):
|
---|
| 29 | return loader.construct_yaml_str(node)
|
---|
| 30 |
|
---|
| 31 | class ForemanInfo:
|
---|
| 32 | """ get information from Foreman server """
|
---|
| 33 | def __init__(self):
|
---|
| 34 | pass
|
---|
| 35 |
|
---|
| 36 | class HostMigrateException(Exception):
|
---|
| 37 | pass
|
---|
| 38 |
|
---|
| 39 | class Host:
|
---|
| 40 | # set map to None, if class should ge removed
|
---|
| 41 | classMap = {
|
---|
| 42 | 'debeka-module-netzwerk': 'debeka_module_netzwerk',
|
---|
| 43 | 'debeka-hardware-support': 'debeka_hardware_support',
|
---|
| 44 | 'debeka-systemzugang': 'debeka_systemzugang',
|
---|
| 45 | 'debeka_virenscanner': 'debeka_virenscanner',
|
---|
| 46 | 'debeka_omw': 'debeka_omw',
|
---|
| 47 | 'debeka_smt': 'debeka_smt',
|
---|
| 48 | 'debeka_datensicherung': 'debeka_datensicherung',
|
---|
| 49 | 'ntp': 'ntp',
|
---|
| 50 | 'ssh': 'ssh',
|
---|
| 51 | }
|
---|
| 52 |
|
---|
| 53 | src = {
|
---|
| 54 | 'hiera': None,
|
---|
| 55 | 'facts': None,
|
---|
| 56 | }
|
---|
| 57 |
|
---|
| 58 | hammer = {
|
---|
| 59 | 'options': {
|
---|
| 60 | 'domain': "--domain server.debeka.de",
|
---|
| 61 | 'comment': '--comment "import test"',
|
---|
| 62 | 'architecture': '--architecture "x86_64"',
|
---|
| 63 | 'operatingsystem': '--operatingsystem "SLES 11 SP3"',
|
---|
| 64 | # TODO
|
---|
| 65 | 'root-password': '--root-password "linuxlinux"',
|
---|
| 66 | 'medium': '--medium smt.debeka-SLES-Pool-x86_64',
|
---|
| 67 | 'partition-table': '--partition-table debeka-autoyast-sda-lvm',
|
---|
| 68 | 'build': '--build false',
|
---|
| 69 | 'interfaces': [],
|
---|
| 70 | },
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | def __init__(self, host, destprefix=""):
|
---|
| 74 | self.logger=logging.getLogger(__name__)
|
---|
| 75 | self.host=host
|
---|
| 76 | self.destprefix=destprefix
|
---|
| 77 | self.logger.info( "* host: " + host )
|
---|
| 78 | self.hammer['options']['name']='--name ' + destprefix + host
|
---|
| 79 |
|
---|
| 80 | def __convert_classes(self):
|
---|
| 81 | classes=self.src['hiera']['classes']
|
---|
| 82 | self.logger.debug(classes)
|
---|
| 83 | self.hammer['puppet-classes'] = []
|
---|
| 84 | for i in classes:
|
---|
| 85 | if not self.classMap.has_key(i):
|
---|
| 86 | self.logger.error("unknown class " + i)
|
---|
| 87 | return False
|
---|
| 88 | elif self.classMap[i] != None:
|
---|
| 89 | self.hammer['puppet-classes'].append(self.classMap[i])
|
---|
| 90 | # TODO: convert class to puppet-class-id
|
---|
| 91 | self.logger.debug(self.hammer)
|
---|
| 92 | # remove all converted data
|
---|
| 93 | self.src['hiera'].pop('classes')
|
---|
| 94 |
|
---|
| 95 | def __convert_netzkarte_alias(self, main, alias, values):
|
---|
| 96 | self.logger.debug( "alias: " + main + alias + pformat( values ) )
|
---|
| 97 | #--interface "identifier=test3,attached_to=eth0,virtual=true,managed=true,subnet_id=2,ip=192.168.27.3"
|
---|
| 98 | interface="identifier=%s,attached_to=%s" % ( main + "_" + alias, main)
|
---|
| 99 | for key in values:
|
---|
| 100 | if key == "ipAddr":
|
---|
| 101 | interface += ",ip=%s" % (values[key])
|
---|
| 102 | elif key == "prefixlen":
|
---|
| 103 | # ignore, should come from network
|
---|
| 104 | pass
|
---|
| 105 | else:
|
---|
| 106 | self.logger.error("failed: net interface %s, alias %s: unhandeld key %s" % (main, alias, key))
|
---|
| 107 | return False
|
---|
| 108 | self.hammer['options']['interfaces'].append( '--interface="' + interface + ',managed=true,virtual=true"' )
|
---|
| 109 |
|
---|
| 110 | def __convert_netzkarte(self):
|
---|
| 111 | try:
|
---|
| 112 | netzkarte=self.src['hiera']['debeka-module-netzwerk::netzkarte']
|
---|
| 113 | except KeyError:
|
---|
| 114 | # no netzkarte defined. Can this be ignored? Ignore host?
|
---|
| 115 | raise HostMigrateException("no debeka-module-netzwerk::netzkarte defined")
|
---|
| 116 | primary = None
|
---|
| 117 | for name in netzkarte:
|
---|
| 118 | interface="identifier=%s" % (name)
|
---|
| 119 | if self.src['facts']['values'].has_key('macaddress_' + name):
|
---|
| 120 | interface += ",mac=%s" % (self.src['facts']['values']['macaddress_' + name])
|
---|
| 121 | for key in netzkarte[name]:
|
---|
| 122 | if key == "ipAddr":
|
---|
| 123 | interface += ",ip=%s" % (netzkarte[name][key])
|
---|
| 124 | elif key == "prefixlen":
|
---|
| 125 | # ignore, should come from network
|
---|
| 126 | pass
|
---|
| 127 | elif key == "aliasse":
|
---|
| 128 | for alias in netzkarte[name][key]:
|
---|
| 129 | self.__convert_netzkarte_alias(name, alias, netzkarte[name][key][alias])
|
---|
| 130 | pass
|
---|
| 131 | else:
|
---|
| 132 | self.logger.error("failed: unhandeld key " + key)
|
---|
| 133 | return False
|
---|
| 134 | # TODO: add subnet_id
|
---|
| 135 | interface += ",managed=true"
|
---|
| 136 | if name == "eth0":
|
---|
| 137 | interface += ",provision=true,primary=true"
|
---|
| 138 | primary=name
|
---|
| 139 | self.logger.debug(interface)
|
---|
| 140 | self.hammer['options']['interfaces'].append( '--interface="' + interface + '"' )
|
---|
| 141 | # one primary interface must be defined
|
---|
| 142 | if primary == None:
|
---|
| 143 | raise HostMigrateException("no primary interface defined")
|
---|
| 144 |
|
---|
| 145 | def __set_environment(self):
|
---|
| 146 | try:
|
---|
| 147 | environment=self.src['hiera']['umgebung']
|
---|
| 148 | except KeyError:
|
---|
| 149 | # not defined
|
---|
| 150 | # required, so raise
|
---|
| 151 | raise HostMigrateException( "no environment (umgebung) defined" )
|
---|
| 152 | #return False
|
---|
| 153 | self.hammer['options']['environment'] = '--environment ' + environment
|
---|
| 154 | ## TODO: should it be like this?
|
---|
| 155 | self.hammer['options']['hostgroup'] = '--hostgroup ' + environment
|
---|
| 156 | # remove all converted data
|
---|
| 157 | self.src['hiera'].pop('umgebung')
|
---|
| 158 |
|
---|
| 159 |
|
---|
| 160 | def __read_hiera_data(self):
|
---|
| 161 | filename="./data/hiera/" + self.host + ".yaml"
|
---|
| 162 | try:
|
---|
| 163 | with open( filename , 'r') as hierafile:
|
---|
| 164 | raw = hierafile.read()
|
---|
| 165 | # fix syntax error in hiera files
|
---|
| 166 | # replace
|
---|
| 167 | # netmask: -
|
---|
| 168 | # device: -
|
---|
| 169 | # by
|
---|
| 170 | # netmask: "-"
|
---|
| 171 | # device: "-"
|
---|
| 172 | data=raw.replace( ": -", ": '-'" );
|
---|
| 173 | except IOError:
|
---|
| 174 | self.logger.error( "failed to read hiera data for host " + self.host + " from file " + filename)
|
---|
| 175 | return False
|
---|
| 176 |
|
---|
| 177 | try:
|
---|
| 178 | self.src['hiera']=yaml.load(data)
|
---|
| 179 | except yaml.scanner.ScannerError as e:
|
---|
| 180 | self.logger.error(e)
|
---|
| 181 | return False
|
---|
| 182 |
|
---|
| 183 | self.logger.debug( "hiera:\n" + pformat( self.src['hiera']))
|
---|
| 184 | return True
|
---|
| 185 |
|
---|
| 186 | def __read_facts(self):
|
---|
| 187 | filename = "./data/facts/" + self.host + ".server.debeka.de.yaml"
|
---|
| 188 | try:
|
---|
| 189 | stream = open(filename, "r")
|
---|
| 190 | except IOError:
|
---|
| 191 | self.logger.error( "failed to read facts for host " + self.host + " from file " + filename)
|
---|
| 192 | return False
|
---|
| 193 |
|
---|
| 194 | yaml.add_multi_constructor(u"!ruby/object:", construct_ruby_object)
|
---|
| 195 | yaml.add_constructor(u"!ruby/sym", construct_ruby_sym)
|
---|
| 196 |
|
---|
| 197 | self.src['facts']=yaml.load(stream)
|
---|
| 198 | self.logger.debug( "facts:" + pformat(self.src['facts']))
|
---|
| 199 | return True
|
---|
| 200 |
|
---|
| 201 | def get_hammer_options(self):
|
---|
| 202 | if not (self.__read_hiera_data() and self.__read_facts()):
|
---|
| 203 | self.logger.error( "failed: aborted host " + self.host )
|
---|
| 204 | return False
|
---|
| 205 |
|
---|
| 206 | self.__convert_classes()
|
---|
| 207 | self.__convert_netzkarte()
|
---|
| 208 | self.__set_environment()
|
---|
| 209 |
|
---|
| 210 | self.logger.info("hammer:")
|
---|
| 211 | self.logger.info(pformat(self.hammer))
|
---|
| 212 |
|
---|
| 213 |
|
---|
| 214 |
|
---|
| 215 | if __name__ == '__main__':
|
---|
| 216 | logging.basicConfig(format='%(message)s')
|
---|
| 217 | logger = logging.getLogger(__name__)
|
---|
| 218 | logger.setLevel(logging.INFO)
|
---|
| 219 |
|
---|
| 220 | destprefix="xxxtest-"
|
---|
| 221 |
|
---|
| 222 | parser = argparse.ArgumentParser(description='Migrate Debeka Puppet hosts to Foreman Puppet.' )
|
---|
| 223 | parser.add_argument( '--debug', action='store_true', help="enable debugging output" )
|
---|
| 224 | parser.add_argument( '--destprefix', dest="prefix", default=destprefix, help="prefix to be added to converted hosts, used for testing (default: '" + destprefix + "')" )
|
---|
| 225 | parser.add_argument( '--host', help="name of the host that should get converted" )
|
---|
| 226 | parser.add_argument( '--dir', help="base directory, that contains hiera and facts subdirectories" )
|
---|
| 227 |
|
---|
| 228 | args = parser.parse_args()
|
---|
| 229 |
|
---|
| 230 | if args.debug:
|
---|
| 231 | logger.setLevel(logging.DEBUG)
|
---|
| 232 | logger.debug( "start" )
|
---|
| 233 |
|
---|
| 234 | if args.host:
|
---|
| 235 | host=Host(args.host, args.prefix)
|
---|
| 236 | host.get_hammer_options()
|
---|
| 237 | elif args.dir:
|
---|
| 238 | for i in glob( args.dir + "/hiera/*.yaml" ):
|
---|
| 239 | #onlyfiles = [ f for f in listdir(mypath) if isfile(join(mypath,f)) ]
|
---|
| 240 | hostname = os.path.splitext(os.path.basename(i))[0]
|
---|
| 241 | host=Host(hostname, args.prefix)
|
---|
| 242 | host.get_hammer_options()
|
---|
| 243 | else:
|
---|
| 244 | logger.error( "either specify host or dir" )
|
---|
| 245 | |
---|