source: people/joerg.steffens/technical/foreman/migration/foreman-migration.py@ 1194

Last change on this file since 1194 was 1194, checked in by joergs, on Sep 1, 2015 at 9:48:51 PM

initial

  • Property svn:executable set to *
File size: 9.1 KB
Line 
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
12import argparse
13from glob import glob
14import logging
15import os
16#from os import listdir
17#from os.path import isfile, join
18from pprint import pprint, pformat
19import yaml
20
21
22# TODO:
23
24
25def construct_ruby_object(loader, suffix, node):
26 return loader.construct_yaml_map(node)
27
28def construct_ruby_sym(loader, node):
29 return loader.construct_yaml_str(node)
30
31class ForemanInfo:
32 """ get information from Foreman server """
33 def __init__(self):
34 pass
35
36class HostMigrateException(Exception):
37 pass
38
39class 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
215if __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
Note: See TracBrowser for help on using the repository browser.