[Et-mgmt-commits-list] [SCM] cobbler branch, master now at v0.4.8-47-gc85f740

Michael DeHaan mdehaan at redhat.com
Fri Jun 15 18:16:50 UTC 2007


Hello,

This is an automated email from the git hooks/update script, it was
generated because a ref change was pushed to the repository.

Updating branch, master,
       via  c85f740ddeb5518d8486237aa4448b7abc981787 (commit)
       via  5eaa46b5af9e89c881645eab69abfa787a6f7e29 (commit)
      from  53fb6c70d0a869de623e4e4b02d78e2a2b6b9f63 (commit)

- Log -----------------------------------------------------------------
commit c85f740ddeb5518d8486237aa4448b7abc981787
Author: Michael DeHaan <mdehaan at mdehaan.rdu.redhat.com>
Date:   Fri Jun 15 14:17:56 2007 -0400

    Optional override of --kickstart on a per-system basis.

commit 5eaa46b5af9e89c881645eab69abfa787a6f7e29
Author: Michael DeHaan <mdehaan at mdehaan.rdu.redhat.com>
Date:   Wed Jun 13 18:03:33 2007 -0400

    Keep track of depth of cobbler objects such that a pseudo-topological sort
    can be done at deserialization time.  The result of this is that full graph
    listing information can be reconstructed at time of config loading (up and
    down links), while only having to store the up links.
    
    This preserves the existing config file format while allowing for arbitrary inheritance and
    a reasonable measure of hand-editability.
    
    If changing profile relationships by hand, the cached depth info may be wrong,
    so some way to automatically resolve this could potentially be doable,
    but it's such a distinct corner case that I don't deem it neccessary at this point.
-----------------------------------------------------------------------

Diffstat:
 CHANGELOG               |    2 +-
 cobbler/action_sync.py  |   11 ++++++-----
 cobbler/cobbler.py      |    8 +++++---
 cobbler/config.py       |   14 +++++++++++---
 cobbler/item_distro.py  |    5 ++++-
 cobbler/item_profile.py |   14 ++++++++++----
 cobbler/item_repo.py    |    7 +++++--
 cobbler/item_system.py  |   43 +++++++++++++++++++++++++++++++++++--------
 cobbler/serializer.py   |   16 +++++++++++++++-
 docs/cobbler.pod        |   14 ++++++++++++++
 10 files changed, 106 insertions(+), 28 deletions(-)

diff --git a/CHANGELOG b/CHANGELOG
index a4d5f98..16242cb 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -13,7 +13,7 @@ Cobbler CHANGELOG
 - Patch to allow --createrepo-flags and to cache on import, fix multiple calls to createrepo
 - Various modifications to allow for profile inheritance
 - All variables in object tree now available for use in templating, nicer blending algorithms
-- ~2x speedup in unit tests due to caching of parent relationships
+- Optional override of --kickstart in system object
 
 * Thu Apr 26 2007 - 0.4.8
 - Make import friendlier for older distros
diff --git a/cobbler/action_sync.py b/cobbler/action_sync.py
index 17ec03b..9648a16 100644
--- a/cobbler/action_sync.py
+++ b/cobbler/action_sync.py
@@ -319,10 +319,11 @@ class BootSync:
 
     def validate_kickstart_for_specific_profile(self,g):
         distro = g.get_conceptual_parent()
+        meta = utils.blender(False, g)
         if distro is None:
            raise CX(_("profile %(profile)s references missing distro %(distro)s") % { "profile" : g.name, "distro" : g.distro })
