[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

a replacement for .discinfo?



Hi folks,

In the course of messing with various installer tools, I've ended up
writing some python code that reads and writes an XML metadata file that
is (basically) an easily-parsable superset of .discinfo.

A quick overview of the concepts involved here, from the top down:

A "compose" is, basically, an entire distribution - all the installable
trees for all the various arches, plus iso sets, plus maybe some SRPMS
and debuginfo packages that go along with them. I suppose I could rename
this to "distro" but we've been using this terminology for so long that
it's just stuck.

A "tree" is a directory layout with all the packages and images you need
to install from.
An "isoset" is (surprise!) a full set of isos that make up the
distribution. 

Okay, here's some examples. The tool that does the composing (pungi,
distill, etc.) should create .compose.xml in the top-level of the
compose dir. That file looks approximately like this:

<compose id="rawhide-20070122" time="1169485348">
  <debug arch="i386">development/i386/debug</debug>
  <debug arch="x86_64">development/x86_64/debug</debug>
  ...
  <source arch="i386">development/source/SRPMS</source>
  <source arch="x86_64">development/source/SRPMS</source>
  ...
  <tree arch="i386">development/i386/os</tree>
  <tree arch="x86_64">development/x86_64/os</tree>
</compose>

This defines the id of the compose (which should be unique for each
compose, and would be nice if it was human-understandable like this one)
and a timestamp that lets you know when it was created.

Really this file just exists to tell point you to the actual contents of
the compose, and where it all lives - there's debuginfo packages here,
sources here, trees here, and so on. Later we should also have:
  <isoset arch="i386">development/i386/iso</isoset>

Each of these items points to a directory where another xml file will
have further information - a "tree" directory will contain a file named
".tree.xml", ".isoset.xml" for isosets, etc.

So here's an example of tree.xml:

<tree id="1169482851.57">
  <compose>rawhide-20070122</compose>
  <family>Fedora Core 6.89</family>
  <version>6.89</version>
  <time>1169482851.57</time>
  <arch>i386</arch>
  <file type="kernel">images/pxeboot/vmlinuz</file>
  <file type="initrd">images/pxeboot/initrd.img</file>
  <file type="boot.iso">images/boot.iso</file>
</tree>

Each tree has a unique ID. Like the composes, it can be any freeform
string, but it must be unique among trees. (A better choice might be
something like "rawhide-20070122.i386" - this is still open to change.)

In the xml structure we've got the name of the parent compose, the
'family' string (the second line of .discinfo), the version (as a
floating point number), the timestamp of the tree (which I am using as
the tree id, due to the fact that it's unique), and the tree's arch. 

Finally there's a list of important files that other applications might
like to know the location of. For my purposes, those three files are the
ones I care about - other applications might want other file items to be
included here.

Okay, so here's the questions:

1) Is this enough info to model trees and composes? What about iso sets?
2) Does anaconda have all the metadata that I'm writing out here?
3) Does this stuff look sane enough for inclusion in anaconda?

Let me know what you think. I'm still not completely sure how to deal
with iso sets and such. I'm sure I'm missing some vital piece of
information from .discinfo that wasn't needed for my purposes, so please
tell me what this lacks.

Thanks in advance!

-w
#!/usr/bin/python
# compose.py - A python class to represent a compose, and read/write XML for it.
# Copyright (C) 2007 Red Hat, Inc.
# Author: Will Woods <wwoods redhat com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

from tree import Debug, Source, Tree, IsoSet
import cElementTree as ET
import os.path

