summaryrefslogtreecommitdiffstats
path: root/cobbler/collection.py
blob: de7ae72bf7854d1da9f12ca8902b7080b6f11e2c (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
"""
Base class for any serializable list of things...

Copyright 2006, Red Hat, Inc
Michael DeHaan <mdehaan@redhat.com>

This software may be freely redistributed under the terms of the GNU
general public license.

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., 675 Mass Ave, Cambridge, MA 02139, USA.
"""

import exceptions
from cexceptions import *
import serializable
import utils
import glob
import sub_process

import action_litesync
import item_system
import item_profile
import item_distro
import item_repo

from rhpl.translate import _, N_, textdomain, utf8

class Collection(serializable.Serializable):

    def __init__(self,config):
        """
        Constructor.
        """
        self.config = config
        self.clear()

    def factory_produce(self,config,seed_data):
        """
        Must override in subclass.  Factory_produce returns an Item object
        from datastructure seed_data
        """
        raise exceptions.NotImplementedError

    def filename(self):
        """
        Must override in subclass.  See Serializable
        """
        raise exceptions.NotImplementedError

    def clear(self):
        """
        Forget about objects in the collection.
        """
        self.listing = {}

    def find(self, name=None, return_list=False, **kargs):
        """
        Return first object in the collection that maches all item='value'
        pairs passed, else return None if no objects can be found.
        When return_list is set, can also return a list.  Empty list
        would be returned instead of None in that case.
        """

        matches = []

        # support the old style innovation without kwargs
        if name is not None:
            kargs["name"] = name

        # no arguments is an error, so we don't return a false match
        if len(kargs) == 0:
            raise CX(_("calling find with no arguments"))

        # performance: if the only key is name we can skip the whole loop
        if len(kargs) == 1 and kargs.has_key("name") and not return_list:
            return self.listing.get(kargs["name"].lower(), None)

        for (name, obj) in self.listing.iteritems():
            if obj.find_match(kargs):
                matches.append(obj)

        if not return_list:
            if len(matches) == 0:
                return None
            return matches[0]
        else:
            return matches

    def to_datastruct(self):
        """
        Serialize the collection
        """
        datastruct = [x.to_datastruct() for x in self.listing.values()]
        return datastruct

    def from_datastruct(self,datastruct):
        if datastruct is None:
            return
        for seed_data in datastruct:
            item = self.factory_produce(self.config,seed_data)
            self.add(item)

    def add(self,ref,with_copy=False):
        """
        Add an object to the collection, if it's valid.  Returns True
        if the object was added to the collection.  Returns False if the
        object specified by ref deems itself invalid (and therefore
        won't be added to the collection).

        with_copy is a bit of a misnomer, but lots of internal add operations
        can run with "with_copy" as False. True means a real final commit, as if
        entered from the command line (or basically, by a user).  
 
        With with_copy as False, the particular add call might just be being run 
        during deserialization, in which case extra semantics around the add don't really apply.
        So, in that case, don't run any triggers and don't deal with any actual files.

        """
        if ref is None or not ref.is_valid():
            raise CX(_("insufficient or invalid arguments supplied"))

        if ref.COLLECTION_TYPE != self.collection_type():
            raise CX(_("API error: storing wrong data type in collection"))

        if not with_copy:
            # don't need to run triggers, so add it already ...
            self.listing[ref.name.lower()] = ref


        # perform filesystem operations
        if with_copy:
            # failure of a pre trigger will prevent the object from being added
            self._run_triggers(ref,"/var/lib/cobbler/triggers/add/%s/pre/*" % self.collection_type())
            self.listing[ref.name.lower()] = ref

            # save just this item if possible, if not, save
            # the whole collection
            self.config.serialize_item(self, ref)

            lite_sync = action_litesync.BootLiteSync(self.config)
            if isinstance(ref, item_system.System):
                lite_sync.add_single_system(ref.name)
            elif isinstance(ref, item_profile.Profile):
                lite_sync.add_single_profile(ref.name) 
            elif isinstance(ref, item_distro.Distro):
                lite_sync.add_single_distro(ref.name)
            elif isinstance(ref, item_repo.Repo):
                pass
            else:
                print _("Internal error. Object type not recognized: %s") % type(ref)
        
            # save the tree, so if neccessary, scripts can examine it.
            self._run_triggers(ref,"/var/lib/cobbler/triggers/add/%s/post/*" % self.collection_type())
        
        # update children cache in parent object
        parent = ref.get_parent()
        if parent != None:
            parent.children[ref.name] = ref
        return True

    def _run_triggers(self,ref,globber):
        triggers = glob.glob(globber)
        for file in triggers:
            rc = sub_process.call("%s %s" % (file,ref.name), shell=True)
            if rc != 0:
               raise CX(_("cobbler trigger failed: %(file)s returns %(code)d") % { "file" : file, "code" : rc })

    def printable(self):
        """
        Creates a printable representation of the collection suitable
        for reading by humans or parsing from scripts.  Actually scripts
        would be better off reading the YAML in the config files directly.
        """
        values = self.listing.values()[:] # copy the values
        values.sort() # sort the copy (2.3 fix)
        results = []
        for i,v in enumerate(values):
           results.append(v.printable())
        if len(values) > 0:
           return "\n\n".join(results)
        else:
           return _("No objects found")

    def __iter__(self):
        """
	Iterator for the collection.  Allows list comprehensions, etc
	"""
        for a in self.listing.values():
	    yield a

    def __len__(self):
        """
	Returns size of the collection
	"""
        return len(self.listing.values())

    def collection_type(self):
        """
        Returns the string key for the name of the collection (for use in messages for humans)
        """
        return exceptions.NotImplementedError