-        kickstart_path = utils.find_kickstart(g.kickstart)
-        if kickstart_path is not None and os.path.exists(g.kickstart):
+        kickstart_path = utils.find_kickstart(meta["kickstart"])
+        if kickstart_path is not None and os.path.exists(kickstart_path):
            # the input is an *actual* file, hence we have to copy it
            copy_path = os.path.join(
                self.settings.webdir,
@@ -424,7 +425,8 @@ class BootSync:
         if profile is None:
             raise CX(_("system %(system)s references missing profile %(profile)s") % { "system" : s.name, "profile" : s.profile })
         distro = profile.get_conceptual_parent()
-        kickstart_path = utils.find_kickstart(profile.kickstart)
+        meta = utils.blender(False, s)
+        kickstart_path = utils.find_kickstart(meta["kickstart"])
         if kickstart_path and os.path.exists(kickstart_path):
             pxe_fn = utils.get_config_filename(s)
             copy_path = os.path.join(self.settings.webdir,
@@ -434,7 +436,6 @@ class BootSync:
             self.mkdir(copy_path)
             dest = os.path.join(copy_path, "ks.cfg")
             try:
-                meta = utils.blender(False, s)
                 ksmeta = meta["ks_meta"]
                 del meta["ks_meta"]
                 meta.update(ksmeta) # make available at top level
@@ -445,7 +446,7 @@ class BootSync:
                 self.apply_template(kfile, meta, dest)
                 kfile.close()
             except:
-                raise CX(_("Error templating file %s to %s") % { "src" : s.kickstart, "dest" : dest })
+                raise CX(_("Error templating file %s to %s") % { "src" : meta["kickstart"], "dest" : dest })
 
     def apply_template(self, data_input, metadata, out_path):
         """
diff --git a/cobbler/cobbler.py b/cobbler/cobbler.py
index fb34871..55edbc7 100755
--- a/cobbler/cobbler.py
+++ b/cobbler/cobbler.py
@@ -495,11 +495,13 @@ class BootCLI:
            '--kopts'       :  lambda(a) : sys.set_kernel_options(a),
            '--ksmeta'      :  lambda(a) : sys.set_ksmeta(a),
            '--hostname'    :  lambda(a) : sys.set_hostname(a),
-           '--pxe-address' :  lambda(a) : sys.set_ip_address(a), # deprecated
+           '--pxe-address' :  lambda(a) : sys.set_ip_address(a),  # deprecated
            '--ip-address'  :  lambda(a) : sys.set_ip_address(a),
-           '--ip'          :  lambda(a) : sys.set_ip_address(a), # alias
+           '--ip'          :  lambda(a) : sys.set_ip_address(a),  # alias
            '--mac-address' :  lambda(a) : sys.set_mac_address(a),
-           '--mac'         :  lambda(a) : sys.set_mac_address(a) # alias
+           '--mac'         :  lambda(a) : sys.set_mac_address(a), # alias
+           '--kickstart'   :  lambda(a) : sys.set_kickstart(a),
+           '--kick-start'  :  lambda(a) : sys.set_kickstart(a)
         }
         def on_ok():
             self.api.systems().add(sys, with_copy=self.api.sync_flag)
diff --git a/cobbler/config.py b/cobbler/config.py
index cd19d00..08bd770 100644
--- a/cobbler/config.py
+++ b/cobbler/config.py
@@ -59,10 +59,16 @@ class Config:
        self._settings     = settings.Settings() # not a true collection
        self._repos        = repos.Repos(weakref.proxy(self))
        self._classes = [
+          self._settings,
+          self._distros,
+          self._profiles,
+          self._systems,
+          self._repos
+       ]
+       self._graph_classes = [
           self._distros,
           self._profiles,
           self._systems,
-          self._settings,
           self._repos
        ]
        self.file_check()
@@ -157,8 +163,10 @@ class Config:
        """
        Load the object hierachy from disk, using the filenames referenced in each object.
        """
-       for x in self._classes:
-          if not serializer.deserialize(x):
+       if not serializer.deserialize(self._settings,topological=False):
+          return False
+       for x in self._graph_classes:
+          if not serializer.deserialize(x,topological=True):
               return False
        return True
 
diff --git a/cobbler/item_distro.py b/cobbler/item_distro.py
index b6a5bea..8fa7682 100644
--- a/cobbler/item_distro.py
+++ b/cobbler/item_distro.py
@@ -38,6 +38,7 @@ class Distro(item.Item):
         self.arch           = ('x86',    '<<inherit>>')[is_subobject]
         self.breed          = ('redhat', '<<inherit>>')[is_subobject]
         self.source_repos   = ([],       '<<inherit>>')[is_subobject]
+        self.depth          = 0
 
     def make_clone(self):
         ds = self.to_datastruct()
@@ -69,6 +70,7 @@ class Distro(item.Item):
         self.arch           = self.load_item(seed_data,'arch','x86')
         self.breed          = self.load_item(seed_data,'breed','redhat')
         self.source_repos   = self.load_item(seed_data,'source_repos',[])
+        self.depth          = self.load_item(seed_data,'depth',0)
 
         # backwards compatibility -- convert string entries to dicts for storage
         if type(self.kernel_options) != dict:
@@ -161,7 +163,8 @@ class Distro(item.Item):
            'arch'           : self.arch,
            'breed'          : self.breed,
            'source_repos'   : self.source_repos,
-           'parent'         : self.parent
+           'parent'         : self.parent,
+           'depth'          : self.depth
         }
 
     def printable(self):
diff --git a/cobbler/item_profile.py b/cobbler/item_profile.py
index 79a7dcb..b653ea3 100644
--- a/cobbler/item_profile.py
+++ b/cobbler/item_profile.py
@@ -40,6 +40,7 @@ class Profile(item.Item):
         self.virt_file_size  = (5,                               '<<inherit>>')[is_subobject]
         self.virt_ram        = (512,                             '<<inherit>>')[is_subobject]
         self.repos           = ("",                              '<<inherit>>')[is_subobject]
+        self.depth           = 1
 
     def from_datastruct(self,seed_data):
         """
@@ -53,7 +54,8 @@ class Profile(item.Item):
         self.kernel_options  = self.load_item(seed_data,'kernel_options')
         self.ks_meta         = self.load_item(seed_data,'ks_meta')
         self.repos           = self.load_item(seed_data,'repos', [])
-      
+        self.depth           = self.load_item(seed_data,'depth', 1)     
+ 
         # backwards compatibility
         if type(self.repos) != list:
             self.set_repos(self.repos)
@@ -89,14 +91,17 @@ class Profile(item.Item):
         if found is None:
            raise CX(_("profile %s not found, inheritance not possible") % parent_name)
         self.parent = parent_name       
+        self.depth = found.depth + 1
 
     def set_distro(self,distro_name):
         """
 	Sets the distro.  This must be the name of an existing
 	Distro object in the Distros collection.
 	"""
-        if self.config.distros().find(distro_name):
+        d = self.config.distros().find(distro_name)
+        if d is not None:
             self.distro = distro_name
+            self.depth  = d.depth +1 # reset depth if previously a subprofile and now top-level
             return True
         raise CX(_("distribution not found"))
 
@@ -127,7 +132,7 @@ class Profile(item.Item):
     def set_kickstart(self,kickstart):
         """
 	Sets the kickstart.  This must be a NFS, HTTP, or FTP URL.
-	Minor checking of the URL is performed here.
+	Or filesystem path.  Minor checking of the URL is performed here.
 	"""
         if utils.find_kickstart(kickstart):
             self.kickstart = kickstart
@@ -217,7 +222,8 @@ class Profile(item.Item):
             'virt_ram'         : self.virt_ram,
             'ks_meta'          : self.ks_meta,
             'repos'            : self.repos,
-            'parent'           : self.parent
+            'parent'           : self.parent,
+            'depth'            : self.depth
         }
 
     def printable(self):
diff --git a/cobbler/item_repo.py b/cobbler/item_repo.py
index c9846b1..19b2e3f 100644
--- a/cobbler/item_repo.py
+++ b/cobbler/item_repo.py
@@ -35,7 +35,8 @@ class Repo(item.Item):
         self.local_filename   = ("",         '<<inherit>>')[is_subobject]
         self.rpm_list         = ("",         '<<inherit>>')[is_subobject]
         self.createrepo_flags = ("-c cache", '<<inherit>>')[is_subobject]
-        
+        self.depth            = 2  # arbitrary, as not really apart of the graph         
+
     def from_datastruct(self,seed_data):
         self.parent           = self.load_item(seed_data, 'parent')
         self.name             = self.load_item(seed_data, 'name')
@@ -44,6 +45,7 @@ class Repo(item.Item):
         self.local_filename   = self.load_item(seed_data, 'local_filename')
         self.rpm_list         = self.load_item(seed_data, 'rpm_list')
         self.createrepo_flags = self.load_item(seed_data, 'createrepo_flags', '-c cache')
+        self.depth            = self.load_item(seed_data, 'depth', 2)
         return self
 
     def set_name(self,name):
@@ -130,7 +132,8 @@ class Repo(item.Item):
            'local_filename'   : self.local_filename,
            'rpm_list'         : self.rpm_list,
            'createrepo_flags' : self.createrepo_flags,
-           'parent'           : self.parent
+           'parent'           : self.parent,
+           'depth'            : self.depth
         }
 
     def printable(self):
