root / pykota / trunk / bin / pkturnkey @ 2502

Revision 2502, 18.6 kB (checked in by jerome, 19 years ago)

Updated pkturnkey's help

  • Property svn:executable set to *
  • Property svn:keywords set to Author Date Id Revision
Line 
1#! /usr/bin/env python
2# -*- coding: ISO-8859-15 -*-
3
4# PyKota Turn Key tool
5#
6# PyKota - Print Quotas for CUPS and LPRng
7#
8# (c) 2003, 2004, 2005 Jerome Alet <alet@librelogiciel.com>
9# This program is free software; you can redistribute it and/or modify
10# it under the terms of the GNU General Public License as published by
11# the Free Software Foundation; either version 2 of the License, or
12# (at your option) any later version.
13#
14# This program is distributed in the hope that it will be useful,
15# but WITHOUT ANY WARRANTY; without even the implied warranty of
16# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17# GNU General Public License for more details.
18#
19# You should have received a copy of the GNU General Public License
20# along with this program; if not, write to the Free Software
21# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22#
23# $Id$
24#
25#
26
27import sys
28import os
29import pwd
30import grp
31import socket
32
33from pykota.tool import Tool, PyKotaToolError, crashed, N_
34
35__doc__ = N_("""pkturnkey v%(__version__)s (c) %(__years__)s %(__author__)s
36
37A turn key tool for PyKota. When launched, this command will initialize
38PyKota's database with all existing print queues and some or all users.
39For now, no prices or limits are set, so printing is fully accounted
40for, but not limited. That's why you'll probably want to also use
41edpykota once the database has been initialized.
42
43command line usage :
44
45  pkturnkey [options] [printqueues names]
46
47options :
48
49  -v | --version       Prints pkturnkey version number then exits.
50  -h | --help          Prints this message then exits.
51 
52  -c | --doconf        Give hints about what to put into pykota.conf
53 
54  -d | --dousers       Manages users accounts as well.
55 
56  -D | --dogroups      Manages users groups as well.
57                       Implies -d | --dousers.
58 
59  -e | --emptygroups   Includes empty groups.
60 
61  -f | --force         Modifies the database instead of printing what
62                       it would do.
63                       
64  -u | --uidmin uid    Only adds users whose uid is greater than or equal to
65                       uid. You can pass an username there as well, and its
66                       uid will be used automatically.
67                       If not set, 0 will be used automatically.
68                       Implies -d | --dousers.
69                       
70  -U | --uidmax uid    Only adds users whose uid is lesser than or equal to
71                       uid. You can pass an username there as well, and its
72                       uid will be used automatically.
73                       If not set, a large value will be used automatically.
74                       Implies -d | --dousers.
75
76  -g | --gidmin gid    Only adds groups whose gid is greater than or equal to
77                       gid. You can pass a groupname there as well, and its
78                       gid will be used automatically.
79                       If not set, 0 will be used automatically.
80                       Implies -D | --dogroups.
81                       
82  -G | --gidmax gid    Only adds groups whose gid is lesser than or equal to
83                       gid. You can pass a groupname there as well, and its
84                       gid will be used automatically.
85                       If not set, a large value will be used automatically.
86                       Implies -D | --dogroups.
87
88examples :                             
89
90  $ pkturnkey --dousers --uidmin jerome
91
92  Will simulate the initialization of PyKota's database will all existing
93  printers and print accounts for all users whose uid is greater than
94  or equal to jerome's one. Won't manage any users group.
95 
96  To REALLY initialize the database instead of simulating it, please
97  use the -f | --force command line switch.
98 
99  You can limit the initialization to only a subset of the existing
100  printers, by passing their names at the end of the command line.
101""")
102       
103class PKTurnKey(Tool) :
104    """A class for an initialization tool."""
105    def listPrinters(self, namestomatch) :
106        """Returns a list of tuples (queuename, deviceuri) for all existing print queues."""
107        self.printInfo("Extracting all print queues.")
108        result = os.popen("lpstat -v", "r")
109        lines = result.readlines()
110        result.close()
111        printers = []
112        for line in lines :
113            (begin, end) = line.split(':', 1)
114            deviceuri = end.strip()
115            queuename = begin.split()[-1]
116            if self.matchString(queuename, namestomatch) :
117                printers.append((queuename, deviceuri))
118            else :   
119                self.printInfo("Print queue %s skipped." % queuename)
120        return printers   
121       
122    def listUsers(self, uidmin, uidmax) :   
123        """Returns a list of users whose uids are between uidmin and uidmax."""
124        self.printInfo("Extracting all users whose uid is between %s and %s." % (uidmin, uidmax))
125        return [(entry[0], entry[3]) for entry in pwd.getpwall() if uidmin <= entry[2] <= uidmax]
126       
127    def listGroups(self, gidmin, gidmax, users) :
128        """Returns a list of groups whose gids are between gidmin and gidmax."""
129        self.printInfo("Extracting all groups whose gid is between %s and %s." % (gidmin, gidmax))
130        groups = [(entry[0], entry[2], entry[3]) for entry in grp.getgrall() if gidmin <= entry[2] <= gidmax]
131        gidusers = {}
132        usersgid = {}
133        for u in users :
134            gidusers.setdefault(u[1], []).append(u[0])
135            usersgid.setdefault(u[0], []).append(u[1]) 
136           
137        membership = {}   
138        for g in range(len(groups)) :
139            (gname, gid, members) = groups[g]
140            newmembers = {}
141            for m in members :
142                newmembers[m] = m
143            try :
144                usernames = gidusers[gid]
145            except KeyError :   
146                pass
147            else :   
148                for username in usernames :
149                    if not newmembers.has_key(username) :
150                        newmembers[username] = username
151            for member in newmembers.keys() :           
152                if not usersgid.has_key(member) :
153                    del newmembers[member]
154            membership[gname] = newmembers.keys()
155        return membership
156       
157    def runCommand(self, command, dryrun) :   
158        """Launches an external command."""
159        self.printInfo("%s" % command)
160        if not dryrun :   
161            os.system(command)
162           
163    def createPrinters(self, printers, dryrun=0) :   
164        """Creates all printers in PyKota's database."""
165        if printers :
166            needswarning = [p[0] for p in printers if p[1].find("cupspykota") == -1]
167            command = "pkprinters --add %s" % " ".join(['"%s"' % p[0] for p in printers])
168            self.runCommand(command, dryrun)
169            for p in needswarning :
170                self.printInfo(_("Printer %s is not managed by PyKota yet. Please modify printers.conf and restart CUPS.") % p, "warn")
171       
172    def createUsers(self, users, printers, dryrun=0) :
173        """Creates all users in PyKota's database."""
174        if users :
175            printersnames = [p[0] for p in printers]
176            command = "edpykota --add --noquota --printer %s %s" \
177                          % (",".join(['"%s"' % p for p in printersnames]), \
178                             " ".join(['"%s"' % u for u in users]))
179            self.runCommand(command, dryrun)
180           
181    def createGroups(self, groups, printers, dryrun=0) :
182        """Creates all groups in PyKota's database."""
183        if groups :
184            printersnames = [p[0] for p in printers]
185            commands = ["edpykota --add --groups --noquota --printer %s %s" \
186                            % (",".join(['"%s"' % p for p in printersnames]), \
187                               " ".join(['"%s"' % g for g in groups.keys()]))]
188            revmembership = {}
189            for (groupname, usernames) in groups.items() :
190                for username in usernames :
191                    revmembership.setdefault(username, []).append(groupname)
192            for (username, groupnames) in revmembership.items() :       
193                commands.append('edpykota --ingroups %s "%s"' \
194                    % (",".join(['"%s"' % g for g in groupnames]), username))
195            for command in commands :
196                self.runCommand(command, dryrun)
197       
198    def hintConfig(self, printers) :   
199        """Gives some hints about what to put into pykota.conf"""
200        if not printers :
201            return
202        sys.stderr.flush() # ensure outputs don't mix   
203        print     
204        print "--- CUT ---"
205        print "# Here are some lines that we suggest you add at the end"
206        print "# of the pykota.conf file. These lines gives possible"
207        print "# values for the way print jobs' size will be computed."
208        print "# NB : it is possible that a manual configuration gives"
209        print "# better results for you. As always, your mileage may vary."
210        print "#"
211        for (name, uri) in printers :
212            print "[%s]" % name
213            accounter = "software()"
214            try :
215                uri = uri.split("cupspykota:", 2)[-1]
216            except (ValueError, IndexError) :   
217                pass
218            else :   
219                while uri and uri.startswith("/") :
220                    uri = uri[1:]
221                try :
222                    (backend, destination) = uri.split(":", 1) 
223                    if backend not in ("ipp", "http", "https", "lpd", "socket") :
224                        raise ValueError
225                except ValueError :   
226                    pass
227                else :       
228                    while destination.startswith("/") :
229                        destination = destination[1:]
230                    checkauth = destination.split("@", 1)   
231                    if len(checkauth) == 2 :
232                        destination = checkauth[1]
233                    parts = destination.split("/")[0].split(":")
234                    if len(parts) == 2 :
235                        (hostname, port) = parts
236                        try :
237                            port = int(port)
238                        except ValueError :
239                            port = 9100
240                    else :   
241                        (hostname, port) = parts[0], 9100
242                       
243                    # check udp/161 (SNMP)
244                    # TODO : this doesn't work as expected if the host
245                    # TODO : is down, I must find another solution.
246                    trysnmp = 0
247                    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
248                    try :
249                        s.connect((hostname, socket.getservbyname("snmp", "udp")))
250                        for i in range(2) :
251                            s.send("")
252                    except :
253                        pass
254                    else :   
255                        trysnmp = 1   
256                    s.close()
257                   
258                    # check tcp/9100
259                    trypjl = 0
260                    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
261                    try :
262                        s.connect((hostname, port))
263                    except :   
264                        pass
265                    else :   
266                        trypjl = 1
267                    s.close()
268                       
269                    if trysnmp :
270                        accounter = "hardware(snmp)"
271                    elif trypjl :   
272                        if port != 9100 :
273                            accounter = "hardware(pjl:%s)" % port
274                        else :   
275                            accounter = "hardware(pjl)" % port
276                   
277            print "accounter : %s" % accounter
278            print
279        print "--- CUT ---"
280       
281    def main(self, names, options) :
282        """Intializes PyKota's database."""
283        if not self.config.isAdmin :
284            raise PyKotaToolError, "%s : %s" % (pwd.getpwuid(os.geteuid())[0],\
285                                   _("You're not allowed to use this command."))
286           
287        if not names :
288            names = ["*"]
289           
290        self.printInfo(_("Please be patient..."))
291        dryrun = not options["force"]
292        if dryrun :
293            self.printInfo(_("Don't worry, the database WILL NOT BE MODIFIED."))
294        else :   
295            self.printInfo(_("Please WORRY NOW, the database WILL BE MODIFIED."))
296           
297        if options["dousers"] :   
298            if not options["uidmin"] :   
299                self.printInfo(_("System users will have a print account as well !"), "warn")
300                uidmin = 0
301            else :   
302                try :
303                    uidmin = int(options["uidmin"])
304                except :   
305                    try :
306                        uidmin = pwd.getpwnam(options["uidmin"])[2]
307                    except KeyError, msg :   
308                        raise PyKotaToolError, _("Unknown username %s : %s") \
309                                                   % (options["uidmin"], msg)
310                       
311            if not options["uidmax"] :   
312                uidmax = sys.maxint
313            else :   
314                try :
315                    uidmax = int(options["uidmax"])
316                except :   
317                    try :
318                        uidmax = pwd.getpwnam(options["uidmax"])[2]
319                    except KeyError, msg :   
320                        raise PyKotaToolError, _("Unknown username %s : %s") \
321                                                   % (options["uidmax"], msg)
322           
323            if uidmin > uidmax :           
324                (uidmin, uidmax) = (uidmax, uidmin)
325            users = self.listUsers(uidmin, uidmax)
326        else :   
327            users = []
328           
329        if options["dogroups"] :   
330            if not options["gidmin"] :   
331                self.printInfo(_("System groups will have a print account as well !"), "warn")
332                gidmin = 0
333            else :   
334                try :
335                    gidmin = int(options["gidmin"])
336                except :   
337                    try :
338                        gidmin = grp.getgrnam(options["gidmin"])[2]
339                    except KeyError, msg :   
340                        raise PyKotaToolError, _("Unknown groupname %s : %s") \
341                                                   % (options["gidmin"], msg)
342                       
343            if not options["gidmax"] :   
344                gidmax = sys.maxint
345            else :   
346                try :
347                    gidmax = int(options["gidmax"])
348                except :   
349                    try :
350                        gidmax = grp.getgrnam(options["gidmax"])[2]
351                    except KeyError, msg :   
352                        raise PyKotaToolError, _("Unknown groupname %s : %s") \
353                                                   % (options["gidmax"], msg)
354           
355            if gidmin > gidmax :           
356                (gidmin, gidmax) = (gidmax, gidmin)
357            groups = self.listGroups(gidmin, gidmax, users)
358            if not options["emptygroups"] :
359                for (groupname, members) in groups.items() :
360                    if not members :
361                        del groups[groupname]
362        else :   
363            groups = []
364           
365        printers = self.listPrinters(names)
366        if printers :
367            self.createPrinters(printers, dryrun)
368            self.createUsers([entry[0] for entry in users], printers, dryrun)
369            self.createGroups(groups, printers, dryrun)
370       
371        if dryrun :
372            self.printInfo(_("Simulation terminated."))
373        else :   
374            self.printInfo(_("Database initialized !"))
375       
376        if options["doconf"] :   
377            self.hintConfig(printers)
378                   
379                     
380if __name__ == "__main__" : 
381    retcode = 0
382    try :
383        short_options = "hvdDefu:U:g:G:c"
384        long_options = ["help", "version", "dousers", "dogroups", \
385                        "emptygroups", "force", "uidmin=", "uidmax=", \
386                        "gidmin=", "gidmax=", "doconf"]
387       
388        # Initializes the command line tool
389        manager = PKTurnKey(doc=__doc__)
390        manager.deferredInit()
391       
392        # parse and checks the command line
393        (options, args) = manager.parseCommandline(sys.argv[1:], \
394                                                   short_options, \
395                                                   long_options, \
396                                                   allownothing=1)
397       
398        # sets long options
399        options["help"] = options["h"] or options["help"]
400        options["version"] = options["v"] or options["version"]
401        options["dousers"] = options["d"] or options["dousers"]
402        options["dogroups"] = options["D"] or options["dogroups"]
403        options["emptygroups"] = options["e"] or options["emptygroups"]
404        options["force"] = options["f"] or options["force"]
405        options["uidmin"] = options["u"] or options["uidmin"]
406        options["uidmax"] = options["U"] or options["uidmax"]
407        options["gidmin"] = options["g"] or options["gidmin"]
408        options["gidmax"] = options["G"] or options["gidmax"]
409        options["doconf"] = options["c"] or options["doconf"]
410       
411        if options["uidmin"] or options["uidmax"] :
412            if not options["dousers"] :
413                manager.printInfo(_("The --uidmin or --uidmax command line option implies --dousers as well."), "warn")
414            options["dousers"] = 1   
415           
416        if options["gidmin"] or options["gidmax"] :
417            if not options["dogroups"] :
418                manager.printInfo(_("The --gidmin or --gidmax command line option implies --dogroups as well."), "warn")
419            options["dogroups"] = 1
420       
421        if options["dogroups"] :
422            if not options["dousers"] :
423                manager.printInfo(_("The --dogroups command line option implies --dousers as well."), "warn")
424            options["dousers"] = 1   
425           
426        if options["help"] :
427            manager.display_usage_and_quit()
428        elif options["version"] :
429            manager.display_version_and_quit()
430        else :
431            retcode = manager.main(args, options)
432    except KeyboardInterrupt :       
433        sys.stderr.write("\nInterrupted with Ctrl+C !\n")
434    except SystemExit :       
435        pass
436    except :
437        try :
438            manager.crashed("pkturnkey failed")
439        except :   
440            crashed("pkturnkey failed")
441        retcode = -1
442
443    try :
444        manager.storage.close()
445    except (TypeError, NameError, AttributeError) :   
446        pass
447       
448    sys.exit(retcode)   
Note: See TracBrowser for help on using the browser.