class Compose(object):
    def __init__(self, **kwargs):
        self.id=""
        self.xmldir=""
        self.time=0.0
        # these should be lists of the corresponding objects
        self.trees=[]
        self.isos=[]
        self.debug=[]
        self.source=[]

        if 'xml' in kwargs:
            self.xmldir=os.path.dirname(kwargs['xml'])
            self.parse(kwargs['xml'])

    def parse(self,xml):
        elemtree = ET.parse(xml)
        r=elemtree.getroot()
        self.id=r.get('id')
        self.time=r.get('time')
        for type, obj, set in (['debug',Debug,self.debug],
                               ['source',Source,self.source],
                               ['tree',Tree,self.trees],
                               ['isos',IsoSet,self.isos]):
            for e in r.findall(type):
                c=obj()
                c.arch=e.get('arch')
                c.variant=e.get('variant') or ""
                c.reldir=e.text
                try:
                    c.parse(os.path.join(self.xmldir,c.reldir,'.'+type+'.xml'))
                    c.compose=self.id
                except:
                    pass
                set.append(c)
        return elemtree

    def element(self):
        compose = ET.Element('compose',id=self.id,time=str(self.time))
        for type, set in {'debug':self.debug,'source':self.source,
                          'tree':self.trees,'isos':self.isos}.items():
            for i in set:
                elem = ET.SubElement(compose,type,arch=i.arch)
                if type in ('tree','isos') and i.variant:
                    elem.attrib['variant'] = i.variant
                elem.text=i.reldir
        return compose

    def xml(self):
        return ET.tostring(indent(self.element()))

    def __repr__(self):
        return "Compose(%s)" % self.id

    def __str__(self):
        return "%s%s" % (self.id, str(self.trees))
#!/usr/bin/python
# tree.py - objects for distro trees and things like them
# Copyright (C) 2007 Red Hat, Inc.
# Author: Will Woods <wwoods redhat com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

import cElementTree as ET

class ComposeTarget(object):
    '''A generic base class for trees and other things built in a compose'''
    def __init__(self,**kwargs):
        self.id=""      # unique ID to represent this tree
        self.compose="" # compose ID
        self.time=0.0   # timestamp
        self.arch=""    # arch
        self.reldir=""  # (optional) location relative to parent compose
        # convenience - what type am I?
        self.type=type(self).__name__
        if 'xml' in kwargs:
            self.parse(kwargs['xml'])
        elif 'dict' in kwargs:
            self.__dict__ = kwargs['dict']

    def parse(self,xml):
        elemtree=ET.parse(xml)
        r=elemtree.getroot()
        self.id = r.get('id')
        self.compose=r.find('compose').text
        self.time=float(r.find('time').text)
        self.arch=r.find('arch').text
        return elemtree

    def element(self,klist=()):
        root=ET.Element(self.type.lower(),id=self.id)
        for k,v in self.__dict__.items():
            if (k in klist+('compose','time','arch')) and v:
                e=ET.SubElement(root,k)
                e.text=str(v)
        return root

    def xml(self):
        return ET.tostring(indent(self.element()))

    def __str__(self):
        str='%s/%s' % (self.compose,self.arch)
        if self.variant: str = str + "/%s" % self.variant
        return "<%s %s>" % (self.type, str)

class Source(ComposeTarget):
    '''A class for a SRPM dir'''
    pass

class Debug(ComposeTarget):
    '''A class for a debuginfo dir'''
    pass

class Tree(ComposeTarget):
    '''A class that represents an installable tree'''
    # This subclass adds family, version, variant, and files
    def __init__(self,**kwargs):
        self.family=""
        self.version=0.0
        self.variant=""
        self.files={}
        ComposeTarget.__init__(self,**kwargs)

    def parse(self,xml):
        elemtree=ComposeTarget.parse(self,xml)
        r=elemtree.getroot()
        self.family=r.find('family').text
        self.version=float(r.find('version').text)
        e=r.find('variant')
        if e:
            self.variant=e.text
        for e in r.findall('file'):
            self.files[e.get('type')]=e.text

    def element(self):
        root=ComposeTarget.element(self,('family','version','variant'))
        for type,path in self.files.items():
            e=ET.SubElement(root,'file',type=type)
            e.text=path
        return root

    def __short_family(self):
        fam_map={"Fedora Core ":"FC",
                 "Red Hat Enterprise Linux ":"RHEL"}
        s=self.family
        for longf,shortf in fam_map.items():
            if s.startswith(longf):
                s=s.replace(longf,shortf,1)
        return s

class IsoSet(Tree):
    '''A class that represents a set of ISO images'''
    # The difference here is that we add some attributes to the 'files'
    # list, e.g.:
    # <file type="cd" number="3">6/i386/iso/FC-6-i386-disc3.iso</file>
    # other types:"dvd", "rescue"
    # FIXME not sure how to actually IMPLEMENT that. class ISO(object): ?
    pass

Attachment: signature.asc
Description: This is a digitally signed message part


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]