diff --git a/cobbler/item_system.py b/cobbler/item_system.py
index c2e8243..479430e 100644
--- a/cobbler/item_system.py
+++ b/cobbler/item_system.py
@@ -29,8 +29,6 @@ class System(item.Item):
         return cloned
 
     def clear(self,is_subobject=False):
-        # names of cobbler repo definitions
-  
         self.name            = None
         self.profile         = (None,     '<<inherit>>')[is_subobject]
         self.kernel_options  = ({},       '<<inherit>>')[is_subobject]
@@ -39,15 +37,19 @@ class System(item.Item):
         self.mac_address     = ("",       '<<inherit>>')[is_subobject]  
         self.netboot_enabled = (1,        '<<inherit>>')[is_subobject] 
         self.hostname        = ("",       '<<inheirt>>')[is_subobject]
+        self.depth           = 2
+        self.kickstart       = "<<inherit>>"   # use value in profile
 
     def from_datastruct(self,seed_data):
 
         self.parent          = self.load_item(seed_data, 'parent')
         self.name            = self.load_item(seed_data, 'name')
         self.profile         = self.load_item(seed_data, 'profile')
-        self.kernel_options  = self.load_item(seed_data, 'kernel_options')
-        self.ks_meta         = self.load_item(seed_data, 'ks_meta')
-        
+        self.kernel_options  = self.load_item(seed_data, 'kernel_options', {})
+        self.ks_meta         = self.load_item(seed_data, 'ks_meta', {})
+        self.depth           = self.load_item(seed_data, 'depth', 2)        
+        self.kickstart       = self.load_item(seed_data, 'kickstart', '<<inherit>>')
+ 
         # backwards compat, load --ip-address from two possible sources.
         # the old --pxe-address was a bit of a misnomer, new value is --ip-address
 
