~racb/ubuntu/precise/cobbler/858860

« back to all changes in this revision

Viewing changes to cobbler/modules/manage_import_freebsd.py

  • Committer: Package Import Robot
  • Author(s): Chuck Short, Chuck Short, Clint Byrum, Robie Basak
  • Date: 2011-11-15 12:35:40 UTC
  • mfrom: (1.1.8)
  • Revision ID: package-import@ubuntu.com-20111115123540-5g139uuhg7z0hv5d
Tags: 2.2.2-0ubuntu1
[Chuck Short]
* New upstream release:
  + Use dh_python2 everywhere.
  + Folded debian/patches/49_ubuntu_add_arm_arch_support.patch
    and debian/patches/56_ubuntu_arm_generate_pxe_files.patch
    into one patch for easier upstreaming.
  + Dropped debian/patches/50_fix_cobbler_timezone.patch:
    Fix upstream.
  + Dropped debian/patches/47_ubuntu_add_oneiric_codename.patch
    in favor of debian/patches/47_ubuntu_add_codenames.patch:
    It adds "precise" and drops unsupported releases as well.
  + Dropped debian/patches/41_update_tree_path_with_arch.patch:
    No longer needed.
  + Dropped debian/patches/55_ubuntu_branding.patch: Will be moved
    to orchestra

 [Clint Byrum]
 * debian/cobbler.postinst: create users.digest mode 0600 so it
   is not world readable. (LP: #858860)
 * debian/control: cobbler needs to depend on python-cobbler
   (LP: #863738)
 * debian/patches/58_fix_egg_cache.patch: Do not point dangerous
   PYTHON_EGG_CACHE at world writable directory. (LP: #858875)
 * debian/cobbler-common.install: remove users.digest as it is
   not required and contains a known password that would leave
   cobblerd vulnerable if started before configuration is done
 * debian/cobbler-web.postinst: fix perms on webui_sessions to
   be more secure (LP: #863755)

 [Robie Basak]
 * Backport safe YAML load from upstream. (LP: #858883)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
This is some of the code behind 'cobbler sync'.
 
3
 
 
4
Copyright 2006-2009, Red Hat, Inc
 
5
Michael DeHaan <mdehaan@redhat.com>
 
6
John Eckersberg <jeckersb@redhat.com>
 
7
 
 
8
This program is free software; you can redistribute it and/or modify
 
9
it under the terms of the GNU General Public License as published by
 
10
the Free Software Foundation; either version 2 of the License, or
 
11
(at your option) any later version.
 
12
 
 
13
This program is distributed in the hope that it will be useful,
 
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
GNU General Public License for more details.
 
17
 
 
18
You should have received a copy of the GNU General Public License
 
19
along with this program; if not, write to the Free Software
 
20
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 
21
02110-1301  USA
 
22
"""
 
23
 
 
24
import os
 
25
import os.path
 
26
import shutil
 
27
import time
 
28
import sys
 
29
import glob
 
30
import re
 
31
import traceback
 
32
import errno
 
33
from utils import popen2
 
34
from shlex import shlex
 
35
 
 
36
import codes
 
37
import utils
 
38
from cexceptions import *
 
39
import templar
 
40
 
 
41
import item_distro
 
42
import item_profile
 
43
import item_repo
 
44
import item_system
 
45
 
 
46
from utils import _
 
47
 
 
48
def register():
 
49
   """
 
50
   The mandatory cobbler module registration hook.
 
51
   """
 
52
   return "manage/import"
 
53
 
 
54
 
 
55
class ImportFreeBSDManager:
 
56
 
 
57
    def __init__(self,config,logger):
 
58
        """
 
59
        Constructor
 
60
        """
 
61
        self.logger        = logger
 
62
        self.config        = config
 
63
        self.api           = config.api
 
64
        self.distros       = config.distros()
 
65
        self.profiles      = config.profiles()
 
66
        self.systems       = config.systems()
 
67
        self.settings      = config.settings()
 
68
        self.repos         = config.repos()
 
69
        self.templar       = templar.Templar(config)
 
70
 
 
71
    # required function for import modules
 
72
    def what(self):
 
73
        return "import/freebsd"
 
74
 
 
75
    # required function for import modules
 
76
    def check_for_signature(self,path,cli_breed):
 
77
       signatures = [
 
78
          'etc/freebsd-update.conf',
 
79
          'boot/frames.4th',
 
80
          '8.2-RELEASE',
 
81
       ]
 
82
 
 
83
       #self.logger.info("scanning %s for a redhat-based distro signature" % path)
 
84
       for signature in signatures:
 
85
           d = os.path.join(path,signature)
 
86
           if os.path.exists(d):
 
87
               self.logger.info("Found a freebsd compatible signature: %s" % signature)
 
88
               return (True,signature)
 
89
 
 
90
       if cli_breed and cli_breed in self.get_valid_breeds():
 
91
           self.logger.info("Warning: No distro signature for kernel at %s, using value from command line" % path)
 
92
           return (True,None)
 
93
 
 
94
       return (False,None)
 
95
 
 
96
    # required function for import modules
 
97
    def run(self,pkgdir,name,path,network_root=None,kickstart_file=None,rsync_flags=None,arch=None,breed=None,os_version=None):
 
98
        self.pkgdir = pkgdir
 
99
        self.network_root = network_root
 
100
        self.kickstart_file = kickstart_file
 
101
        self.rsync_flags = rsync_flags
 
102
        self.arch = arch
 
103
        self.breed = breed
 
104
        self.os_version = os_version
 
105
        self.name = name
 
106
        self.path = path
 
107
        self.rootdir = path
 
108
 
 
109
        # some fixups for the XMLRPC interface, which does not use "None"
 
110
        if self.arch == "":           self.arch           = None
 
111
        if self.kickstart_file == "": self.kickstart_file = None
 
112
        if self.os_version == "":     self.os_version     = None
 
113
        if self.rsync_flags == "":    self.rsync_flags    = None
 
114
        if self.network_root == "":   self.network_root   = None
 
115
 
 
116
        # If no breed was specified on the command line, set it to "freebsd" for this module
 
117
        if self.breed == None:
 
118
            self.breed = "freebsd"
 
119
 
 
120
        # import takes a --kickstart for forcing selection that can't be used in all circumstances
 
121
 
 
122
        if self.kickstart_file and not self.breed:
 
123
            utils.die(self.logger,"Kickstart file can only be specified when a specific breed is selected")
 
124
 
 
125
        if self.os_version and not self.breed:
 
126
            utils.die(self.logger,"OS version can only be specified when a specific breed is selected")
 
127
 
 
128
        if self.breed and self.breed.lower() not in self.get_valid_breeds():
 
129
            utils.die(self.logger,"Supplied import breed is not supported by this module")
 
130
 
 
131
        # now walk the filesystem looking for distributions that match certain patterns
 
132
 
 
133
        self.logger.info("adding distros")
 
134
        distros_added = []
 
135
        # FIXME : search below self.path for isolinux configurations or known directories from TRY_LIST
 
136
        os.path.walk(self.path, self.distro_adder, distros_added)
 
137
 
 
138
        # find out if we can auto-create any repository records from the install tree
 
139
 
 
140
        if self.network_root is None:
 
141
            self.logger.info("associating repos")
 
142
            # FIXME: this automagic is not possible (yet) without mirroring
 
143
            self.repo_finder(distros_added)
 
144
 
 
145
        # find the most appropriate answer files for each profile object
 
146
 
 
147
        self.logger.info("associating answerfiles")
 
148
        self.kickstart_finder(distros_added)
 
149
 
 
150
        # ensure bootloaders are present
 
151
        self.api.pxegen.copy_bootloaders()
 
152
 
 
153
        return True
 
154
 
 
155
    # required function for import modules
 
156
    def get_valid_arches(self):
 
157
        return ["i386", "ia64", "ppc", "ppc64", "s390", "s390x", "x86_64", "x86",]
 
158
 
 
159
    # required function for import modules
 
160
    def get_valid_breeds(self):
 
161
        return ["freebsd",]
 
162
 
 
163
    # required function for import modules
 
164
    def get_valid_os_versions(self):
 
165
        return codes.VALID_OS_VERSIONS["freebsd"]
 
166
 
 
167
    def get_valid_repo_breeds(self):
 
168
        return ["rsync", "rhn", "yum",]
 
169
 
 
170
    def get_release_files(self):
 
171
        data = glob.glob(os.path.join(self.get_rootdir(), "*RELEASE"))
 
172
        data2 = []
 
173
        for x in data:
 
174
            b = os.path.basename(x)
 
175
            for valid_os in self.get_valid_os_versions():
 
176
                if b.find(valid_os) != -1:
 
177
                    data2.append(x)
 
178
        return data2
 
179
 
 
180
    def get_tree_location(self, distro):
 
181
        """
 
182
        Once a distribution is identified, find the part of the distribution
 
183
        that has the URL in it that we want to use for kickstarting the
 
184
        distribution, and create a ksmeta variable $tree that contains this.
 
185
        """
 
186
 
 
187
        base = self.get_rootdir()
 
188
 
 
189
        if self.network_root is None:
 
190
            dest_link = os.path.join(self.settings.webdir, "links", distro.name)
 
191
            # create the links directory only if we are mirroring because with
 
192
            # SELinux Apache can't symlink to NFS (without some doing)
 
193
            if not os.path.exists(dest_link):
 
194
                try:
 
195
                    os.symlink(base, dest_link)
 
196
                except:
 
197
                    # this shouldn't happen but I've seen it ... debug ...
 
198
                    self.logger.warning("symlink creation failed: %(base)s, %(dest)s") % { "base" : base, "dest" : dest_link }
 
199
            # how we set the tree depends on whether an explicit network_root was specified
 
200
            tree = "http://@@http_server@@/cblr/links/%s" % (distro.name)
 
201
            self.set_install_tree(distro, tree)
 
202
        else:
 
203
            # where we assign the answerfile source is relative to our current directory
 
204
            # and the input start directory in the crawl.  We find the path segments
 
205
            # between and tack them on the network source path to find the explicit
 
206
            # network path to the distro that Anaconda can digest.
 
207
            tail = self.path_tail(self.path, base)
 
208
            tree = self.network_root[:-1] + tail
 
209
            self.set_install_tree(distro, tree)
 
210
 
 
211
        return
 
212
 
 
213
    def repo_finder(self, distros_added):
 
214
        """
 
215
        This routine looks through all distributions and tries to find
 
216
        any applicable repositories in those distributions for post-install
 
217
        usage.
 
218
        """
 
219
 
 
220
        for distro in distros_added:
 
221
            self.logger.info("traversing distro %s" % distro.name)
 
222
            # FIXME : Shouldn't decide this the value of self.network_root ?
 
223
            if distro.kernel.find("ks_mirror") != -1:
 
224
                basepath = os.path.dirname(distro.kernel)
 
225
                top = self.get_rootdir()
 
226
                self.logger.info("descent into %s" % top)
 
227
                # FIXME : The location of repo definition is known from breed
 
228
                os.path.walk(top, self.repo_scanner, distro)
 
229
            else:
 
230
                self.logger.info("this distro isn't mirrored")
 
231
 
 
232
    def repo_scanner(self,distro,dirname,fnames):
 
233
        """
 
234
        This is an os.path.walk routine that looks for potential yum repositories
 
235
        to be added to the configuration for post-install usage.
 
236
        """
 
237
 
 
238
        matches = {}
 
239
        for x in fnames:
 
240
            if x == "base" or x == "repodata":
 
241
                self.logger.info("processing repo at : %s" % dirname)
 
242
                # only run the repo scanner on directories that contain a comps.xml
 
243
                gloob1 = glob.glob("%s/%s/*comps*.xml" % (dirname,x))
 
244
                if len(gloob1) >= 1:
 
245
                    if matches.has_key(dirname):
 
246
                        self.logger.info("looks like we've already scanned here: %s" % dirname)
 
247
                        continue
 
248
                    self.logger.info("need to process repo/comps: %s" % dirname)
 
249
                    self.process_comps_file(dirname, distro)
 
250
                    matches[dirname] = 1
 
251
                else:
 
252
                    self.logger.info("directory %s is missing xml comps file, skipping" % dirname)
 
253
                    continue
 
254
 
 
255
    def process_comps_file(self, comps_path, distro):
 
256
        """
 
257
        When importing Fedora/EL certain parts of the install tree can also be used
 
258
        as yum repos containing packages that might not yet be available via updates
 
259
        in yum.  This code identifies those areas.
 
260
        """
 
261
 
 
262
        processed_repos = {}
 
263
 
 
264
        masterdir = "repodata"
 
265
        if not os.path.exists(os.path.join(comps_path, "repodata")):
 
266
            # older distros...
 
267
            masterdir = "base"
 
268
 
 
269
        # figure out what our comps file is ...
 
270
        self.logger.info("looking for %(p1)s/%(p2)s/*comps*.xml" % { "p1" : comps_path, "p2" : masterdir })
 
271
        files = glob.glob("%s/%s/*comps*.xml" % (comps_path, masterdir))
 
272
        if len(files) == 0:
 
273
            self.logger.info("no comps found here: %s" % os.path.join(comps_path, masterdir))
 
274
            return # no comps xml file found
 
275
 
 
276
        # pull the filename from the longer part
 
277
        comps_file = files[0].split("/")[-1]
 
278
 
 
279
        try:
 
280
            # store the yum configs on the filesystem so we can use them later.
 
281
            # and configure them in the answerfile post, etc
 
282
 
 
283
            counter = len(distro.source_repos)
 
284
 
 
285
            # find path segment for yum_url (changing filesystem path to http:// trailing fragment)
 
286
            seg = comps_path.rfind("ks_mirror")
 
287
            urlseg = comps_path[seg+10:]
 
288
 
 
289
            # write a yum config file that shows how to use the repo.
 
290
            if counter == 0:
 
291
                dotrepo = "%s.repo" % distro.name
 
292
            else:
 
293
                dotrepo = "%s-%s.repo" % (distro.name, counter)
 
294
 
 
295
            fname = os.path.join(self.settings.webdir, "ks_mirror", "config", "%s-%s.repo" % (distro.name, counter))
 
296
 
 
297
            repo_url = "http://@@http_server@@/cobbler/ks_mirror/config/%s-%s.repo" % (distro.name, counter)
 
298
            repo_url2 = "http://@@http_server@@/cobbler/ks_mirror/%s" % (urlseg)
 
299
 
 
300
            distro.source_repos.append([repo_url,repo_url2])
 
301
 
 
302
            # NOTE: the following file is now a Cheetah template, so it can be remapped
 
303
            # during sync, that's why we have the @@http_server@@ left as templating magic.
 
304
            # repo_url2 is actually no longer used. (?)
 
305
 
 
306
            config_file = open(fname, "w+")
 
307
            config_file.write("[core-%s]\n" % counter)
 
308
            config_file.write("name=core-%s\n" % counter)
 
309
            config_file.write("baseurl=http://@@http_server@@/cobbler/ks_mirror/%s\n" % (urlseg))
 
310
            config_file.write("enabled=1\n")
 
311
            config_file.write("gpgcheck=0\n")
 
312
            config_file.write("priority=$yum_distro_priority\n")
 
313
            config_file.close()
 
314
 
 
315
            # don't run creatrepo twice -- this can happen easily for Xen and PXE, when
 
316
            # they'll share same repo files.
 
317
 
 
318
            if not processed_repos.has_key(comps_path):
 
319
                utils.remove_yum_olddata(comps_path)
 
320
                #cmd = "createrepo --basedir / --groupfile %s %s" % (os.path.join(comps_path, masterdir, comps_file), comps_path)
 
321
                cmd = "createrepo %s --groupfile %s %s" % (self.settings.createrepo_flags,os.path.join(comps_path, masterdir, comps_file), comps_path)
 
322
                utils.subprocess_call(self.logger, cmd, shell=True)
 
323
                processed_repos[comps_path] = 1
 
324
                # for older distros, if we have a "base" dir parallel with "repodata", we need to copy comps.xml up one...
 
325
                p1 = os.path.join(comps_path, "repodata", "comps.xml")
 
326
                p2 = os.path.join(comps_path, "base", "comps.xml")
 
327
                if os.path.exists(p1) and os.path.exists(p2):
 
328
                    shutil.copyfile(p1,p2)
 
329
 
 
330
        except:
 
331
            self.logger.error("error launching createrepo (not installed?), ignoring")
 
332
            utils.log_exc(self.logger)
 
333
 
 
334
    def distro_adder(self,distros_added,dirname,fnames):
 
335
        """
 
336
        This is an os.path.walk routine that finds distributions in the directory
 
337
        to be scanned and then creates them.
 
338
        """
 
339
 
 
340
        initrd = None
 
341
        kernel = None
 
342
 
 
343
        for x in fnames:
 
344
            adtls = []
 
345
 
 
346
            fullname = os.path.join(dirname,x)
 
347
            if os.path.islink(fullname) and os.path.isdir(fullname):
 
348
                if fullname.startswith(self.path):
 
349
                    self.logger.warning("avoiding symlink loop")
 
350
                    continue
 
351
                self.logger.info("following symlink: %s" % fullname)
 
352
                os.path.walk(fullname, self.distro_adder, distros_added)
 
353
 
 
354
            if x == "mfsroot.gz":
 
355
                initrd = os.path.join(dirname,x)
 
356
            if x == "pxeboot" or x == "pxeboot.bs":
 
357
                kernel = os.path.join(dirname,x)
 
358
 
 
359
            # if we've collected a matching kernel and initrd pair, turn the in and add them to the list
 
360
            if initrd is not None and kernel is not None:
 
361
                adtls.append(self.add_entry(dirname,kernel,initrd))
 
362
                kernel = None
 
363
                initrd = None
 
364
 
 
365
            for adtl in adtls:
 
366
                distros_added.extend(adtl)
 
367
 
 
368
 
 
369
    def add_entry(self,dirname,kernel,initrd):
 
370
        """
 
371
        When we find a directory with a valid kernel/initrd in it, create the distribution objects
 
372
        as appropriate and save them.  This includes creating xen and rescue distros/profiles
 
373
        if possible.
 
374
        """
 
375
 
 
376
        proposed_name = self.get_proposed_name(dirname,kernel)
 
377
        proposed_arch = self.get_proposed_arch(dirname)
 
378
 
 
379
        if self.arch and proposed_arch and self.arch != proposed_arch:
 
380
            utils.die(self.logger,"Arch from pathname (%s) does not match with supplied one %s"%(proposed_arch,self.arch))
 
381
 
 
382
        archs = self.learn_arch_from_tree()
 
383
        if not archs:
 
384
            if self.arch:
 
385
                archs.append( self.arch )
 
386
        else:
 
387
            if self.arch and self.arch not in archs:
 
388
                utils.die(self.logger, "Given arch (%s) not found on imported tree %s"%(self.arch,self.get_rootdir()))
 
389
        if proposed_arch:
 
390
            if archs and proposed_arch not in archs:
 
391
                self.logger.warning("arch from pathname (%s) not found on imported tree %s" % (proposed_arch,self.get_rootdir()))
 
392
                return
 
393
 
 
394
            archs = [ proposed_arch ]
 
395
 
 
396
        if len(archs)>1:
 
397
            self.logger.warning("- Warning : Multiple archs found : %s" % (archs))
 
398
 
 
399
        distros_added = []
 
400
 
 
401
        for pxe_arch in archs:
 
402
            name = proposed_name + "-" + pxe_arch
 
403
            existing_distro = self.distros.find(name=name)
 
404
 
 
405
            if existing_distro is not None:
 
406
                self.logger.warning("skipping import, as distro name already exists: %s" % name)
 
407
                continue
 
408
 
 
409
            else:
 
410
                self.logger.info("creating new distro: %s" % name)
 
411
                distro = self.config.new_distro()
 
412
 
 
413
            distro.set_name(name)
 
414
            distro.set_kernel(kernel)
 
415
            distro.set_initrd(initrd)
 
416
            distro.set_arch(pxe_arch)
 
417
            distro.set_breed(self.breed)
 
418
            # If a version was supplied on command line, we set it now
 
419
            if self.os_version:
 
420
                distro.set_os_version(self.os_version)
 
421
 
 
422
            self.distros.add(distro,save=True)
 
423
            distros_added.append(distro)
 
424
 
 
425
            existing_profile = self.profiles.find(name=name)
 
426
 
 
427
            # see if the profile name is already used, if so, skip it and
 
428
            # do not modify the existing profile
 
429
 
 
430
            if existing_profile is None:
 
431
                self.logger.info("creating new profile: %s" % name)
 
432
                #FIXME: The created profile holds a default answerfile, and should be breed specific
 
433
                profile = self.config.new_profile()
 
434
            else:
 
435
                self.logger.info("skipping existing profile, name already exists: %s" % name)
 
436
                continue
 
437
 
 
438
            # save our minimal profile which just points to the distribution and a good
 
439
            # default answer file
 
440
 
 
441
            profile.set_name(name)
 
442
            profile.set_distro(name)
 
443
            profile.set_kickstart(self.kickstart_file)
 
444
            profile.set_virt_type("vmware")
 
445
 
 
446
            # save our new profile to the collection
 
447
 
 
448
            self.profiles.add(profile,save=True)
 
449
 
 
450
        return distros_added
 
451
 
 
452
    def get_proposed_name(self,dirname,kernel=None):
 
453
        """
 
454
        Given a directory name where we have a kernel/initrd pair, try to autoname
 
455
        the distribution (and profile) object based on the contents of that path
 
456
        """
 
457
 
 
458
        if self.network_root is not None:
 
459
            name = self.name + "-".join(self.path_tail(os.path.dirname(self.path),dirname).split("/"))
 
460
        else:
 
461
            # remove the part that says /var/www/cobbler/ks_mirror/name
 
462
            name = "-".join(dirname.split("/")[5:])
 
463
        
 
464
        # Clean up name
 
465
        name = name.replace("-boot","")
 
466
 
 
467
        for separator in [ '-' , '_'  , '.' ] :
 
468
            for arch in [ "i386" , "x86_64" , "ia64" , "ppc64", "ppc32", "ppc", "x86" , "s390x", "s390" , "386" , "amd" ]:
 
469
                name = name.replace("%s%s" % ( separator , arch ),"")
 
470
 
 
471
        return name
 
472
 
 
473
    def get_proposed_arch(self,dirname):
 
474
        """
 
475
        Given an directory name, can we infer an architecture from a path segment?
 
476
        """
 
477
        if dirname.find("x86_64") != -1 or dirname.find("amd") != -1:
 
478
            return "x86_64"
 
479
        if dirname.find("ia64") != -1:
 
480
            return "ia64"
 
481
        if dirname.find("i386") != -1 or dirname.find("386") != -1 or dirname.find("x86") != -1:
 
482
            return "i386"
 
483
        if dirname.find("s390x") != -1:
 
484
            return "s390x"
 
485
        if dirname.find("s390") != -1:
 
486
            return "s390"
 
487
        if dirname.find("ppc64") != -1 or dirname.find("chrp") != -1:
 
488
            return "ppc64"
 
489
        if dirname.find("ppc32") != -1:
 
490
            return "ppc"
 
491
        if dirname.find("ppc") != -1:
 
492
            return "ppc"
 
493
        return None
 
494
 
 
495
    def arch_walker(self,foo,dirname,fnames):
 
496
        """
 
497
        See docs on learn_arch_from_tree.
 
498
 
 
499
        The TRY_LIST is used to speed up search, and should be dropped for default importer
 
500
        Searched kernel names are kernel-header, linux-headers-, kernel-largesmp, kernel-hugemem
 
501
 
 
502
        This method is useful to get the archs, but also to package type and a raw guess of the breed
 
503
        """
 
504
 
 
505
        # try to find a kernel header RPM and then look at it's arch.
 
506
        for x in fnames:
 
507
            if self.match_kernelarch_file(x):
 
508
                for arch in self.get_valid_arches():
 
509
                    if x.find(arch) != -1:
 
510
                        foo[arch] = 1
 
511
                for arch in [ "i686" , "amd64" ]:
 
512
                    if x.find(arch) != -1:
 
513
                        foo[arch] = 1
 
514
 
 
515
    def kickstart_finder(self,distros_added):
 
516
        """
 
517
        For all of the profiles in the config w/o a answerfile, use the
 
518
        given answerfile, or look at the kernel path, from that,
 
519
        see if we can guess the distro, and if we can, assign a answerfile
 
520
        if one is available for it.
 
521
        """
 
522
        for profile in self.profiles:
 
523
            distro = self.distros.find(name=profile.get_conceptual_parent().name)
 
524
            if distro is None or not (distro in distros_added):
 
525
                continue
 
526
 
 
527
            kdir = os.path.dirname(distro.kernel)
 
528
            if self.kickstart_file == None:
 
529
                for rpm in self.get_release_files():
 
530
                    # FIXME : This redhat specific check should go into the importer.find_release_files method
 
531
                    if rpm.find("notes") != -1:
 
532
                        continue
 
533
                    results = self.scan_pkg_filename(rpm)
 
534
                    # FIXME : If os is not found on tree but set with CLI, no answerfile is searched
 
535
                    if results is None:
 
536
                        self.logger.warning("No version found on imported tree")
 
537
                        continue
 
538
                    (flavor, major, minor) = results
 
539
                    version , ks = self.set_variance(flavor, major, minor, distro.arch)
 
540
                    if self.os_version:
 
541
                        if self.os_version != version:
 
542
                            utils.die(self.logger,"CLI version differs from tree : %s vs. %s" % (self.os_version,version))
 
543
                    ds = self.get_datestamp()
 
544
                    distro.set_comment(version)
 
545
                    distro.set_os_version(version)
 
546
                    if ds is not None:
 
547
                        distro.set_tree_build_time(ds)
 
548
                    profile.set_kickstart(ks)
 
549
                    if flavor == "freebsd":
 
550
                        self.logger.info("This is FreeBSD - adding boot files to fetchable files")
 
551
                        # add fetchable files to distro
 
552
                        distro.set_fetchable_files('boot/mfsroot.gz=$initrd boot/*=$webdir/ks_mirror/$distro/boot/')
 
553
                    self.profiles.add(profile,save=True)
 
554
 
 
555
            self.configure_tree_location(distro)
 
556
            self.distros.add(distro,save=True) # re-save
 
557
            self.api.serialize()
 
558
 
 
559
    def configure_tree_location(self, distro):
 
560
        """
 
561
        Once a distribution is identified, find the part of the distribution
 
562
        that has the URL in it that we want to use for kickstarting the
 
563
        distribution, and create a ksmeta variable $tree that contains this.
 
564
        """
 
565
 
 
566
        base = self.get_rootdir()
 
567
 
 
568
        if self.network_root is None:
 
569
            dest_link = os.path.join(self.settings.webdir, "links", distro.name)
 
570
            # create the links directory only if we are mirroring because with
 
571
            # SELinux Apache can't symlink to NFS (without some doing)
 
572
            if not os.path.exists(dest_link):
 
573
                try:
 
574
                    os.symlink(base, dest_link)
 
575
                except:
 
576
                    # this shouldn't happen but I've seen it ... debug ...
 
577
                    self.logger.warning("symlink creation failed: %(base)s, %(dest)s") % { "base" : base, "dest" : dest_link }
 
578
            # how we set the tree depends on whether an explicit network_root was specified
 
579
            tree = "http://@@http_server@@/cblr/links/%s" % (distro.name)
 
580
            self.set_install_tree( distro, tree)
 
581
        else:
 
582
            # where we assign the kickstart source is relative to our current directory
 
583
            # and the input start directory in the crawl.  We find the path segments
 
584
            # between and tack them on the network source path to find the explicit
 
585
            # network path to the distro that Anaconda can digest.
 
586
            tail = utils.path_tail(self.path, base)
 
587
            tree = self.network_root[:-1] + tail
 
588
            self.set_install_tree( distro, tree)
 
589
 
 
590
    def get_rootdir(self):
 
591
        return self.rootdir
 
592
 
 
593
    def get_pkgdir(self):
 
594
        if not self.pkgdir:
 
595
            return None
 
596
        return os.path.join(self.get_rootdir(),self.pkgdir)
 
597
 
 
598
    def set_install_tree(self, distro, url):
 
599
        distro.ks_meta["tree"] = url
 
600
 
 
601
    def learn_arch_from_tree(self):
 
602
        """
 
603
        If a distribution is imported from DVD, there is a good chance the path doesn't
 
604
        contain the arch and we should add it back in so that it's part of the
 
605
        meaningful name ... so this code helps figure out the arch name.  This is important
 
606
        for producing predictable distro names (and profile names) from differing import sources
 
607
        """
 
608
        result = {}
 
609
        # FIXME : this is called only once, should not be a walk
 
610
        if self.get_rootdir():
 
611
            os.path.walk(self.get_rootdir(), self.arch_walker, result)
 
612
        if result.pop("amd64",False):
 
613
            result["x86_64"] = 1
 
614
        if result.pop("i686",False):
 
615
            result["i386"] = 1
 
616
        return result.keys()
 
617
 
 
618
    def match_kernelarch_file(self, filename):
 
619
        """
 
620
        Is the given filename a kernel filename?
 
621
        """
 
622
 
 
623
        if not filename.endswith("rpm") and not filename.endswith("deb"):
 
624
            return False
 
625
        for match in ["kernel-header", "kernel-source", "kernel-smp", "kernel-largesmp", "kernel-hugemem", "linux-headers-", "kernel-devel", "kernel-"]:
 
626
            if filename.find(match) != -1:
 
627
                return True
 
628
        return False
 
629
 
 
630
    def scan_pkg_filename(self, filename):
 
631
        """
 
632
        Determine what the distro is based on the release package filename.
 
633
        """
 
634
        release_file = os.path.basename(filename)
 
635
 
 
636
        if release_file.lower().find("release") != -1:
 
637
            flavor = "freebsd"
 
638
            match = re.search(r'(\d).(\d)-RELEASE', release_file)
 
639
            if match:
 
640
                major   = match.group(1)
 
641
                minor   = match.group(2)
 
642
            else:
 
643
                # FIXME: what should we do if the re fails above?
 
644
                return None
 
645
 
 
646
        return (flavor, major, minor)
 
647
 
 
648
    def get_datestamp(self):
 
649
        """
 
650
        Based on a FreeBSD tree find the creation timestamp
 
651
        """
 
652
        pass
 
653
 
 
654
    def set_variance(self, flavor, major, minor, arch):
 
655
        """
 
656
        find the profile answerfile and set the distro breed/os-version based on what
 
657
        we can find out from the filenames and then return the answerfile
 
658
        path to use.
 
659
        """
 
660
 
 
661
        os_version = "%s%s.%s" % (flavor,major,minor)
 
662
        answerfile = "/var/lib/cobbler/kickstarts/default.ks"
 
663
 
 
664
        return os_version, answerfile
 
665
 
 
666
# ==========================================================================
 
667
 
 
668
def get_import_manager(config,logger):
 
669
    return ImportFreeBSDManager(config,logger)