summaryrefslogtreecommitdiffstats
path: root/api.py
blob: f0c2a5af959d2e38f25a01c1ed0796bcc0021e3d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
# friendly OO python API module for BootConf 
#
# Michael DeHaan <mdehaan@redhat.com>

import os
import traceback

import config
import util
import sync
import check
from msg import *

class BootAPI:

    """
    Constructor...
    """
    def __init__(self):
       self.last_error = ''
       self.config = config.BootConfig(self)
       self.utils  = util.BootUtil(self,self.config)
       # if the file already exists, load real data now
       try:
           if self.config.files_exist():
              self.config.deserialize()
       except:
           # traceback.print_exc()
           print m("no_cfg")
           try:
               self.config.serialize()
           except:
               traceback.print_exc()
               pass
       if not self.config.files_exist():
           self.config.serialize()

    """
    Forget about current list of groups, distros, and systems
    """
    def clear(self):
       self.config.clear()

    """
    Return the current list of systems
    """
    def get_systems(self):
       return self.config.get_systems()

    """
    Return the current list of groups
    """
    def get_groups(self):
       return self.config.get_groups()

    """
    Return the current list of distributions
    """
    def get_distros(self):
       return self.config.get_distros()

    """
    Create a blank, unconfigured system
    """
    def new_system(self):
       return System(self,None)

    """
    Create a blank, unconfigured distro
    """
    def new_distro(self):
       return Distro(self,None)

    """
    Create a blank, unconfigured group
    """
    def new_group(self):
       return Group(self,None)

    """
    See if all preqs for network booting are operational
    """
    def check(self):
       return check.BootCheck(self).run()

    """
    Update the system with what is specified in the config file
    """ 
    def sync(self,dry_run=True):
       self.config.deserialize();
       configurator = sync.BootSync(self)
       configurator.sync(dry_run)

    """
    Save the config file
    """
    def serialize(self):
       self.config.serialize() 
    
    """
    Make the API's internal state reflect that of the config file
    """
    def deserialize(self):
       self.config.deserialize()

#--------------------------------------

"""
Base class for any serializable lists of things...
"""
class Collection:

    """
    Return anything named 'name' in the collection, else return None
    """
    def find(self,name):
        if name in self.listing.keys():
            return self.listing[name]
        return None

    """
    Return datastructure representation (to feed to serializer)
    """
    def to_datastruct(self):
        return [x.to_datastruct() for x in self.listing.values()]
    
     
    """
    Add an object to the collection, if it's valid
    """
    def add(self,ref):
        if ref is None or not ref.is_valid(): 
            if self.api.last_error is None or self.api.last_error == "":
                self.api.last_error = m("bad_param")
            return False
        self.listing[ref.name] = ref
        return True

    """
    Printable representation
    """
    def __str__(self):
        buf = ""
        values = map(lambda(a): str(a), sorted(self.listing.values()))
        if len(values) > 0: 
           return "\n\n".join(values)
        else:
           return m("empty_list")

    def contents(self):
        return self.listing.values()

#--------------------------------------------

"""
A distro represents a network bootable matched set of kernels
and initrd files
"""
class Distros(Collection):

    def __init__(self,api,seed_data):
        self.api = api
        self.listing = {}
        if seed_data is not None:
           for x in seed_data: 
               self.add(Distro(self.api,x))
    """
    Remove element named 'name' from the collection
    """
    def remove(self,name):
        # first see if any Groups use this distro
        for k,v in self.api.get_groups().listing.items():
            if v.distro == name:
               self.api.last_error = m("orphan_group")
               return False
        if self.find(name):
            del self.listing[name]
            return True
        self.api.last_error = m("delete_nothing")
        return False
    

#--------------------------------------------

"""
A group represents a distro paired with a kickstart file.
For instance, FC5 with a kickstart file specifying OpenOffice
might represent a 'desktop' group.
"""
class Groups(Collection):

    def __init__(self,api,seed_data):
        self.api = api
        self.listing = {}
        if seed_data is not None:
           for x in seed_data: 
               self.add(Group(self.api,x))
    """
    Remove element named 'name' from the collection
    """
    def remove(self,name):
        for k,v in self.api.get_systems().listing.items():
           if v.group == name:
               self.api.last_error = m("orphan_system")
               return False
        if self.find(name):
            del self.listing[name]
            return True
        self.api.last_error = m("delete_nothing")
        return False
    

#--------------------------------------------

"""
Systems are hostnames/MACs/IP names and the associated groups
they belong to.
"""
class Systems(Collection):

    def __init__(self,api,seed_data):
        self.api = api
        self.listing = {}
        if seed_data is not None:
           for x in seed_data: 
               self.add(System(self.api,x))
    """
    Remove element named 'name' from the collection
    """
    def remove(self,name):
        if self.find(name):
            del self.listing[name]
            return True
        self.api.last_error = m("delete_nothing")
        return False
    

#-----------------------------------------