@@ -166,7 +168,7 @@ class System(item.Item):
            return True
         raise CX(_("invalid format for MAC address"))
 
-    def set_ip_address(self,address):
+    def set_pxe_address(self,address):
         # backwards compatibility for API users:
         return self.set_ip_address(address)
 
@@ -175,8 +177,10 @@ class System(item.Item):
 	Set the system to use a certain named profile.  The profile
 	must have already been loaded into the Profiles collection.
 	"""
-        if self.config.profiles().find(profile_name):
+        p = self.config.profiles().find(profile_name)
+        if p is not None:
             self.profile = profile_name
+            self.depth = p.depth + 1 # subprofiles have varying depths.
             return True
         raise CX(_("invalid profile name"))
 
@@ -208,11 +212,31 @@ class System(item.Item):
         # NOTE: this validation code does not support inheritable distros at this time.
         # this is by design as inheritable systems don't make sense.
         if self.name is None:
+            raise CX(_("need to specify a name for this object"))
             return False
         if self.profile is None:
+            raise CX(_("need to specify a profile for this system"))
             return False
         return True
 
+    def set_kickstart(self,kickstart):
+        """
+        Sets the kickstart.  This must be a NFS, HTTP, or FTP URL.
+        Or filesystem path. Minor checking of the URL is performed here.
+
+        NOTE -- usage of the --kickstart parameter in the profile
+        is STRONGLY encouraged.  This is only for exception cases
+        where a user already has kickstarts made for each system
+        and can't leverage templating.  Profiles provide an important
+        abstraction layer -- assigning systems to defined and repeatable 
+        roles.
+        """
+        if utils.find_kickstart(kickstart):
+            self.kickstart = kickstart
+            return True
+        raise CX(_("kickstart not found"))
+
+
     def to_datastruct(self):
         return {
            'name'            : self.name,
@@ -223,7 +247,9 @@ class System(item.Item):
            'netboot_enabled' : self.netboot_enabled,
            'hostname'        : self.hostname,
            'mac_address'     : self.mac_address,
-           'parent'          : self.parent
+           'parent'          : self.parent,
+           'depth'           : self.depth,
+           'kickstart'       : self.kickstart
         }
 
     def printable(self):
@@ -237,5 +263,6 @@ class System(item.Item):
         buf = buf + _("pxe info set?    : %s\n") % self.is_pxe_supported()
         buf = buf + _("config id        : %s\n") % utils.get_config_filename(self)
         buf = buf + _("netboot enabled? : %s\n") % self.netboot_enabled 
+        buf = buf + _("kickstart        : %s\n") % self.kickstart
         return buf
 
diff --git a/cobbler/serializer.py b/cobbler/serializer.py
index 38a182d..39cb982 100644
--- a/cobbler/serializer.py
+++ b/cobbler/serializer.py
@@ -49,7 +49,7 @@ def serialize(obj):
     fd.close()
     return True
 
-def deserialize(obj):
+def deserialize(obj,topological=False):
     """
     Populate an existing object with the contents of datastruct.
     Object must "implement" Serializable.  Returns True assuming
@@ -69,7 +69,21 @@ def deserialize(obj):
     data = fd.read()
     datastruct = yaml.load(data).next()  # first record
     fd.close()
+
+    if topological:
+       # in order to build the graph links from the flat list, sort by the
+       # depth of items in the graph.  If an object doesn't have a depth, sort it as
+       # if the depth were 0.  It will be assigned a proper depth at serialization
+       # time.  This is a bit cleaner implementation wise than a topological sort,
+       # though that would make a shiny upgrade.
+       datastruct.sort(cmp=__depth_cmp)
     obj.from_datastruct(datastruct)
     return True
 
+def __depth_cmp(item1, item2):
+    if not item1.has_key("depth"):
+       return 1
+    if not item2.has_key("depth"):
+       return -1
+    return cmp(item1["depth"],item2["depth"])
 
diff --git a/docs/cobbler.pod b/docs/cobbler.pod
index da83ef6..6746187 100644
--- a/docs/cobbler.pod
+++ b/docs/cobbler.pod
@@ -133,6 +133,16 @@ If this parameter is not provided, the kickstart file will default to /etc/cobbl
 (optional) a space delimited list of all the repos (created with "cobbler repo add" and "cobbler reposync") that this profile
 can make use of during kickstart installation.  For example, an example might be --repos="fc6i386updates fc6i386extras".
 
+=item inherit
+
+(optional) profiles may inherit from other profiles in lieu of specifing --distro.  Inherited profiles will override any settings specified in their parent, with the exception of --ksmet(templating) and --kopts (kernel options), which will be blended together.  
+
+Example:  If profile A has --kopts="x=7 y=2", B inherits from A, and B has --kopts="x=9 z=2", the actual kernel options that will be used for B are "x=7 y=2 z=2". 
+
+Example:  If profile B has --virt-ram=256 and A has --virt-ram of 512, profile B will use the value 256.  
+
+Example:  If profile A has a --virt-file-size of 5 and B does not specify a size, B will use the value from A.
+
 =back
 
 =head2 ADDING A SYSTEM
@@ -189,6 +199,10 @@ basically ignored.
 
 Example: --hostname=mycomputer.example.com
 
+=item --kickstart
+
+(optional) While it is recommended that the --kickstart parameter is only used within for the "profile add" command, there are scenarios when an install base switching to cobbler may have kickstarts created on a per-system basis (one kickstart for each system, nothing shared) and may not want to immediately make use of the cobbler templating system.  This allows specifing a kickstart for use on a per-system basis.  Creation of a parent profile is still required.  If the kickstart is a filesystem location, it will still be treated as a cobbler template.
+
 =end
 
 =head2 ADDING A REPOSITORY TO MIRROR

hooks/update
---
Git Source Code Management System
hooks/update refs/heads/master \
  53fb6c70d0a869de623e4e4b02d78e2a2b6b9f63 \
  c85f740ddeb5518d8486237aa4448b7abc981787




More information about the Et-mgmt-commits-list mailing list