"""
An Item is a serializable thing that can appear in a Collection
"""
class Item:
  
    """
    All objects have names, and with the exception of System
    they aren't picky about it.
    """
    def set_name(self,name):
        self.name = name
        return True

    def set_kernel_options(self,options_string):
        self.kernel_options = options_string
        return True

    def to_datastruct(self):
        raise "not implemented"
   
    def is_valid(self):
        return False 

#------------------------------------------

class Distro(Item):

    def __init__(self,api,seed_data):
        self.api = api
        self.name = None
        self.kernel = None
        self.initrd = None
        self.kernel_options = ""
        if seed_data is not None:
           self.name = seed_data['name']
           self.kernel = seed_data['kernel']
           self.initrd = seed_data['initrd']
           self.kernel_options = seed_data['kernel_options']

    def set_kernel(self,kernel):
        if self.api.utils.find_kernel(kernel):
            self.kernel = kernel
            return True
        self.api.last_error = m("no_kernel")
        return False

    def set_initrd(self,initrd):
        if self.api.utils.find_initrd(initrd):
            self.initrd = initrd
            return True
        self.api.last_error = m("no_initrd")
        return False

    def is_valid(self):
        for x in (self.name,self.kernel,self.initrd):
            if x is None: return False
        return True

    def to_datastruct(self):
        return { 
           'name': self.name, 
           'kernel': self.kernel, 
           'initrd' : self.initrd,
           'kernel_options' : self.kernel_options
        }

    def __str__(self):
        kstr = self.api.utils.find_kernel(self.kernel)
        istr = self.api.utils.find_initrd(self.initrd)
        if kstr is None:
            kstr = "%s (NOT FOUND!)" % self.kernel
        elif os.path.isdir(self.kernel):
            kstr = "%s (FOUND BY SEARCH)" % kstr
        if istr is None:
            istr = "%s (NOT FOUND)" % self.initrd
        elif os.path.isdir(self.initrd):
            istr = "%s (FOUND BY SEARCH)" % istr
        buf = ""
        buf = buf + "distro      : %s\n" % self.name
        buf = buf + "kernel      : %s\n" % kstr
        buf = buf + "initrd      : %s\n" % istr
        buf = buf + "kernel opts : %s" % self.kernel_options
        return buf

#---------------------------------------------

class Group(Item):

    def __init__(self,api,seed_data):
        self.api = api
        self.name = None
        self.distro = None # a name, not a reference
        self.kickstart = None
        self.kernel_options = ""
        if seed_data is not None:
           self.name        = seed_data['name']
           self.distro    = seed_data['distro']
           self.kickstart = seed_data['kickstart'] 
           self.kernel_options = seed_data['kernel_options']

    def set_distro(self,distro_name):
        if self.api.get_distros().find(distro_name):
            self.distro = distro_name
            return True
        self.last_error = m("no_distro")
        return False

    def set_kickstart(self,kickstart):
        if self.api.utils.find_kickstart(kickstart):
            self.kickstart = kickstart
            return True
        self.last_error = m("no_kickstart")
        return False

    def is_valid(self):
        for x in (self.name, self.distro):
            if x is None: 
                return False
        return True

    def to_datastruct(self):
        return { 
            'name' : self.name,
            'distro' : self.distro,
            'kickstart' : self.kickstart,
            'kernel_options' : self.kernel_options
        }

    def __str__(self):
        buf = ""
        buf = buf + "group       : %s\n" % self.name
        buf = buf + "distro      : %s\n" % self.distro
        buf = buf + "kickstart   : %s\n" % self.kickstart
        buf = buf + "kernel opts : %s" % self.kernel_options
        return buf

#---------------------------------------------

class System(Item):

    def __init__(self,api,seed_data):
        self.api = api
        self.name = None
        self.group = None # a name, not a reference
        self.kernel_options = ""
        if seed_data is not None:
           self.name = seed_data['name']
           self.group = seed_data['group']
           self.kernel_options = seed_data['kernel_options']
    
    """
    A name can be a resolvable hostname (it instantly resolved and replaced with the IP), 
    any legal ipv4 address, or any legal mac address. ipv6 is not supported yet but _should_ be.
    See utils.py
    """
    def set_name(self,name):
        new_name = self.api.utils.find_system_identifier(name) 
        if new_name is None or new_name == False:
            self.api.last_error = m("bad_sys_name")
            return False
        self.name = name  # we check it add time, but store the original value.
        return True

    def set_group(self,group_name):
        if self.api.get_groups().find(group_name):
            self.group = group_name
            return True
        return False

    def is_valid(self):
        if self.name is None:
            self.api.last_error = m("bad_sys_name")
            return False
        if self.group is None:
            return False
        return True

    def to_datastruct(self):
        return {
           'name'   : self.name,
           'group'  : self.group,
           'kernel_options' : self.kernel_options
        }

    def __str__(self):
        buf = ""
        buf = buf + "system       : %s\n" % self.name
        buf = buf + "group        : %s\n" % self.group
        buf = buf + "kernel opts  : %s" % self.kernel_options
        return buf